package org.oddjob.scheduling;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.log4j.Logger;
public class TrackingExecutor implements ScheduledExecutorService {
static final Logger logger = Logger.getLogger(TrackingExecutor.class);
private final ScheduledExecutorService executor;
private final List<Wrapper> running =
new ArrayList<Wrapper>();
public int getTaskCount() {
synchronized (running) {
return running.size();
}
}
public void waitForNothingOutstanding() throws InterruptedException {
synchronized (running) {
while (running.size() > 0 && !executor.isShutdown()) {
running.wait();
}
}
}
public TrackingExecutor(ScheduledExecutorService scheduler) {
this.executor = scheduler;
}
public ScheduledFuture<?> schedule(Runnable command, long delay,
TimeUnit unit) {
RunnableWrapper wrapper = wrapperFor(command);
return scheduledFuture(
executor.schedule(wrapper, delay, unit),
wrapper);
}
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay,
TimeUnit unit) {
CallableWrapper<V> wrapper = wrapperFor(callable);
return scheduledFuture(
executor.schedule(wrapper, delay, unit),
wrapper);
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay, long period, TimeUnit unit) {
RunnableWrapper wrapper = wrapperFor(command);
return scheduledFuture(
executor.scheduleAtFixedRate(wrapper, initialDelay, period, unit),
wrapper);
}
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay, long delay, TimeUnit unit) {
RunnableWrapper wrapper = wrapperFor(command);
return scheduledFuture(
executor.scheduleWithFixedDelay(wrapper, initialDelay, delay, unit),
wrapper);
}
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return executor.awaitTermination(timeout, unit);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
throw new UnsupportedOperationException("Too difficult to track.");
}
public <T> List<Future<T>> invokeAll(
Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException {
throw new UnsupportedOperationException("Too difficult to track.");
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException("Too difficult to track.");
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
throw new UnsupportedOperationException("Too difficult to track.");
}
public boolean isShutdown() {
return executor.isShutdown();
}
public boolean isTerminated() {
return executor.isTerminated();
}
public void shutdown() {
executor.shutdown();
synchronized (running) {
running.notifyAll();
}
}
public List<Runnable> shutdownNow() {
List<Runnable> outstanding = executor.shutdownNow();
synchronized (running) {
running.notifyAll();
}
return outstanding;
}
public <T> Future<T> submit(Callable<T> task) {
CallableWrapper<T> wrapper = wrapperFor(task);
return future(
executor.submit(wrapper),
wrapper);
}
public Future<?> submit(Runnable task) {
RunnableWrapper wrapper = wrapperFor(task);
return future(
executor.submit(wrapper),
wrapper);
}
public <T> Future<T> submit(Runnable task, T result) {
RunnableWrapper wrapper = wrapperFor(task);
return future(
executor.submit(wrapper, result),
wrapper);
}
public void execute(Runnable command) {
executor.execute(wrapperFor(command));
}
private RunnableWrapper wrapperFor(Runnable command) {
synchronized (running) {
RunnableWrapper wrapper = new RunnableWrapper(command);
running.add(wrapper);
return wrapper;
}
}
private <X> CallableWrapper<X> wrapperFor(Callable<X> callable) {
synchronized (running) {
CallableWrapper<X> wrapper = new CallableWrapper<X>(callable);
running.add(wrapper);
return wrapper;
}
}
interface Wrapper { }
class RunnableWrapper implements Runnable, Wrapper {
private final Runnable runnable;
public RunnableWrapper(Runnable runnable) {
this.runnable = runnable;
}
public void run() {
try {
runnable.run();
}
finally {
synchronized (running) {
running.remove(this);
running.notifyAll();
}
}
}
}
class CallableWrapper<V> implements Callable<V>, Wrapper {
private final Callable<V> callable;
public CallableWrapper(Callable<V> callable) {
this.callable = callable;
}
public V call() throws Exception {
try {
return callable.call();
}
finally {
synchronized (running) {
running.remove(this);
running.notifyAll();
}
}
}
}
private <T> Future<T> future(Future<T> wrapping, Wrapper wrapper) {
return new FutureWrapper<T>(wrapping, wrapper);
}
class FutureWrapper<V> implements Future<V> {
private final Future<V> wrapping;
private final Wrapper wrapper;
public FutureWrapper(Future<V> wrapping, Wrapper wrapper) {
this.wrapping = wrapping;
this.wrapper = wrapper;
}
public boolean cancel(boolean mayInterruptIfRunning) {
if (wrapping.cancel(mayInterruptIfRunning)) {
synchronized (running) {
running.remove(wrapper);
}
return true;
}
else {
return false;
}
}
public V get() throws InterruptedException, ExecutionException {
return wrapping.get();
}
public V get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return wrapping.get(timeout, unit);
}
public boolean isCancelled() {
return wrapping.isCancelled();
}
public boolean isDone() {
return wrapping.isDone();
}
}
private <T> ScheduledFuture<T> scheduledFuture(ScheduledFuture<T> wrapping, Wrapper wrapper) {
return new ScheduledFutureWrapper<T>(wrapping, wrapper);
}
class ScheduledFutureWrapper<V> extends FutureWrapper<V>
implements ScheduledFuture<V> {
private final ScheduledFuture<V> wrapping;
public ScheduledFutureWrapper(ScheduledFuture<V> wrapping, Wrapper wrapper) {
super(wrapping, wrapper);
this.wrapping = wrapping;
}
public int compareTo(Delayed o) {
return wrapping.compareTo(o);
}
public long getDelay(TimeUnit unit) {
return wrapping.getDelay(unit);
}
}
}