package org.limewire.concurrent; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.limewire.listener.EventListener; import org.limewire.util.Objects; /** * The base implementation of {@link AsyncFuture}. * * <p>NOTE: The documentation for {@link FutureTask} is lying a * bit. A {@link FutureTask} is not the result of an asynchronous * computation. It's an asynchronously scheduled but otherwise * totally synchronous computation. * * <p>That means the underlying computation (represented by a * {@link Runnable} or {@link Callable} task) is synchronous * and it's assumed there is a result available at the end * of the computation. * * <p>But what if we want to start a computation and let an * another {@link Thread} deliver the result at some future * point in time. In the mean time we want the {@link Thread} * that started this computation do other things and not * wait for the result? * * <p>{@link AsyncFuture}s allow us to decouple the staring * and finishing of operations. * * <p>Example: Sending a message over the Network from one * {@link Thread} and an another {@link Thread} delivers the * response at some future point in time. * * @see AsyncFuture * @see AsyncFutureTask */ public class AsyncValueFuture<V> implements AsyncFuture<V> { /** * {@link List} of {@link EventListener}s that were added * before the {@link AsyncValueFuture} completed. */ private final List<EventListener<FutureEvent<V>>> listeners = new ArrayList<EventListener<FutureEvent<V>>>(); /** * */ private final OnewayExchanger<V, ExecutionException> exchanger = new OnewayExchanger<V, ExecutionException>(this, true); /** * Creates a {@link AsyncValueFuture} */ public AsyncValueFuture() { } /** * Creates a {@link AsyncValueFuture} with the given value */ public AsyncValueFuture(V value) { setValue(value); } /** * Creates a {@link AsyncValueFuture} with the given {@link Throwable} */ public AsyncValueFuture(Throwable exception) { setException(exception); } @Override public boolean setValue(V value) { boolean success = exchanger.setValue(value); if (success) { complete(); } return success; } @Override public boolean setException(Throwable exception) { boolean success = exchanger.setException(wrap(exception)); if (success) { complete(); } return success; } @Override public boolean cancel(boolean mayInterruptIfRunning) { boolean success = exchanger.cancel(); if (success) { complete(); } return success; } @Override public V get() throws InterruptedException, ExecutionException { checkIfEventThread(); return exchanger.get(); } @Override public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { checkIfEventThread(); return exchanger.get(timeout, unit); } @Override public boolean isCancelled() { return exchanger.isCancelled(); } @Override public boolean isDone() { return exchanger.isDone(); } @Override public boolean isCompletedAbnormally() { return exchanger.throwsException(); } /** * Notifies all {@link EventListener}s and calls {@link #done()}. */ private void complete() { fireOperationComplete(); done(); } /** * Protected method invoked when this task transitions to state * <tt>isDone</tt> (whether normally or via cancellation). The * default implementation does nothing. Subclasses may override * this method to invoke completion callbacks or perform * bookkeeping. Note that you can query status inside the * implementation of this method to determine whether this task * has been cancelled. */ protected void done() { // Override } @Override public void addFutureListener(EventListener<FutureEvent<V>> listener) { Objects.nonNull(listener, "listener"); boolean done = false; synchronized (this) { done = isDone(); if (!done) { listeners.add(listener); } } if (done) { fireOperationComplete(listener); } } /** * Checks if the caller {@link Thread} is the event {@link Thread}. * The default implementation is always returning false, custom * implementations may override this method. */ protected boolean isEventThread() { return false; } /** * Checks if it's called from the event {@link Thread} and * throws an {@link IllegalStateException} if {@link #isDone()} * is returning false. This is a safe-guard to ensure that * the event {@link Thread} cannot call any of the get() * methods if the {@link AsyncFuture} isn't done yet. */ private void checkIfEventThread() { if (!isDone() && isEventThread()) { throw new IllegalStateException(); } } /** * Notifies all {@link EventListener}s that were added before */ @SuppressWarnings("unchecked") private void fireOperationComplete() { FutureEvent<V> event = null; EventListener<FutureEvent<V>>[] l = null; synchronized (this) { if (!listeners.isEmpty()) { event = FutureEvent.createEvent(this); l = listeners.toArray(new EventListener[0]); } } if (l != null) { fireOperationComplete(l, event); } } /** * Notifies the given {@link EventListener}. */ @SuppressWarnings("unchecked") private void fireOperationComplete(EventListener<FutureEvent<V>> listener) { FutureEvent<V> event = FutureEvent.createEvent(this); fireOperationComplete(new EventListener[] { listener }, event); } /** * Notifies the given {@link EventListener}s. */ protected void fireOperationComplete( EventListener<FutureEvent<V>>[] listeners, FutureEvent<V> event) { for (EventListener<FutureEvent<V>> l : listeners) { l.handleEvent(event); } } /** * Takes the given {@link Throwable} and wraps it in an * {@link ExecutionException} if it isn't already. */ private static ExecutionException wrap(Throwable t) { if (t instanceof ExecutionException) { return (ExecutionException)t; } return new ExecutionException(t); } }