/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.util; import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A base class for any job which consists of cycles of work, and may be terminated between any two cycles. * This implements {@link Runnable} so that it may be executed in its own thread. */ public abstract class TerminatableJob implements Runnable { /** Logger. */ private static final Logger s_logger = LoggerFactory.getLogger(TerminatableJob.class); /** * A flag to indicate whether the job has been started */ private AtomicBoolean _started = new AtomicBoolean(false); /** * A flag to indicate whether the job has terminated. */ private volatile boolean _terminated; /** * Implements {@code Runnable} to add termination support. * To add behaviour, use the methods {@link #preStart()}, {@link #runOneCycle()} and {@link #postRunCycle()}. */ @Override public final void run() { if (_started.getAndSet(true)) { throw new IllegalStateException("Job has already been run or is currently running"); } preStart(); try { while (!isTerminated()) { runOneCycle(); } } catch (Exception e) { s_logger.warn("Job " + this + " terminated with unhandled exception", e); } finally { // Want to ensure that even if the job terminates with an exception (e.g. InterruptedException), the tidy-up // semantics of postRunCycle are preserved if (isTerminated()) { postRunCycle(); } } } /** * Invoked by {@link #run()} once, immediately before the cycle starts. */ protected void preStart() { } /** * Invoked by {@link #run()} to perform one cycle of the job. * Override this to implement actual behavior. */ protected abstract void runOneCycle(); /** * Invoked by {@link #run()} once, after the job is terminated and the last cycle completes. */ protected void postRunCycle() { } //------------------------------------------------------------------------- /** * Terminates the job after the current cycle completes. */ public void terminate() { _terminated = true; } /** * Gets whether the job has been started. * * @return true if the job has been started */ public boolean isStarted() { return _started.get(); } /** * Gets whether the job has been terminated. * * @return true if the job has been terminated */ public boolean isTerminated() { return _terminated; } }