package org.marketcetera.photon.commons; import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.CountDownLatch; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import org.marketcetera.util.misc.ClassVersion; /* $License$ */ /** * An abstract ExecutorService that provides simple shutdown and termination * semantics, assuming that {@link #doExecute(Runnable)} ensures tasks are run * in order. It does not support {@link #shutdownNow()}, which will simply call * {@link #shutdown()} and return null. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: SimpleExecutorService.java 16154 2012-07-14 16:34:05Z colin $ * @since 2.0.0 */ @ThreadSafe @ClassVersion("$Id: SimpleExecutorService.java 16154 2012-07-14 16:34:05Z colin $") public abstract class SimpleExecutorService extends AbstractExecutorService { private final Object mLock = new Object(); @GuardedBy("mLock") private volatile boolean mIsShutdown; @GuardedBy("mLock") private volatile boolean mTerminated; private final CountDownLatch mTerminatedLatch = new CountDownLatch(1); @Override public final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { mTerminatedLatch.await(timeout, unit); return mTerminated; } @Override public final boolean isShutdown() { return mIsShutdown; } @Override public final boolean isTerminated() { return mTerminated; } @Override public final void shutdown() { synchronized (mLock) { if (mIsShutdown) { return; } mIsShutdown = true; try { // queue shutdown command doExecute(new Runnable() { @Override public void run() { mTerminated = true; mTerminatedLatch.countDown(); } }); } catch (Exception e) { Messages.SIMPLE_EXECUTOR_SERVICE_ABNORMAL_SHUTDOWN.error(this, e); } } } @Override public final List<Runnable> shutdownNow() { // unable to determine tasks that haven't executed so just shutdown shutdown(); return null; } @Override public final void execute(Runnable command) { if (command == null) { // per specification throw new NullPointerException("command must not be null"); //$NON-NLS-1$ } synchronized (mLock) { if (mIsShutdown) { throw new RejectedExecutionException( Messages.SIMPLE_EXECUTOR_SERVICE_REJECT_SHUTDOWN .getText()); } try { doExecute(command); } catch (RejectedExecutionException e) { // pass through if it is already the declared type throw e; } catch (Exception e) { throw new RejectedExecutionException(e); } } } /** * Hook for subclasses to execute the given command. Subclasses must ensure * that commands are executed in order. After {@link #shutdown()} completes, * it is guaranteed that this method will no longer be called. * * Implementation note: this method is called while holding a lock that * ensures it will not be called concurrently. This lock is required by * {@link #shutdown()}, {@link #shutdownNow()}, or * {@link #execute(Runnable)}. * * @param command * the runnable task, guaranteed to be non-null * @throws Exception * if this task cannot be accepted for execution */ protected abstract void doExecute(Runnable command) throws Exception; }