package edu.ualberta.med.biobank.mvp.action; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import com.google.inject.Inject; import edu.ualberta.med.biobank.common.action.Action; import edu.ualberta.med.biobank.common.action.ActionCallback; import edu.ualberta.med.biobank.common.action.ActionResult; import edu.ualberta.med.biobank.common.action.Dispatcher; import edu.ualberta.med.biobank.mvp.action.StaleSafeDispatcher.AsyncContext.Call; /** * Thread-safe delegating {@link Dispatcher} that will not execute the callback * methods of stale {@link ActionCallback}-s. * <p> * This is most easily explained with an example. Say an action is * asynchronously invoked twice (via * {@link Dispatcher#asyncExec(Action, ActionCallback)}, but the second * invocation returns before the first. This {@link StaleSafeDispatcher} * implementation will invoke the callback of the second {@link Action}, but not * the first, if and when it does finally return. * <p> * Staleness is determined according to an {@link Action}'s class. * * @author jferland * */ @SuppressWarnings("unused") public class StaleSafeDispatcher implements Dispatcher { private final Dispatcher dispatcher; private final Map<Object, AsyncContext> contexts = new HashMap<Object, AsyncContext>(); @Inject public StaleSafeDispatcher(Dispatcher dispatcher) { this.dispatcher = dispatcher; } @Override public <T extends ActionResult> T exec(Action<T> action) { return dispatcher.exec(action); } @Override public synchronized <T extends ActionResult> Future<T> asyncExec( Action<T> action, ActionCallback<T> callback) { return asyncExec(action.getClass(), action, callback); } private synchronized <T extends ActionResult> Future<T> asyncExec( Object contextKey, Action<T> action, ActionCallback<T> callback) { AsyncContext context = new AsyncContext(); Call call = context.start(); return dispatcher.asyncExec(action, new StaleSafeActionCallback<T>( call, callback)); } public static class AsyncContext { private AtomicInteger placeProvider = new AtomicInteger(0); private Integer lastFinished = -1; public Call start() { return new Call(); } public class Call { private final Integer place; private Call() { this.place = placeProvider.getAndIncrement(); } public synchronized boolean finish() { if (place > lastFinished) { lastFinished = place; return true; } return false; } } } }