package org.oddjob.framework; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import javax.inject.Inject; import org.oddjob.FailedToStopException; import org.oddjob.Stoppable; import org.oddjob.arooa.deploy.annotations.ArooaComponent; import org.oddjob.state.IsStoppable; import org.oddjob.state.ParentState; /** * An abstract base class for Structural jobs where all child jobs * can run simultaneously. * * @author rob * */ abstract public class SimultaneousStructural extends StructuralJob<Object> implements Stoppable { private static final long serialVersionUID = 2009031800L; /** The executor to use. */ private volatile transient ExecutorService executorService; /** Watch execution to start the state reflector when all children * have finished, and track job threads. */ private volatile transient AsyncExecutionSupport asyncSupport; /** * Create a new instance. */ public SimultaneousStructural() { completeConstruction(); } /** * Called once following construction or deserialisation. */ private void completeConstruction() { asyncSupport = new AsyncExecutionSupport(new Runnable() { public void run() { stop = false; SimultaneousStructural.super.startChildStateReflector(); } }); } /** * Set the {@link ExecutorService}. * * @oddjob.property executorService * @oddjob.description The ExecutorService to use. This will * be automatically set by Oddjob. * @oddjob.required No. * * @param child A child */ @Inject public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } /** * Getter for executor service. * * @return The executor service or null if not set. */ public ExecutorService getExecutorService() { return executorService; } /** * Add a child job. * * @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) { logger().debug( "Adding child [" + child + "], index [" + index + "]"); if (child == null) { childHelper.removeChildAt(index); } else { childHelper.insertChild(index, child); } } /* * (non-Javadoc) * @see org.oddjob.jobs.AbstractJob#execute() */ protected void execute() throws InterruptedException, ExecutionException { if (executorService == null) { throw new NullPointerException("No Executor! Were services set?"); } asyncSupport.reset(); for (Object child : childHelper) { if (stop) { break; } if (!(child instanceof Runnable)) { logger().info("Child [" + child + "] is not Runnable - ignoring."); continue; } Runnable job = (Runnable) child; asyncSupport.submitJob(executorService, job); logger().info("Submitted [" + job + "]"); } if (isJoin()) { logger().info("Join property is set, waiting for threads to finish."); asyncSupport.joinOnAllJobs(); } else { if (asyncSupport.size() > 0) { stateHandler().waitToWhen(new IsStoppable(), new Runnable() { public void run() { getStateChanger().setState(ParentState.ACTIVE); } }); } } // Stop will have called asyncSupport#stopAllJobs which will have // called startChildStateReflector which will reflect state of // stopped jobs. if (!stop) { asyncSupport.startWatchingJobs(); } } @Override protected void onStop() throws FailedToStopException { super.onStop(); asyncSupport.cancelAllPendingJobs(); } @Override protected void startChildStateReflector() { // This is started by us so override and do nothing. } /** * Should execution wait for all jobs to be executed before returning * to calling code. * <p> * This property is intended to be overridden by sub classes that * wish to give that choice to users. * * @return true for execution to wait for children. False for completion * to be asynchronous. */ public boolean isJoin() { return false; } /* * Custom serialization. */ private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); } /* * Custom serialization. */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); completeConstruction(); } }