package org.argeo.slc.core.execution.generator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.slc.SlcException; import org.argeo.slc.core.execution.DefaultExecutionSpec; import org.argeo.slc.execution.ExecutionContext; import org.argeo.slc.execution.ExecutionFlow; import org.argeo.slc.execution.ExecutionSpec; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; /** * Execution Flow calling a list of Runnable (identified by their * bean name in the Spring Application Context) after configuring the Execution * context and a Map potentially shared by the called Runnable * */ public class RunnableCallFlow implements ExecutionFlow, ApplicationContextAware { private final static Log log = LogFactory.getLog(RunnableCallFlow.class); /** * Key in the execution context for the index of the call (e.g. 0 for the * first runnable called, ...) */ public final static String VAR_CALL_INDEX = "slcVar.runnableCallFlow.callIndex"; /** * Name of the flow. Also bean name */ private String name; /** * Path of the flow */ private String path; /** * Whether an exception in a Runnable shall stop the execution * of the flow */ private Boolean failOnError = true; /** * List of Runnable to call, with bean name, execution * variables and context values */ private List runnableCalls; /** * Map potentially referenced by called flows. Updated with the context * values of a Runnable before calling it. */ private Map sharedContextValuesMap; /** * ExecutionSpec of the flow. Does not contain any attribute. */ private ExecutionSpec executionSpec = new DefaultExecutionSpec(); /** * Reference to the ExecutionContext */ private ExecutionContext executionContext; /** * Reference to the Spring ApplicationContext. Set via * setApplicationContext, the class implementing * ApplicationContextAware */ private ApplicationContext applicationContext; /** * Runs a Runnable after configuring the Execution Context and * sharedContextValuesMap * * @param runnable * the Runnable to call * @param executionVariables * the variables to add to the ExecutionContext * @param contextValues * the variables to add to sharedContextValuesMap * @param callIndex * index of the call (0 for the first called * Runnable) set as variable of the * ExecutionContext */ private void run(Runnable runnable, Map executionVariables, Map contextValues, int callIndex) { // add all variables to the Execution Context for (Map.Entry entry : executionVariables.entrySet()) { executionContext.setVariable(entry.getKey(), entry.getValue()); } // add call Index Variable executionContext.setVariable(VAR_CALL_INDEX, callIndex); // clear sharedContextValues and add all values of contextValues if (sharedContextValuesMap != null) { sharedContextValuesMap.clear(); sharedContextValuesMap.putAll(contextValues); } // then run the runnable doExecuteRunnable(runnable); } public void doExecuteRunnable(Runnable runnable) { runnable.run(); } /** * Executes the flow. For each RunnableCall, the corresponding * flow is retrieved from the Spring Application Context, the * ExecutionContext and sharedContextValuesMap are * configured and the Runnable is called. */ public void run() { if (applicationContext == null) { throw new SlcException("No ApplicationContext defined"); } try { for (int callIndex = 0; callIndex < runnableCalls.size(); ++callIndex) { RunnableCall runnableCall = runnableCalls.get(callIndex); Object bean = applicationContext.getBean( runnableCall.getBeanName(), Runnable.class); if (log.isDebugEnabled()) log.debug("Running flow '" + runnableCall.getBeanName() + "'"); run((Runnable) bean, runnableCall.getExecutionVariables(), runnableCall.getContextValues(), callIndex); } } catch (RuntimeException e) { if (failOnError) throw e; else { log.error("Execution flow failed," + " but process did not fail" + " because failOnError property" + " is set to false: " + e); if (log.isTraceEnabled()) e.printStackTrace(); } } } public Iterator runnables() { List runnables = new ArrayList(); for (int callIndex = 0; callIndex < runnableCalls.size(); ++callIndex) { RunnableCall runnableCall = runnableCalls.get(callIndex); Object bean = applicationContext.getBean( runnableCall.getBeanName(), Runnable.class); runnables.add((Runnable) bean); } return runnables.iterator(); } public Runnable getRunnable() { if (runnableCalls.size() == 1) return runnables().next(); else throw new SlcException("There are " + runnableCalls.size() + " runnables in flow " + getName()); } @Override public String toString() { return new StringBuffer("RunnableCallFlow ").append(name).toString(); } public ExecutionSpec getExecutionSpec() { return executionSpec; } public String getName() { return name; } public Object getParameter(String key) { throw new SlcException("RunnableCallFlow have no parameters"); } public String getPath() { return path; } public Boolean isSetAsParameter(String key) { // The ExecutionSpec having no attribute, // always return false return false; } public void setName(String name) { this.name = name; } public void setPath(String path) { this.path = path; } public void setExecutionContext(ExecutionContext executionContext) { this.executionContext = executionContext; } public void setRunnableCalls(List runnableCalls) { this.runnableCalls = runnableCalls; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void setSharedContextValuesMap(Map contextValues) { this.sharedContextValuesMap = contextValues; } public void setFailOnError(Boolean failOnError) { this.failOnError = failOnError; } }