package org.oddjob.state; import java.util.concurrent.atomic.AtomicReference; import org.oddjob.Stateful; import org.oddjob.arooa.deploy.annotations.ArooaComponent; import org.oddjob.framework.StructuralJob; import org.oddjob.util.OddjobConfigException; /** * @oddjob.description * * Waits for a COMPLETE state from it's child job before allowing * the thread of execution to continue. * <p> * * @oddjob.example * * An join that waits for two triggers. In this example another trigger * to run the last job might be a better solution because it wouldn't hog * a thread - but there are situations when join is just simpler. * * {@oddjob.xml.resource org/oddjob/state/JoinExample.xml} * * @author Rob Gordon */ public class JoinJob extends StructuralJob<Runnable> { private static final long serialVersionUID = 2010081600L; private volatile long timeout; /** * Set the child job. * * @oddjob.property job * @oddjob.description The child job. * @oddjob.required No, but pointless if missing. * * @param child A child */ @ArooaComponent public void setJob(Runnable child) { if (child == null) { logger().debug("Removing child."); childHelper.removeAllChildren(); } else { logger().debug("Adding child [" + child + "]"); if (childHelper.getChild() != null) { throw new OddjobConfigException( "Join can't have more than one child component."); } childHelper.insertChild(0, child); } } /* * (non-Javadoc) * @see org.oddjob.jobs.AbstractJob#execute() */ protected void execute() throws InterruptedException { final Runnable child = childHelper.getChild(); if (child == null) { return; } child.run(); final AtomicReference<State> state = new AtomicReference<State>(); StateListener listener = new StateListener() { @Override public void jobStateChange(StateEvent event) { State nowState = event.getState(); state.set(nowState); if (!nowState.isStoppable()) { synchronized (JoinJob.this) { JoinJob.this.notifyAll(); } ((Stateful) child).removeStateListener(this); } } }; ((Stateful) child).addStateListener(listener); try { if (!stop && state.get().isStoppable()) { synchronized(this) { wait(timeout); } } if (!stop && state.get().isStoppable()) { throw new IllegalStateException("Join failed within timeout of " + timeout); } } finally { removeStateListener(listener); } stop = false; } @Override protected StateOperator getInitialStateOp() { return new AnyActiveStateOp(); } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout = timeout; } }