| /******************************************************************************* |
| * 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()); |
| } |
| } |