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;
}
}