package org.springframework.async;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Jon Brisbin <jon@jbrisbin.com>
*/
public abstract class Promise<V> implements Future<V> {
private final String handlerMutex = "handlers";
protected LinkedBlockingDeque<CompletionHandler<V>> completionHandlers = new LinkedBlockingDeque<CompletionHandler<V>>();
protected AtomicReference<Throwable> error = new AtomicReference<Throwable>();
protected AtomicLong timeout = new AtomicLong(30000L);
protected V obj;
public LinkedBlockingDeque<CompletionHandler<V>> getCompletionHandlers() {
return completionHandlers;
}
public void setTimeout(long timeout) {
this.timeout.set(timeout);
}
public Promise<V> addCompletionHandler(CompletionHandler<V> completionHandler) {
Future<V> future = getFuture();
if (future.isDone()) {
try {
completionHandler.completed(null != obj ? obj : future.get(timeout.get(), TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
completionHandler.failed(e);
} catch (ExecutionException e) {
completionHandler.failed(e);
} catch (TimeoutException e) {
completionHandler.failed(e);
}
} else if (future.isCancelled()) {
completionHandler.cancelled(true);
} else if (null != error.get()) {
completionHandler.failed(error.get());
} else {
this.completionHandlers.add(completionHandler);
}
return this;
}
public void result(V obj) {
if (!completionHandlers.isEmpty()) {
List<CompletionHandler<V>> handlers = new ArrayList<CompletionHandler<V>>(completionHandlers.size());
completionHandlers.drainTo(handlers);
for (CompletionHandler<V> handler : handlers) {
handler.completed(obj);
}
} else {
this.obj = obj;
handleResult(obj);
}
}
protected abstract Future<V> getFuture();
protected abstract void handleResult(V obj);
public void failure(Throwable throwable) {
if (!completionHandlers.isEmpty()) {
List<CompletionHandler<V>> handlers = new ArrayList<CompletionHandler<V>>(completionHandlers.size());
completionHandlers.drainTo(handlers);
for (CompletionHandler<V> handler : handlers) {
handler.failed(throwable);
}
} else {
this.error.set(throwable);
handleFailure(throwable);
}
}
protected abstract void handleFailure(Throwable throwable);
@Override public boolean cancel(boolean b) {
return getFuture().cancel(b);
}
@Override public boolean isCancelled() {
return getFuture().isCancelled();
}
@Override public boolean isDone() {
return getFuture().isDone();
}
@Override public V get() throws InterruptedException, ExecutionException {
return getFuture().get();
}
@Override public V get(long l, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
return getFuture().get(l, timeUnit);
}
}