blob: caf6e2b91b97e580423626b58fd538c88fa5bb84 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 Wind River Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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<>();
private Map<TimerDMContext, Future<?>> fTimerFutures = new HashMap<>();
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());
}
}