package vroom.common.utilities.callbacks; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.TimeUnit; import vroom.common.utilities.logging.Logging; /** * Creation date: Mar 9, 2010 - 11:43:48 AM<br/> * <code>CallbackStack</code> provides a stack to store callbacks calls that will be executed asynchronously. * <p> * Callbacks are executed in the order in which they appear in the stack * </p> * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class CallbackStack implements Runnable { private static boolean sGlobalRunning = true; /** * This method will cause all instances of {@link CallbackStack} to finish their execution. */ public static void stopAllThreads() { sGlobalRunning = false; } private final PriorityBlockingQueue<CallbackCall<?, ?>> mPendingCallbacks; private boolean mRunning; /** * Creates a new <code>CallbackExecuter</code> */ public CallbackStack() { super(); mRunning = true; mPendingCallbacks = new PriorityBlockingQueue<CallbackCall<?, ?>>(100); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void run() { CallbackCall c; boolean invariant = mRunning && sGlobalRunning; while (invariant) { try { c = mPendingCallbacks.poll(500, TimeUnit.MILLISECONDS); if (c != null) { c.callback.execute(c.event); } } catch (InterruptedException e) { Logging.getBaseLogger().warn("Execption caught in method CallbackStack.run", e); } c = null; invariant = mRunning && sGlobalRunning || !mPendingCallbacks.isEmpty(); } } /** * Put the call to <code>callback</code> with the given <code>event</code> and <code>params</code> in the stack. * * @param <S> * @param <T> * @param callback * the callback to be added to the stack * @param event * the event that triggered the callback * @param params * optional parameters */ public synchronized <S, T extends ICallbackEventTypes> void put(ICallback<S, T> callback, ICallbackEvent<S, T> event) { if (!mRunning || !sGlobalRunning) { Logging.getBaseLogger() .warn("This callback stack has been stopped, ignoring callback %s [event %s](running=%s, globalRunning=%s)", callback, event, mRunning, sGlobalRunning); throw new IllegalStateException( "Cannot add new callbacks if this stack has been stopped"); } else { CallbackCall<S, T> call = new CallbackCall<S, T>(callback, event); if (!mPendingCallbacks.offer(call)) { Logging.getBaseLogger().warn("Callback %s could not be added to the stack", call); } } } /** * Stops the parent thread by causing the termination of the {@link #run()} method. */ public void stop() { mRunning = false; } /** * <code>CallbackCall</code> is a container class used to store information to asynchronous executing of a callback. */ private static class CallbackCall<S, T extends ICallbackEventTypes> implements Comparable<CallbackCall<S, T>> { private final ICallback<S, T> callback; private final ICallbackEvent<S, T> event; /** * Creates a new <code>CallbackCall</code> * * @param params * @param callback * @param event */ public CallbackCall(ICallback<S, T> callback, ICallbackEvent<S, T> event) { super(); this.callback = callback; this.event = event; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return String.format("event:%s, callback:%s", event, callback); } @Override public int compareTo(CallbackCall<S, T> o) { if (o.callback.getPriority() != this.callback.getPriority()) { return (o.callback.getPriority() - this.callback.getPriority()) * 60000; } else { return (int) (this.event.getTimeStamp() - o.event.getTimeStamp()); } } } }