Add Flow Generator
authorOlivier Capillon <olivier.capillon@gmail.com>
Mon, 19 Oct 2009 18:28:39 +0000 (18:28 +0000)
committerOlivier Capillon <olivier.capillon@gmail.com>
Mon, 19 Oct 2009 18:28:39 +0000 (18:28 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@3046 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/CompositeRunnableFactory.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/DefaultRunnableDataNode.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/ExecutionFlowGenerator.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/ExecutionFlowGeneratorSource.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCall.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCallFlow.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCallFlowDescriptor.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableDataNode.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableFactory.java [new file with mode: 0644]

diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/CompositeRunnableFactory.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/CompositeRunnableFactory.java
new file mode 100644 (file)
index 0000000..7a43f66
--- /dev/null
@@ -0,0 +1,62 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.Map;\r
+\r
+import org.argeo.slc.SlcException;\r
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;\r
+\r
+/**\r
+ * Composite <code>RunnableFactory</code>, redirecting the Runnable \r
+ * creation to on of the configured <code>RunnableFactory</code> depending\r
+ * on an entry of the data of the <code>RunnableDataNode</code>.\r
+ */\r
+public class CompositeRunnableFactory implements RunnableFactory {\r
+\r
+       /**\r
+        * Key used to access factory ID in the data of the <code>RunnableDataNode</code>\r
+        */\r
+       private String factoryKey;\r
+\r
+       /**\r
+        * Maps a factory ID to an ExecutionFlowFactory\r
+        */\r
+       private Map<String, RunnableFactory> factories;\r
+\r
+       public void createAndRegisterRunnable(RunnableDataNode node,\r
+                       BeanDefinitionRegistry beanDefinitionRegistry) {\r
+               findFactory(node).createAndRegisterRunnable(node, beanDefinitionRegistry);\r
+       }       \r
+       \r
+       /**\r
+        * Finds the <code>RunnableFactory</code> to use for a <code>RunnableDataNode</code>\r
+        * @param node\r
+        * @return the <code>RunnableFactory</code> to use for the <code>RunnableDataNode</code>\r
+        */\r
+       private RunnableFactory findFactory(RunnableDataNode node) {\r
+               // get the factory ID from the data of the RunnableDescriptor\r
+               Map<String, Object> data = node.getData();\r
+               if (!data.containsKey(factoryKey)) {\r
+                       throw new SlcException("No data value for key '" + factoryKey + "'");\r
+               }\r
+               String factoryId = data.get(factoryKey).toString();\r
+               \r
+               // see if we have a factory for the factory ID\r
+               if ((factories != null) && factories.containsKey(factoryId)) {\r
+                       return factories.get(factoryId);\r
+               }\r
+               // if not, look for a bean of name equals to the factory ID\r
+               else {\r
+                       throw new SlcException("Not implemented");\r
+               }               \r
+       }\r
+       \r
+       public void setFactoryKey(String factoryKey) {\r
+               this.factoryKey = factoryKey;\r
+       }\r
+\r
+       public void setFactories(Map<String, RunnableFactory> factories) {\r
+               this.factories = factories;\r
+       }\r
+\r
+\r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/DefaultRunnableDataNode.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/DefaultRunnableDataNode.java
new file mode 100644 (file)
index 0000000..da20f18
--- /dev/null
@@ -0,0 +1,89 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+/**\r
+ * Default implementation of <code>RunnableDataNode</code>\r
+ *\r
+ */\r
+public class DefaultRunnableDataNode implements RunnableDataNode {\r
+\r
+       private List<RunnableDataNode> children = new ArrayList<RunnableDataNode>();\r
+       \r
+       private RunnableDataNode parent;\r
+       \r
+       /**\r
+        * Data of the RunnableDataNode. Does not contain\r
+        * parent data.\r
+        */\r
+       private Map<String, Object> properData = new HashMap<String, Object>();\r
+       \r
+       private String path;\r
+       \r
+       private String beanName;\r
+\r
+       public boolean isLeaf() {\r
+               return children.size() == 0;\r
+       }\r
+       \r
+       public List<RunnableDataNode> getChildren() {\r
+               return children;\r
+       }\r
+\r
+       public void addChild(RunnableDataNode child) {\r
+               child.setParent(this);\r
+               children.add(child);\r
+       }\r
+       \r
+       public Map<String, Object> getData() {\r
+               Map<String, Object> data = new HashMap<String, Object>();\r
+               if(parent != null) {\r
+                       Map<String, Object> parentData = parent.getData();\r
+                       if(parentData != null) {\r
+                               data.putAll(parentData);\r
+                       }\r
+               }\r
+               // entries defined in parentData can be overridden\r
+               // in properData\r
+               if(properData != null) {\r
+                       data.putAll(properData);\r
+               }\r
+               return data;\r
+       }\r
+\r
+       public Map<String, Object> getProperData() {\r
+               return properData;\r
+       }\r
+\r
+       public void setProperData(Map<String, Object> properData) {\r
+               this.properData = properData;\r
+       }\r
+\r
+       public String getPath() {\r
+               return path;\r
+       }\r
+\r
+       public void setPath(String path) {\r
+               this.path = path;\r
+       }\r
+\r
+       public String getBeanName() {\r
+               return beanName;\r
+       }\r
+\r
+       public void setBeanName(String beanName) {\r
+               this.beanName = beanName;\r
+       }\r
+\r
+       public void setParent(RunnableDataNode parent) {\r
+               this.parent = parent;\r
+       }\r
+\r
+       public RunnableDataNode getParent() {\r
+               return parent;\r
+       }\r
+\r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/ExecutionFlowGenerator.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/ExecutionFlowGenerator.java
new file mode 100644 (file)
index 0000000..5451402
--- /dev/null
@@ -0,0 +1,155 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.HashMap;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.argeo.slc.SlcException;\r
+import org.springframework.aop.scope.ScopedProxyUtils;\r
+import org.springframework.beans.BeansException;\r
+import org.springframework.beans.MutablePropertyValues;\r
+import org.springframework.beans.factory.config.BeanDefinitionHolder;\r
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;\r
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\r
+import org.springframework.beans.factory.config.RuntimeBeanReference;\r
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;\r
+import org.springframework.beans.factory.support.GenericBeanDefinition;\r
+import org.springframework.core.Ordered;\r
+import org.springframework.core.PriorityOrdered;\r
+\r
+/**\r
+ * Generates <code>ExecutionFlows</code> and <code>Runnables</code> as\r
+ * beans in the Spring Application Context.\r
+ * Called by the Application Context as a <code>BeanFactoryPostProcessor</code>.\r
+ * Two kinds of beans are generated:\r
+ * <code>RunnableCallFlow</code>, calling a list of <code>Runnables</code> from the\r
+ * Application Context after configuring the <code>ExecutionContext</code>, \r
+ * and outputs of a <code>RunnableFactory</code>.\r
+ */\r
+/**\r
+ * @author Olivier Capillon\r
+ *\r
+ */\r
+public class ExecutionFlowGenerator implements BeanFactoryPostProcessor,\r
+               PriorityOrdered {\r
+       \r
+       private final Log log = LogFactory.getLog(getClass());\r
+\r
+       /**\r
+        * Source providing a list of <code>RunnableCallFlowDescriptor</code> \r
+        * used to create <code>RunnableCallFlow</code> and a list of \r
+        * <code>RunnableDataNode</code> used to create any kind of flow via a factory\r
+        */\r
+       protected ExecutionFlowGeneratorSource source;\r
+       \r
+       /**\r
+        * Factory used to create Runnables in the Application context from\r
+        * the <code>RunnableDataNode</code> provided from the source.\r
+        */\r
+       protected RunnableFactory runnableFactory;\r
+       \r
+       /**\r
+        * Bean name of the <code>ExecutionContext</code>.\r
+        * Used to provide the created <code>RunnableCallFlow</code> beans \r
+        * with a <code>RuntimeBeanReference</code> to\r
+        * the <code>ExecutionContext</code>\r
+        */\r
+       private String executionContextBeanName = "executionContext";\r
+       \r
+       /**\r
+        * Bean name of the context values Map.\r
+        * A bean of class HashMap is created with this name, and a \r
+        * <code>RuntimeBeanReference</code> is provided to the created\r
+        * <code>RunnableCallFlow</code> beans.\r
+        */\r
+       private String contextValuesBeanName = "executionFlowGenerator.contextValues";\r
+               \r
+       public void postProcessBeanFactory(\r
+                       ConfigurableListableBeanFactory beanFactory) throws BeansException {\r
+\r
+               // assert that the beanFactory is a BeanDefinitionRegistry\r
+               if (!(beanFactory instanceof BeanDefinitionRegistry)) {\r
+                       throw new SlcException("Can only work on "\r
+                                       + BeanDefinitionRegistry.class);\r
+               } \r
+               \r
+               // add bean for the Context Values Map\r
+               createAndRegisterContextValuesBean((BeanDefinitionRegistry) beanFactory);\r
+               \r
+               // add beans for each RunnableDataNode\r
+               for(RunnableDataNode node : source.getRunnableDataNodes()) {\r
+                       runnableFactory.createAndRegisterRunnable(node, (BeanDefinitionRegistry) beanFactory);\r
+               }\r
+               \r
+               // add beans for each RunnableCallFlowDescriptor of the source to the application context\r
+               for (RunnableCallFlowDescriptor descriptor : source\r
+                               .getRunnableCallFlowDescriptors()) {\r
+                       createAndRegisterFlowFor(descriptor, (BeanDefinitionRegistry) beanFactory);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Creates a <code>RunnableCallFlow</code> bean\r
+        * for a <code>RunnableCallFlowDescriptor</code> and registers \r
+        * it in the <code>BeanDefinitionRegistry</code>\r
+        * @param flowDescriptor\r
+        * @param registry\r
+        */\r
+       private void createAndRegisterFlowFor(RunnableCallFlowDescriptor flowDescriptor, BeanDefinitionRegistry registry) {\r
+               // create the flow bean\r
+               GenericBeanDefinition flowBean = new GenericBeanDefinition();\r
+               flowBean.setBeanClass(RunnableCallFlow.class);\r
+               \r
+               MutablePropertyValues mpv = new MutablePropertyValues();                \r
+               mpv.addPropertyValue("runnableCalls", flowDescriptor.getRunnableCalls());\r
+               mpv.addPropertyValue("sharedContextValuesMap", new RuntimeBeanReference(contextValuesBeanName));\r
+               \r
+               mpv.addPropertyValue("name", flowDescriptor.getBeanName());\r
+               mpv.addPropertyValue("path", flowDescriptor.getPath());\r
+\r
+               mpv.addPropertyValue("executionContext", new RuntimeBeanReference(executionContextBeanName));\r
+               \r
+               flowBean.setPropertyValues(mpv);\r
+               \r
+               // register it\r
+               if(log.isDebugEnabled()) {\r
+                       log.debug("Registering bean definition for RunnableCallFlow " + flowDescriptor.getBeanName());\r
+               }\r
+               registry.registerBeanDefinition(flowDescriptor.getBeanName(), flowBean);\r
+       }\r
+       \r
+       /**\r
+        * Creates the Context Values bean and register it in the\r
+        * <code>BeanDefinitionRegistry</code>\r
+        * @param registry\r
+        */\r
+       private void createAndRegisterContextValuesBean(BeanDefinitionRegistry registry) {\r
+               GenericBeanDefinition contextValuesBean = new GenericBeanDefinition();\r
+               contextValuesBean.setBeanClass(HashMap.class);\r
+               \r
+               BeanDefinitionHolder bdh = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(contextValuesBean, contextValuesBeanName), registry, true);                                                                                                                      \r
+               registry.registerBeanDefinition(contextValuesBeanName, bdh.getBeanDefinition());                \r
+       }\r
+       \r
+       public int getOrder() {\r
+               return Ordered.HIGHEST_PRECEDENCE;\r
+       }\r
+\r
+       public void setSource(ExecutionFlowGeneratorSource source) {\r
+               this.source = source;\r
+       }\r
+\r
+       public void setRunnableFactory(RunnableFactory runnableFactory) {\r
+               this.runnableFactory = runnableFactory;\r
+       }\r
+\r
+       public void setExecutionContextBeanName(String executionContextBeanName) {\r
+               this.executionContextBeanName = executionContextBeanName;\r
+       }\r
+\r
+       public void setContextValuesBeanName(String contextValuesBeanName) {\r
+               this.contextValuesBeanName = contextValuesBeanName;\r
+       }\r
+\r
+\r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/ExecutionFlowGeneratorSource.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/ExecutionFlowGeneratorSource.java
new file mode 100644 (file)
index 0000000..8dc5d7d
--- /dev/null
@@ -0,0 +1,24 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.List;\r
+\r
+/**\r
+ * Provides 2 types of information required by an <code>ExecutionFlowGenerator</code>: \r
+ * a list of <code>RunnableCallFlowDescriptor</code> used to create <code>RunnableCallFlow</code>\r
+ * and a list of <code>RunnableDataNode</code> used to create any kind of flow via a factory.\r
+ */\r
+public interface ExecutionFlowGeneratorSource {\r
+       \r
+       /**\r
+        * @return a list of <code>RunnableCallFlowDescriptor</code> used \r
+        * by a <code>ExecutionFlowGenerator</code> to create <code>RunnableCallFlow</code>\r
+        */\r
+       public List<RunnableCallFlowDescriptor> getRunnableCallFlowDescriptors();\r
+       \r
+       /**\r
+        * @return a list of <code>RunnableDataNode</code> used \r
+        * by a <code>ExecutionFlowGenerator</code> to create any kind of flow via a factory\r
+        */\r
+       public List<RunnableDataNode> getRunnableDataNodes();\r
+       \r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCall.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCall.java
new file mode 100644 (file)
index 0000000..d44ff2e
--- /dev/null
@@ -0,0 +1,55 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.Map;\r
+\r
+/**\r
+ * Storage class for information required to call a flow \r
+ * of the Spring execution context: \r
+ * bean name of the flow,\r
+ * variables to add to the Execution Context before the call \r
+ * and variables (context values) to add to a Map \r
+ * potentially referenced by the called flow \r
+ */\r
+public class RunnableCall {\r
+       \r
+       /**\r
+        * Bean name of the flow to call\r
+        */\r
+       private String beanName;\r
+       \r
+       /**\r
+        * Variables to add to the execution context before the call\r
+        */\r
+       private Map<String, Object> executionVariables;\r
+       \r
+       /**\r
+        * Variables to add to the Map potentially referenced by\r
+        * the called flow\r
+        */\r
+       private Map<String, Object> contextValues;\r
+\r
+       public String getBeanName() {\r
+               return beanName;\r
+       }\r
+\r
+       public void setBeanName(String beanName) {\r
+               this.beanName = beanName;\r
+       }\r
+\r
+       public Map<String, Object> getExecutionVariables() {\r
+               return executionVariables;\r
+       }\r
+\r
+       public void setExecutionVariables(Map<String, Object> executionVariables) {\r
+               this.executionVariables = executionVariables;\r
+       }\r
+\r
+       public Map<String, Object> getContextValues() {\r
+               return contextValues;\r
+       }\r
+\r
+       public void setContextValues(Map<String, Object> contextValues) {\r
+               this.contextValues = contextValues;\r
+       }\r
+\r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCallFlow.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCallFlow.java
new file mode 100644 (file)
index 0000000..4e58bfc
--- /dev/null
@@ -0,0 +1,190 @@
+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
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCallFlowDescriptor.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableCallFlowDescriptor.java
new file mode 100644 (file)
index 0000000..68f9cb9
--- /dev/null
@@ -0,0 +1,55 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+/**\r
+ * Storage Class for information required to\r
+ * instantiate a <code>RunnableCallFlow</code>:\r
+ * bean name of the flow, \r
+ * path of the flow \r
+ * and list of <code>RunnableCall</code>. \r
+ *\r
+ */\r
+public class RunnableCallFlowDescriptor {\r
+       \r
+       /**\r
+        * Bean name of the flow to instantiate\r
+        */\r
+       private String beanName;\r
+       \r
+       /**\r
+        * Path of the flow to instantiate\r
+        */\r
+       private String path;\r
+       \r
+       /**\r
+        * List of <code>RunnableCall</code> \r
+        */\r
+       private List<RunnableCall> runnableCalls = new ArrayList<RunnableCall>();\r
+\r
+       public String getBeanName() {\r
+               return beanName;\r
+       }\r
+\r
+       public void setBeanName(String beanName) {\r
+               this.beanName = beanName;\r
+       }\r
+\r
+       public String getPath() {\r
+               return path;\r
+       }\r
+\r
+       public void setPath(String path) {\r
+               this.path = path;\r
+       }\r
+\r
+       public List<RunnableCall> getRunnableCalls() {\r
+               return runnableCalls;\r
+       }\r
+\r
+       public void setRunnableCalls(List<RunnableCall> runnableCalls) {\r
+               this.runnableCalls = runnableCalls;\r
+       }\r
+       \r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableDataNode.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableDataNode.java
new file mode 100644 (file)
index 0000000..25fee91
--- /dev/null
@@ -0,0 +1,59 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+/**\r
+ * Stores information relative to a Runnable.\r
+ * Allows to structure the information as a tree, each node\r
+ * storing data as a Map.\r
+ */\r
+public interface RunnableDataNode {\r
+\r
+       /**\r
+        * @return a Map containing the data associated with this node.\r
+        * Data associated with parent nodes are expected\r
+        * to be contained in the returned Map\r
+        */\r
+       public Map<String, Object> getData();   \r
+\r
+       /**\r
+        * @return the name of the bean to create.\r
+        * Can be null if no bean shall be created for the \r
+        * <code>RunnableDataNode</code> (e.g. is is a sub-node)\r
+        */\r
+       public String getBeanName();\r
+       \r
+       /**\r
+        * @return the path of the flow bean to create.\r
+        * Can be null if the bean to created is not an\r
+        * <code>ExecutionFlow</code> or if no bean shall be created for the \r
+        * <code>RunnableDataNode</code> (e.g. is is a sub-node)\r
+        */\r
+       public String getPath();\r
+               \r
+       /**\r
+        * @return whether the <code>RunnableDataNode</code> has\r
+        * children or not.\r
+        * Expected to be equivalent to <code>getChildren().empty()</code>\r
+        */\r
+       public boolean isLeaf();\r
+\r
+       /**\r
+        * @return the list of <code>RunnableDataNode</code> children.\r
+        * Can be empty. Shall not be null.\r
+        */\r
+       public List<RunnableDataNode> getChildren();\r
+       \r
+       /**\r
+        * @return the <code>RunnableDataNode</code> parent.\r
+        * Can be null if no parent is defined (top node).\r
+        */\r
+       public RunnableDataNode getParent();\r
+       \r
+       /**\r
+        * Sets the <code>RunnableDataNode</code> parent\r
+        * @param parent\r
+        */\r
+       public void setParent(RunnableDataNode parent); \r
+}\r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableFactory.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/generator/RunnableFactory.java
new file mode 100644 (file)
index 0000000..cd0af70
--- /dev/null
@@ -0,0 +1,14 @@
+package org.argeo.slc.core.execution.generator;\r
+\r
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;\r
+\r
+/**\r
+ * Interprets a <code>RunnableDataNode</code> by creating corresponding\r
+ * beans and registering them in a <code>BeanDefinitionRegistry</code>\r
+ *\r
+ */\r
+public interface RunnableFactory {\r
+\r
+       public void createAndRegisterRunnable(RunnableDataNode node,\r
+                       BeanDefinitionRegistry beanDefinitionRegistry);\r
+}\r