import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * A promise is a simple kind of {@link java.util.concurrent.Future * future}, which captures a promise that a value of some type will be * provided. The promise is made by creating an instance of the class * and giving it to partners; it is kept by passing the value to the * {@link #keep(Object) keep} method. Alternatively, it may be broken by * passing an exception to the {@link #breach(Throwable) breach} method. * Or cancelled with the @link {@link #cancel(boolean)} method. */ public class Promise implements Future { // First, establish the basic idea: the promise has an outcome, which once set, cannot be changed. The outcome is only touched via its accessors. private static abstract class Outcome { public abstract V get() throws ExecutionException, CancellationException; } private Outcome outcome; private synchronized void setOutcome(Outcome outcome) { if (this.outcome != null) throw new IllegalStateException("this promise already has an outcome: " + this.outcome); this.outcome = outcome; notifyAll(); } private synchronized Outcome getOutcome() { return outcome; } // The outcome can be set in three ways: the promise can be kept, broken, or cancelled. /** * Provides the promised value. * * @param value the promised value; this cannot be null */ public void keep(final V value) { if (value == null) throw new NullPointerException("value"); setOutcome(new Outcome() { public V get() { return value; } public String toString() { return "kept: " + value; } }); } /** * Breaks the promise; no value will be supplied. Sadly, break is a * keyword, so we draw on the richness of the English language and * use a synonym. * * @param e an exception indicating why the promise was broken; if * you are going to break promises, you can at least * explain why, so this cannot be null */ public void breach(final Throwable e) { if (e == null) throw new NullPointerException("e"); setOutcome(new Outcome() { public V get() throws ExecutionException { throw new ExecutionException(e); } public String toString() { return "breached: " + e; } }); } private static final Outcome CANCELLED = new Outcome() { public Void get() throws CancellationException { throw new CancellationException(); } public String toString() { return "cancelled"; } }; /** * Cancels the promise. This doesn't really fit the metaphor, but * is's something you can do with a Future. * * @param mayInterruptIfRunning is ignored, as this object is not * linked to any particular thread */ public boolean cancel(boolean mayInterruptIfRunning) { return cancel(); } /** * Cancels the promise. This doesn't really fit the metaphor, but * is's something you can do with a Future. */ public synchronized boolean cancel() { if (getOutcome() != null) return false; @SuppressWarnings("unchecked") Outcome cancelled = (Outcome)CANCELLED; // safe because this outcome never returns a value setOutcome(cancelled); return true; } // The outcome of the promise can be asked about obliquely or directly public boolean isCancelled() { return getOutcome() == CANCELLED; } public boolean isDone() { return getOutcome() != null; } public V get() throws InterruptedException, ExecutionException { try { return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { throw new AssertionError("unlikely timeout: " + e); } } /** * {@inheritDoc} * * Note that the waiting is done with a granularity of milliseconds, * regardless of the time unit specified. */ public synchronized V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { long deadline = System.currentTimeMillis() + unit.toMillis(timeout); Outcome outcome; while ((outcome = getOutcome()) == null) { wait(deadline - System.currentTimeMillis()); } return outcome.get(); // the three kinds of outcome will react to this method in very different ways } }