package com.github.atemerev.hollywood.future; import com.github.atemerev.pms.listeners.MessageListener; import com.github.atemerev.pms.listeners.MessageListenerDelegate; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * <code>AbstractPromise</code> provides a convenient implementation of <code>Promise</code> methods for appending * continuations. * * @author Alexander Temerev, Alexander Kuklev * @version $Id$ */ public abstract class AbstractPromise<T> implements Promise<T> { boolean isDone = false; boolean isCancelled = false; List<Runnable> continuations = new LinkedList<Runnable>(); protected MessageListenerDelegate delegate = new MessageListenerDelegate(); /** * Get list of attached listeners. Here you can attach listeners for the following events: * <ul> * <li><code>CompletedEvent</code>: if the task has been successfully completed.</li> * <li><code>CancelledEvent</code>: if the task has been cancelled.</li> * </ul> * * @return List of attached listeners. */ public List<MessageListener> listeners() { return delegate.listeners(); } // Status getters /** * Returns <tt>true</tt> if this task completed. * * Completion may be due to normal termination, an exception, or * cancellation -- in all of these cases, this method will return * <tt>true</tt>. * * @return <tt>true</tt> if this task completed */ public boolean isDone() { return isDone; } /** * Returns <tt>true</tt> if this task was cancelled before it completed * normally. * * @return <tt>true</tt> if this task was cancelled before it completed */ public boolean isCancelled() { return isCancelled; } // Status setters /** * This method must be called <strong>once</strong> when the task is completed. This will mark the * promise as done, fire <code>CompletedEvent</code> to attached listeners and process all continuations. */ public void markDone() { isDone = true; delegate.processMessage(new CompletedEvent(this)); fireContinuations(); } /** * This method must be called if the task was cancelled. This will mark the promise as cancelled and fire * <code>CancelledEvent</code> to attached listeners. */ public void markCancelled() { isDone = true; isCancelled = true; delegate.processMessage(new CancelledEvent(this)); } /** * Append a callable continuation -- i.e. a continuation which returns a value. * * @param continuation A continuation to append -- something implementing <code>Callable</code> interface. * Normally it would be called a <em>closure</em>. * @return A promise for this continuation (which can be checked for execution, or appended with other * continuations). */ public <W> Promise<W> append(Callable<W> continuation) { PromiseTask<W> promiseContinuation = new PromiseTask<W>(continuation); if (isDone) { promiseContinuation.run(); } else { continuations.add(promiseContinuation); } return promiseContinuation; } /** * Append a runnable continuation -- i.e. a continuation which doesn't return a value. * * @param continuation A continuation to append -- something implementing <code>Runnable</code> interface. * @return A promise for this continuation. */ public Promise<Void> append(final Runnable continuation) { PromiseTask<Void> promiseContinuation = new PromiseTask<Void>(new Callable<Void>() { public Void call() { continuation.run(); return null; } }); if (isDone) { promiseContinuation.run(); } else { continuations.add(promiseContinuation); } return promiseContinuation; } /** * Append a callable continuation -- i.e. a continuation which returns a value -- using a specified * executor to run it. * * @param continuation A continuation to append -- something implementing <code>Callable</code> interface. * Normally it would be called a <em>closure</em>. * @param executor An executor to submit continuation to. * @return A promise for this continuation (which can be checked for execution, or appended with other * continuations). */ public <W> Promise<W> append(Callable<W> continuation, final Executor executor) { final PromiseTask<W> promiseContinuation = new PromiseTask<W>(continuation); if (isDone) { executor.execute(promiseContinuation); } else { continuations.add(new Runnable() { public void run() { executor.execute(promiseContinuation); } }); } return promiseContinuation; } /** * Append a runnable continuation -- i.e. a continuation which doesn't return a value -- using a specified * executor to run it. * * @param continuation A continuation to append -- something implementing <code>Runnable</code> interface. * @param executor An executor to submit continuation to. * @return A promise for this continuation (which can be checked for execution, or appended with other * continuations). */ public Promise<Void> append(final Runnable continuation, final Executor executor) { final PromiseTask<Void> promiseContinuation = new PromiseTask<Void>(new Callable<Void>() { public Void call() { continuation.run(); return null; } }); if (isDone) { executor.execute(promiseContinuation); } else { continuations.add(new Runnable() { public void run() { executor.execute(promiseContinuation); } }); } return promiseContinuation; } public final T get(long timeout, TimeUnit unit) throws TimeoutException { return get(unit.toMillis(timeout)); } public final void join() { get(); } public final void join(long timeout) throws TimeoutException { get(timeout); } // Stuff to override public abstract T get(); public abstract T get(long timeout) throws TimeoutException; // Can be overridden, though need not be. public boolean cancel(boolean mayInterruptIfRunning) { return false; } // Private methods private void fireContinuations() { for (Runnable continuation : continuations) { continuation.run(); } } }