package com.github.atemerev.hollywood.future; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * // todo rewrite comments * A support class for appending continuations to promises. After the promise is fulfilled (i.e. its underlying * future task is done), continuation execution is started by fire() method. The result of promise execution is * accessible within the caller's scope. * <p/> * Continuations can be chained. This will inevitably lead you to the Haskell hell with all its monads, so be * warned. * * @author Alexander Temerev, Alexander Kuklev * @version $Id$ */ public class PromiseTask<T> extends AbstractPromise<T> implements Promise<T>, Runnable { private Callable<T> task; private CountDownLatch done = new CountDownLatch(1); private RuntimeExecutionException executionException; private T result; /** * Create the continuation with the supplied Callable. * * @param task A Callable implementation to use for the continuation. */ public PromiseTask(Callable<T> task) { this.task = task; } /** * Start the continuation. This method should be called after the caller promise is fulfilled. If the continuation * throws exception, it will be stored and will be thereafter thrown by get() method. */ public void run() { try { result = task.call(); } catch (Throwable e) { executionException = new RuntimeExecutionException(e); } done.countDown(); markDone(); } /** * Join the continuation execution to the current thread and get its result. If exception had been thrown, * it's wrapped into the unchecked RuntimeExcecutionException and then rethrown. * * @return Continuation result. * @throws RuntimeExecutionException If exception has been thrown during the execution. */ public T get() throws RuntimeExecutionException { try { done.await(); if (executionException != null) { throw executionException; } } catch (InterruptedException e) { throw new RuntimeException("Manual interruption is not supported"); } return result; } /** * Attempt to join the continuation to the current thread for the specified time. If still running, * throws TimeoutException (and the continuation continues to run, so if you want to stop it, use cancel()). * Other than that, works the same way as the no-argument get(). * * @param timeout Timeout to wait for execution completion while joining the thread. * @return Execution result (if completed before timeout). * @throws TimeoutException If timeout happened before execution has been completed. * @throws RuntimeExecutionException If exception has been thrown during the execution. */ public T get(long timeout) throws RuntimeExecutionException, TimeoutException { try { done.await(timeout, TimeUnit.MILLISECONDS); if (executionException != null) { throw executionException; } if (result == null) { throw new TimeoutException(); } } catch (InterruptedException e) { throw new RuntimeException("Manual interruption is not supported"); } return result; } //TODO: Reimplement the whole class using the FutureTask source codes. }