Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7f4da21612664198d2dbf663c7c94ec76e8d4f1b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*******************************************************************************
 * Copyright (c) 2006, 2009 Wind River Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.examples.dsf.timers;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin;
import org.osgi.framework.BundleContext;

/**
 * Timer service tracks a set of timers, which are created per user request.
 * The timers are represented using a Data Model context object, which 
 * implements {@link IDMContext}.  Each timers value, which can be retrieved
 * by calling {@link #getTimerValue(TimerDMContext)}, is incremented every 
 * second.  When a timer value is incremented the TimerService issues a 
 * {@link TimerTickDMEvent}.
 */
public class TimerService extends AbstractDsfService 
{
    /** Event indicating that the list of timers is changed. */
    @Immutable
    public static class TimersChangedEvent  {}
    
    /** Data Model context representing a timer. */
    @Immutable
    public static class TimerDMContext extends AbstractDMContext {
        final int fNumber;
        
        public TimerDMContext(String sessionId, int timer) {
            super(sessionId, new IDMContext[0]);
            fNumber = timer;
        }
        
        /** Returns the sequential creation number of this timer. */
        public int getTimerNumber() {
            return fNumber;
        }
        
        // Timer context objects are created as needed and not cached, so the 
        // equals method implementation is critical.
        @Override
        public boolean equals(Object other) {
            return baseEquals(other) && 
                ((TimerDMContext)other).fNumber == fNumber;
        }
        
        @Override
        public int hashCode() { return baseHashCode() + fNumber; } 
        
        @Override
        public String toString() {
            return baseToString() + ".timer[" + fNumber + "]";
        }
    }            
    
    /**
     * Event indicating that a timer's value has incremented.  The context in 
     * the event points to the timer that has changed.   
     */
    public class TimerTickDMEvent extends AbstractDMEvent<TimerDMContext> {
        public TimerTickDMEvent(TimerDMContext context) {
            super(context);
        }
    }

    /** Counter for generating timer numbers */
    private int fTimerNumberCounter = 1;
    
    // Use a linked hash in order to be able to return an ordered list of timers.
    private Map<TimerDMContext, Integer> fTimers = 
        new LinkedHashMap<TimerDMContext, Integer>();
    
    private Map<TimerDMContext, Future<?>> fTimerFutures = 
        new HashMap<TimerDMContext, Future<?>>();
    
    
    TimerService(DsfSession session) {
        super(session);
    }
    
    @Override 
    protected BundleContext getBundleContext() {
        return DsfExamplesPlugin.getDefault().getBundle().getBundleContext();
    }    

    @Override 
    public void initialize(final RequestMonitor requestMonitor) {
        super.initialize(
            new RequestMonitor(getExecutor(), requestMonitor) { 
                @Override
                public void handleSuccess() {
                    // After super-class is finished initializing
                    // perform TimerService initialization.
                    doInitialize(requestMonitor);
                }});
    }

    private void doInitialize(RequestMonitor requestMonitor) {
        // Register service
        register( new String[]{ TimerService.class.getName() }, 
            new Hashtable<String,String>() );
        requestMonitor.done();
    }

    @Override 
    public void shutdown(RequestMonitor requestMonitor) {
        // Cancel timer futures to avoid firing more events.
        for (Future<?> future : fTimerFutures.values()) {
            future.cancel(false);
        }
        unregister();
        super.shutdown(requestMonitor);
    }
    
    /** Retrieves the list of timer contexts. */
    public TimerDMContext[] getTimers() {
        return fTimers.keySet().toArray(new TimerDMContext[fTimers.size()]);
    }

    /** Retrieves the timer value for the given context. */
    public int getTimerValue(TimerDMContext context) {
        Integer value = fTimers.get(context);
        if (value != null) {
            return value;
        } 
        return -1;
    }
    
    /** Creates a new timer and returns its context. */
    public TimerDMContext startTimer() {
        // Create a new timer context and add it to the internal list.
        final TimerDMContext newTimer = 
            new TimerDMContext(getSession().getId(), fTimerNumberCounter++); 
        fTimers.put(newTimer, 0);

        // Create a new runnable that will execute every second and increment
        // the timer value.  The returned future is the handle that allows 
        // for canceling the scheduling of the runnable.
        Future<?> timerFuture = getExecutor().scheduleAtFixedRate(
            new Runnable() {
                @Override
		public void run() {
                    fTimers.put(newTimer, fTimers.get(newTimer) + 1);
                    getSession().dispatchEvent(new TimerTickDMEvent(newTimer), getProperties());
                }
                @Override
                public String toString() { return "Scheduled timer runnable for timer " + newTimer; } //$NON-NLS-1$
            }, 
            1, 1, TimeUnit.SECONDS);
        fTimerFutures.put(newTimer, timerFuture);
        
        // Issue an event to allow clients to update the list of timers.
        getSession().dispatchEvent(new TimersChangedEvent(), getProperties());
        return newTimer;
    }
    
    /** Removes given timer from list of timers. */
    public void killTimer(TimerDMContext timerContext) {
        if (fTimers.containsKey(timerContext)) {
            fTimers.remove(timerContext);
            fTimerFutures.remove(timerContext).cancel(false);
        }
        getSession().dispatchEvent(new TimersChangedEvent(), getProperties());
    }
}

Back to the top