// Copyright (c) 2007 Dustin Sallings <dustin@spy.net>
package net.spy.concurrent;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* CompletionService that honors RetryableCallable instances.
*
* @param <V>
*/
public class RetryableExecutorCompletionService<V>
implements CompletionService<V> {
private final ExecutorService executor;
final BlockingQueue<Future<V>> completionQueue;
public RetryableExecutorCompletionService(ExecutorService e) {
super();
completionQueue=new LinkedBlockingQueue<Future<V>>();
executor=e;
}
public Future<V> poll() {
return completionQueue.poll();
}
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
public Future<V> submit(Callable<V> c) {
Future<V> rv=null;
if(c instanceof RetryableCallable) {
TrackingCallable tc=new TrackingCallable((RetryableCallable<V>)c);
// Lock the callable before submitting to ensure it can know its
// Future before it attempts to run
synchronized(tc) {
rv=executor.submit(tc);
tc.setFuture(rv);
}
} else {
rv=new QueueingFuture(c);
executor.submit((Runnable)rv);
}
return rv;
}
public Future<V> submit(Runnable task, V result) {
QueueingFuture rv=new QueueingFuture(task, result);
executor.execute(rv);
return rv;
}
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
class QueueingFuture extends FutureTask<V> {
QueueingFuture(Callable<V> c) {
super(c);
}
QueueingFuture(Runnable t, V r) {
super(t, r);
}
@Override
protected void done() {
completionQueue.add(this);
}
}
class TrackingCallable implements RetryableCallable<V> {
private final RetryableCallable<V> callable;
private Future<V> future=null;
public TrackingCallable(RetryableCallable<V> c) {
super();
callable=c;
}
public void setFuture(Future<V> f) {
future=f;
assert future != null : "Future is null";
}
public long getRetryDelay() {
return callable.getRetryDelay();
}
public synchronized void onComplete(boolean success, Object res) {
callable.onComplete(success, res);
assert future != null : "Future is null";
completionQueue.add(future);
}
public void onExecutionException(ExecutionException exception) {
callable.onExecutionException(null);
}
public V call() throws Exception {
assert future != null : "Future is null";
return callable.call();
}
}
}