package net.jxta.impl.util.threads; import java.util.concurrent.Delayed; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Future implementation which works with the ProxiedCallable to provide * offloading to a target executor. It pretends as if it was * * @param <V> return type of the callable function */ public class ProxiedScheduledFuture<V> implements ScheduledFuture<V> { /** * Future that we were given when we scheduled our proxy task. */ private final ScheduledFuture<V> pFuture; /** * Actual future that we are passed at runtime. */ private Future<V> future; /////////////////////////////////////////////////////////////////////////// // Constructor: /** * Creates a new proxied ProxiedCallableFuture instance. * * @param proxyFuture ScheduledFuture obtained by scheduling a proxy task * instance */ public ProxiedScheduledFuture( final ScheduledFuture<V> proxyFuture) { pFuture = proxyFuture; } /////////////////////////////////////////////////////////////////////////// // ScheduledFuture interface methods: /** * {@inheritDoc} * * This instance is equivalent to calling * <code>proxyFuture.getDelay()</code>. */ public long getDelay(final TimeUnit unit) { return pFuture.getDelay(unit); } /** * {@inheritDoc} * * This instance is equivalent to calling * <code>proxyFuture.compareTo()</code>. */ public int compareTo(final Delayed o) { return pFuture.compareTo(o); } /** * {@inheritDoc} * * This instance is equivalent to calling * <code>proxyFuture.cancel()</code> before execution is started on * the alternate executor service, and is equivalent to * <code>future.cancel()</code> thereafter. */ public boolean cancel(final boolean mayInterruptIfRunning) { synchronized(this) { if (future == null) { return pFuture.cancel(mayInterruptIfRunning); } else { return future.cancel(mayInterruptIfRunning); } } } /** * {@inheritDoc} * * This instance is equivalent to calling * <code>proxyFuture.isCancelled()</code> before execution is started on * the alternate executor service, and is equivalent to * <code>future.isCancelled()</code> thereafter. */ public boolean isCancelled() { synchronized(this) { if (future == null) { return pFuture.isCancelled(); } else { return future.isCancelled(); } } } /** * {@inheritDoc} * * This instance is equivalent to calling * <code>proxyFuture.isDone()</code> before execution is started on * the alternate executor service, and is equivalent to * <code>future.isDone()</code> thereafter. */ public boolean isDone() { synchronized(this) { if (future == null) { return pFuture.isDone(); } else { return future.isDone(); } } } /** * {@inheritDoc} * * @throws InterruptedException if the current thread was interrupted * while waiting */ public V get() throws InterruptedException, ExecutionException { synchronized(this) { while(future == null) { wait(); } return future.get(); } } /** * {@inheritDoc} * * @throws InterruptedException if the current thread was interrupted * while waiting */ public V get( final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { long entryTime = System.currentTimeMillis(); synchronized(this) { long timeoutMillis = TimeUnit.MILLISECONDS.convert(timeout, unit); wait(timeoutMillis); if (future == null) { throw(new TimeoutException( "Timeout while waiting for future (1)")); } // Adjust the timeout based on how long we've waited already timeoutMillis -= (System.currentTimeMillis() - entryTime); if (timeoutMillis < 0) { throw(new TimeoutException( "Timeout while waiting for future (2)")); } return future.get(timeoutMillis, TimeUnit.MILLISECONDS); } } /////////////////////////////////////////////////////////////////////////// // Package methods: /** * Called by the ProxiedCallable if/when it executes, providing this class * with the actual Future instance. * * @param actualFuture actual future instance */ void setFuture(final Future<V> actualFuture) { synchronized(this) { if (future == null) { future = actualFuture; if (isCancelled()) { future.cancel(true); } notifyAll(); } else { throw(new IllegalStateException( "Actual future has already been set")); } } } }