package org.oddjob.jobs.structural; import org.oddjob.Stoppable; import org.oddjob.Structural; import org.oddjob.arooa.deploy.annotations.ArooaAttribute; import org.oddjob.arooa.deploy.annotations.ArooaComponent; import org.oddjob.framework.ComponentBoundry; import org.oddjob.framework.OptionallyTransient; import org.oddjob.framework.StructuralJob; import org.oddjob.state.SequentialHelper; import org.oddjob.state.StateOperator; import org.oddjob.state.WorstStateOp; /** * @oddjob.description Executes it's children in a sequence one after the * other. The sequence will only continue to be executed if each child * COMPLETEs. If a child is INCOMPLETE, or throws an EXCEPTION then execution * will terminate and this job's state will reflect that of the * failed child. * <p> * This behaviour can be changed by setting the <b><code>independent</code></b> * property which will cause execution to continue regardless of the last * executed child state. * * <h4>State Operator</h4> * * The <b><code>stateOperator</b></code> property changes the way in which * this jobs state reflects its child states. Oddjob currently supports the * following State Operators: * <dl> * <dt>ACTIVE</dt> * <dd>If any child is EXECUTING, ACTIVE or STARTING this job's state * will be ACTIVE. Otherwise, if a child is STARTED, this job's state * will be STARTED. Otherwise, if a child is READY, this job's state will * be READY. Otherwise, this job's state will reflect the worst state of * the child jobs.</dd> * <dt>WORST</dt> * <dd>This job's state will be EXCEPTION or INCOMPLETE if any of the * child job's are in this state. Otherwise the rules for ACTIVE apply.</dd> * <dt>SERVICES</dt> * <dd>This state operator is designed for starting services. This job * will COMPLETE when all services are STARTED. If any * services fails to start this job reflects the EXCEPTION state. * Because this job, when using this state operator, completes even though * it's children are running, this job is analogous to creating daemon * threads in that the services will not stop Oddjob from shutting down * once all other jobs have completed.</dd> * </dl> * * <h4>Stopping</h4> * As with other structural jobs, when this job is stopping, either because * of a manual stop, or during Oddjob's shutdown cycle, the child jobs and * services will still be stopped in an reverse order. * * <h4>Persistence</h4> * If this job has an Id and Oddjob is running with a Persister, then * this job's state will be persisted when it changes. Thus a COMPLETE * state will be persisted once all child jobs have completed. If Oddjob * is restarted at this point the COMPLETE state of this job will stop * the child job's from re-running, if though they themselves might not * have been persisted. To stop this job from being persisted set the * <code>transient</code> property to true. Not that when starting * services with this job, persistence is probably not desirable as * it will stop the services from re-starting. * * <h4>Re-running Child Jobs</h4> * * If the failed job is later run manually and completes this Job will * reflect the new state. As such it is useful as a trigger point for * the completion of a sequence of jobs. * * * @oddjob.example * * A simple sequence of two jobs. * * {@oddjob.xml.resource org/oddjob/jobs/structural/SimpleSequentialExample.xml} * * * @oddjob.example * * Starting two services. To perform odd jobs, in a workshop for instance, * this first 'job' is to turn on the lights and turn on any machines * required. The service manager encompasses this idea - and this example * embelishes the idea. Real odd jobs for Oddjob will involve activities * such as starting services such as a data source or a server connection. * The concept however is still the same. * * {@oddjob.xml.resource org/oddjob/jobs/structural/ServiceManagerExample.xml} * * The services are started in order. Once both services have started * a job is performed that requires both services. If this configuration * were running from the command line, Oddjob would stop the services * as it shut down. First the machine would be turned of and then finally * the lights would be turned out. * * @author Rob Gordon */ public class SequentialJob extends StructuralJob<Object> implements Structural, Stoppable, OptionallyTransient { private static final long serialVersionUID = 20111017; /** Are children independent? i.e does failure stop the sequence. */ private volatile boolean independent; /** * @oddjob.property transient * @oddjob.description Is this job transient. If true state will not * be persisted. * @oddjob.required No, default is false. * * @param stateOperator The state operator to be applied to children's * states to derive our state. */ private volatile boolean _transient; /** * @oddjob.property stateOperator * @oddjob.description Set the way the children's state is * evaluated and reflected by the parent. Values can be WORST, * ACTIVE, or SERVICES. * @oddjob.required No, default is WORST. * * @param stateOperator The state operator to be applied to children's * states to derive our state. */ @ArooaAttribute public void setStateOperator(StateOperator stateOperator) { ComponentBoundry.push(loggerName(), this); try { this.structuralState.setStateOperator(stateOperator); } finally { ComponentBoundry.pop(); } } public StateOperator getStateOperator() { return this.structuralState.getStateOperator(); } /* * (non-Javadoc) * @see org.oddjob.framework.StructuralJob#getStateOp() */ @Override public StateOperator getInitialStateOp() { return new WorstStateOp(); } /** * Add a child. * * @oddjob.property jobs * @oddjob.description The child jobs. * @oddjob.required No, but pointless if missing. * * @param child A child */ @ArooaComponent public void setJobs(int index, Object child) { if (child == null) { childHelper.removeChildAt(index); } else { childHelper.insertChild(index, child); } } /* * (non-Javadoc) * @see org.oddjob.jobs.AbstractJob#execute() */ public void execute() throws Exception { for (Object child : childHelper) { if (stop) { stop = false; break; } if (!(child instanceof Runnable)) { logger().info("Not Executing [" + child + "] as it is not a job."); } else { Runnable job = (Runnable) child; logger().info("Executing child [" + job + "]"); job.run(); } // Test we can still continue children. if (!(independent || new SequentialHelper().canContinueAfter(child))) { logger().info("Child [" + child + "] failed. Can't continue."); break; } } } public boolean isIndependent() { return independent; } /** * Set whether children are considered dependent (false, default) * or independent (true). * * @oddjob.property independent * @oddjob.description Whether the child jobs are independent or not. * @oddjob.required Default is dependent child jobs. * * @param independent flag value to set */ public void setIndependent(boolean independent) { this.independent = independent; } /* * (non-Javadoc) * @see org.oddjob.framework.OptionallyTransient#isTransient() */ public boolean isTransient() { return _transient; } public void setTransient(boolean _transient) { this._transient = _transient; } }