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