--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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