package org.oddjob.framework; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.oddjob.FailedToStopException; import org.oddjob.Stateful; import org.oddjob.logging.LogEnabled; import org.oddjob.state.IsStoppable; import org.oddjob.state.StateListener; import org.oddjob.state.State; import org.oddjob.state.StateEvent; /** * A utility class to provide wait until stopped functionality. * <p> * The default timeout is 5 seconds before a {@link FailedToStopException} * is thrown. * * @author rob * */ public class StopWait { private final Stateful stateful; private final Logger logger; private final long timeout; /** * Constructor with default timeout. * * @param stateful The thing to wait until stopped. */ public StopWait(Stateful stateful) { this(stateful, 5000); } /** * Constructor where timeout can be specified. * * @param stateful The thing to wait until stopped. * @param timeout The timeout. Note that a timeout of 0 or less is * no timeout. */ public StopWait(Stateful stateful, long timeout) { this.stateful = stateful; if (stateful instanceof LogEnabled) { logger = Logger.getLogger(((LogEnabled) stateful).loggerName()); } else { logger = Logger.getLogger(stateful.getClass()); } this.timeout = timeout; } /** * Run the stop wait. This will block until the job stops or the * timeout occurs. * * @throws FailedToStopException If timeout occurs. */ public void run() throws FailedToStopException { if (new IsStoppable().test( stateful.lastStateEvent().getState())) { doWait(); } } private void doWait() throws FailedToStopException { final BlockingQueue<State> handoff = new LinkedBlockingQueue<State>(); class StopListener implements StateListener { @Override public void jobStateChange(StateEvent event) { handoff.add(event.getState()); } }; StopListener listener = new StopListener(); stateful.addStateListener(listener); try { while (true) { State state = handoff.poll(timeout, TimeUnit.MILLISECONDS); if (state == null) { logger.debug("[" + stateful + "] stop wait timed out. Exception is on it's way..."); throw new FailedToStopException(stateful); } if (!state.isStoppable()) { logger.debug("[" + stateful + "] is " + state + ", waiting to stop is over."); return; } logger.debug("[" + stateful + "] is " + state + ", waiting to stop..."); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { stateful.removeStateListener(listener); } } }