FlowNamespace extended (flows in flows, param in arg)
authorOlivier Capillon <olivier.capillon@gmail.com>
Tue, 18 May 2010 08:39:00 +0000 (08:39 +0000)
committerOlivier Capillon <olivier.capillon@gmail.com>
Tue, 18 May 2010 08:39:00 +0000 (08:39 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@3580 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/InstantiationManager.java
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/xml/ExecutionScopeDecorator.java [new file with mode: 0644]
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/xml/FlowBeanDefinitionParser.java
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/xml/FlowNamespaceHandler.java
runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/xml/SpecBeanDefinitionParser.java
runtime/org.argeo.slc.core/src/main/resources/org/argeo/slc/core/execution/xml/slc-flow-0.12.xsd
runtime/org.argeo.slc.core/src/test/java/org/argeo/slc/core/execution/xml/FlowNamespaceTest.java
runtime/org.argeo.slc.core/src/test/resources/org/argeo/slc/core/execution/xml/advanced.xml [new file with mode: 0644]

index eb6ce53473407a258fb5f1a6af52401be5201eb8..c3692a55026dffab43ca0dc3dfc6eceff6191c10 100644 (file)
@@ -26,14 +26,14 @@ public class InstantiationManager {
        }\r
 \r
        public void flowInitializationStarted(ExecutionFlow flow, String flowName) {\r
-               if (log.isTraceEnabled())\r
-                       log.trace("Start initialization of " + flow.hashCode() + " ("\r
-                                       + flow + " - " + flow.getClass() + ")");\r
-\r
                // set the flow name if it is DefaultExecutionFlow\r
                if (flow instanceof DefaultExecutionFlow) {\r
                        ((DefaultExecutionFlow) flow).setBeanName(flowName);\r
                }\r
+               \r
+               if (log.isTraceEnabled())\r
+                       log.trace("Start initialization of " + flow.hashCode() + " ("\r
+                                       + flow + " - " + flow.getClass() + ")");\r
 \r
                // log.info("# flowInitializationStarted " + flowName);\r
                // create a stack for this thread if there is none\r
@@ -47,12 +47,19 @@ public class InstantiationManager {
                if (log.isTraceEnabled())\r
                        log.trace("Finish initialization of " + flow.hashCode() + " ("\r
                                        + flow + " - " + flow.getClass() + ")");\r
-               ExecutionFlow registeredFlow = flowStack.get().pop();\r
-               if (registeredFlow != null) {\r
-                       if (!flow.getName().equals(registeredFlow.getName()))\r
-                               throw new SlcException("Current flow is " + flow);\r
-                       // log.info("# flowInitializationFinished " + flowName);\r
-                       // initializingFlow.set(null);\r
+               \r
+               if(flowStack.get() != null) {\r
+                       ExecutionFlow registeredFlow = flowStack.get().pop();\r
+                       if (registeredFlow != null) {\r
+                               if (!flow.getName().equals(registeredFlow.getName()))\r
+                                       throw new SlcException("Current flow is " + flow);\r
+                               // log.info("# flowInitializationFinished " + flowName);\r
+                               // initializingFlow.set(null);\r
+                       }\r
+               }\r
+               else {\r
+                       // happens for flows imported as services\r
+                       log.warn("flowInitializationFinished - Flow Stack is null");\r
                }\r
        }\r
 \r
diff --git a/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/xml/ExecutionScopeDecorator.java b/runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/xml/ExecutionScopeDecorator.java
new file mode 100644 (file)
index 0000000..e5738b1
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.slc.core.execution.xml;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+import org.springframework.aop.scope.ScopedProxyUtils;\r
+import org.springframework.beans.factory.config.BeanDefinitionHolder;\r
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;\r
+import org.springframework.beans.factory.xml.BeanDefinitionDecorator;\r
+import org.springframework.beans.factory.xml.ParserContext;\r
+import org.w3c.dom.Node;\r
+\r
+/**\r
+ * Inspired by org.springframework.aop.config.AopNamespaceHandler\r
+ * Conceived to replace Element "aop:scoped-proxy" by an attribute.\r
+ * Does not work correctly with other attribute decorators (e.g. \r
+ * p namespace) since this decorator needs to be called after all\r
+ * properties have been set on target bean. \r
+ */\r
+public class ExecutionScopeDecorator implements BeanDefinitionDecorator {\r
+       private Log log = LogFactory.getLog(ExecutionScopeDecorator.class);\r
+       \r
+       public BeanDefinitionHolder decorate(Node node,\r
+                       BeanDefinitionHolder definition, ParserContext parserContext) {\r
+               \r
+               Boolean isVar = Boolean.valueOf(node.getNodeValue());\r
+                               \r
+               if(isVar) {\r
+                       definition.getBeanDefinition().setScope("execution");\r
+                       \r
+                       boolean proxyTargetClass = true;\r
+                       \r
+                       // Register the original bean definition as it will be referenced by the scoped proxy and is relevant for tooling (validation, navigation).\r
+                       String targetBeanName = ScopedProxyUtils.getTargetBeanName(definition.getBeanName());\r
+                       parserContext.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(definition.getBeanDefinition(), targetBeanName));\r
+                       \r
+                       log.debug("Decorating bean " + definition.getBeanName());\r
+                       \r
+                       return ScopedProxyUtils.createScopedProxy(definition, parserContext.getRegistry(), proxyTargetClass);           \r
+               }\r
+               else {\r
+                       return definition;\r
+               }\r
+       }\r
+}\r
index 30fc5926cfb29915b7b1e9186a30275ca3d44423..8594e956e4b375c4185dd3628d17d2ddee228d11 100644 (file)
@@ -42,6 +42,10 @@ public class FlowBeanDefinitionParser extends
                if (StringUtils.hasText(parent))
                        builder.setParentName(parent);
 
+               
+               builder.getBeanDefinition().setDescription(DomUtils.getChildElementValueByTagName(element, 
+                               "description"));
+               
                List<Element> execElems = new ArrayList<Element>();
                List<Element> argsElems = new ArrayList<Element>();
                NodeList nodeList = element.getChildNodes();
@@ -50,7 +54,7 @@ public class FlowBeanDefinitionParser extends
                        if (node instanceof Element) {
                                if (DomUtils.nodeNameEquals(node, "arg"))
                                        argsElems.add((Element) node);
-                               else
+                               else if(!DomUtils.nodeNameEquals(node, "description"))
                                        execElems.add((Element) node);
                        }
                }
@@ -73,21 +77,10 @@ public class FlowBeanDefinitionParser extends
                // Executables
                if (execElems.size() != 0) {
                        ManagedList executables = new ManagedList(execElems.size());
-                       for (int i = 0; i < execElems.size(); i++) {
-                               Element child = execElems.get(i);
-                               String name = child.getLocalName();
-                               if (DomUtils.nodeNameEquals(child, "bean")
-                                               || DomUtils.nodeNameEquals(child, "ref")) {
-                                       // Object target = parseBeanReference((Element) child,
-                                       // parserContext, builder);
-                                       executables.add(NamespaceUtils.parseBeanOrReference(child,
-                                                       parserContext, builder.getBeanDefinition()));
-                               } else if (DomUtils.nodeNameEquals(child, "flow")) {
-                                       throw new SlcException(
-                                                       "Nested flows are not yet supported, use a standard ref to another flow.");
-                               } else {
-                                       throw new SlcException("Unsupported child '" + name + "'");
-                               }
+                       for(Element child : execElems) {
+                               // child validity check is performed in xsd  
+                               executables.add(NamespaceUtils.parseBeanOrReference(child,
+                                               parserContext, builder.getBeanDefinition()));                           
                        }
                        builder.addPropertyValue("executables", executables);
                }
index db648401101d0feec822fb76ba824b04f26e1e84..f092bb1ac14649647d6ff7d47c3c26e317d535a9 100644 (file)
@@ -10,6 +10,7 @@ public class FlowNamespaceHandler extends NamespaceHandlerSupport {
                registerBeanDefinitionDecoratorForAttribute("as-flow",
                                new AsFlowDecorator());
                registerBeanDefinitionParser("param", new ParamDecorator());
+//             registerBeanDefinitionDecoratorForAttribute("var", new ExecutionScopeDecorator());
        }
 
 }
index 2e970e096b2d11201c71fe529586ee5f0730be47..e5a4531ab920a3a8c48bf5bbb733098b1b518108 100644 (file)
@@ -26,6 +26,9 @@ public class SpecBeanDefinitionParser extends
        @Override
        protected void doParse(Element element, ParserContext parserContext,
                        BeanDefinitionBuilder builder) {
+               builder.getBeanDefinition().setDescription(DomUtils.getChildElementValueByTagName(element, 
+               "description"));                
+               
                ManagedMap attributes = new ManagedMap();
 
                // Primitives
index 439528e0ff3a8505009a3031e5ebb938296e39b7..884faaa2125de08634a3a7c879fa04eb12ac9d84 100644 (file)
@@ -26,6 +26,7 @@
                        <xsd:complexContent>
                                <xsd:extension base="beans:identifiedType">
                                        <xsd:sequence>
+                                               <xsd:element ref="beans:description" minOccurs="0"/>
                                                <xsd:sequence>
                                                        <xsd:element name="arg" minOccurs="0" maxOccurs="unbounded"
                                                                type="flow:argType">
@@ -41,6 +42,7 @@
                                                        <xsd:choice minOccurs="0" maxOccurs="unbounded">
                                                                <xsd:element ref="beans:bean" />
                                                                <xsd:element ref="beans:ref" />
+                                                               <xsd:element ref="flow:flow" />
                                                        </xsd:choice>
                                                        <!--
                                                                <xsd:any namespace="##other" processContents="strict"
@@ -94,6 +96,7 @@
                        <xsd:complexContent>
                                <xsd:extension base="beans:identifiedType">
                                        <xsd:sequence>
+                                               <xsd:element ref="beans:description" minOccurs="0"/>
                                                <xsd:choice minOccurs="0" maxOccurs="unbounded">
                                                        <xsd:element name="primitive" type="flow:primitiveSpecAttributeType"
                                                                minOccurs="0" maxOccurs="unbounded">
                <xsd:choice minOccurs="0" maxOccurs="1">
                        <xsd:element ref="beans:bean" />
                        <xsd:element ref="beans:ref" />
+                       <xsd:element ref="flow:param" />
                </xsd:choice>
                <xsd:attribute name="name" use="required" type="xsd:string">
                        <xsd:annotation>
                        ]]></xsd:documentation>
                </xsd:annotation>
        </xsd:attribute>
-
+<!--
+       <xsd:attribute name="var" type="xsd:boolean">
+               <xsd:annotation>
+                       <xsd:documentation><![CDATA[
+       If true, the decorated bean is set to scope execution and proxied.
+                       ]]></xsd:documentation>
+               </xsd:annotation>
+       </xsd:attribute>
+-->
        <xsd:element name="param">
                <xsd:annotation>
                        <xsd:documentation><![CDATA[
                </xsd:complexType>
        </xsd:element>
 
+       <!-- for description tags, beans or current namespace are valid -->
+       <xsd:element name="description" substitutionGroup="beans:description"/>
+
 </xsd:schema>
\ No newline at end of file
index 119a1e5b03170851a35d3d0fd6cadd5dc23110ad..b9f035db1bed9651ad30d389af23f03d91594d6e 100644 (file)
@@ -10,4 +10,11 @@ public class FlowNamespaceTest extends AbstractExecutionFlowTestCase {
                ((ExecutionFlow) applicationContext.getBean("canonic-ns.001")).run();
                ((ExecutionFlow) applicationContext.getBean("canonic-ns.002")).run();
        }
+       
+       public void testAdvanced() throws Exception {
+               ConfigurableApplicationContext applicationContext = createApplicationContext("advanced.xml");
+               ((ExecutionFlow) applicationContext.getBean("flow1")).run();
+               ((ExecutionFlow) applicationContext.getBean("flow2")).run();
+               ((ExecutionFlow) applicationContext.getBean("flow3")).run();
+       }       
 }
diff --git a/runtime/org.argeo.slc.core/src/test/resources/org/argeo/slc/core/execution/xml/advanced.xml b/runtime/org.argeo.slc.core/src/test/resources/org/argeo/slc/core/execution/xml/advanced.xml
new file mode 100644 (file)
index 0000000..4745a10
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"\r
+       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:flow="http://www.argeo.org/schema/slc-flow"\r
+       xsi:schemaLocation="\r
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
+       http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd\r
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd\r
+       http://www.argeo.org/schema/slc-flow http://www.argeo.org/schema/slc-flow-0.12.xsd">\r
+\r
+       <import resource="classpath:org/argeo/slc/core/execution/spring.xml" />\r
+       <import resource="classpath:/org/argeo/slc/core/test/spring.xml" />\r
+\r
+       <flow:spec id="advanced.spec">\r
+               <flow:description>spec description</flow:description>\r
+               <flow:primitive name="param1" isParameter="true"\r
+                       type="integer" />\r
+               <flow:ref name="param2" targetClass="org.argeo.slc.core.test.BasicTestData"\r
+                       isParameter="true">\r
+                       <flow:value>\r
+                               <bean class="org.argeo.slc.core.test.BasicTestData">\r
+                                       <property name="expected" value="tata" />\r
+                                       <property name="reached" value="tata" />\r
+                               </bean>                         \r
+                       </flow:value>\r
+               </flow:ref>     \r
+       </flow:spec>\r
+\r
+\r
+       <flow:flow id="advanced.flowTemplate" abstract="true" spec="advanced.spec"\r
+               path="/path">\r
+               <description>flow description</description>\r
+               <bean parent="task.echo"\r
+                       scope="execution">\r
+                       <property name="message" value="Advanced: param1=@{param1}" />\r
+                       <aop:scoped-proxy/>\r
+               </bean> \r
+               <flow:flow>\r
+                       <bean parent="task.echo" p:message="Flow in Flow - param1=@{param1}" />\r
+               </flow:flow>\r
\r
+               <bean class="org.argeo.slc.core.test.SimpleTestRun">\r
+                       <property name="testDefinition">\r
+                               <bean class="org.argeo.slc.core.test.BasicTestDefinition" />\r
+                       </property>\r
+                       <property name="testData">\r
+                               <flow:param name="param2" />\r
+                       </property>\r
+\r
+                       <property name="testResult">\r
+                               <bean parent="slcDefault.test.basicTreeTestResult" scope="execution">\r
+                                       <property name="attributes">\r
+                                               <map>\r
+                                                       <entry key="param1" value="@{param1}" />\r
+                                               </map>\r
+                                       </property>\r
+                                       <aop:scoped-proxy />\r
+                               </bean>\r
+                       </property>\r
+               </bean>\r
\r
+       </flow:flow>\r
+\r
+       <flow:flow id="advanced.flowTemplate2" abstract="true" spec="advanced.spec"\r
+               path="/path">\r
+               <bean parent="task.echo"\r
+                       scope="execution">\r
+                       <property name="message" value="Advanced2: param1=@{param1}" />\r
+                       <aop:scoped-proxy/>\r
+               </bean>\r
+               <flow:flow parent="advanced.flowTemplate">\r
+                       <flow:arg name="param1" value="@{param1}"/>\r
+                       <flow:arg name="param2">\r
+                               <!-- flow:param in flow:arg -->\r
+                               <flow:param name="param2" />\r
+                       </flow:arg>\r
+               </flow:flow>\r
+       </flow:flow>\r
+\r
+       \r
+       <flow:flow id="flow1" parent="advanced.flowTemplate">\r
+               <flow:arg name="param1" value="1" />\r
+       </flow:flow>\r
+       \r
+       <flow:flow id="flow2">\r
+               <flow:flow parent="advanced.flowTemplate">\r
+                       <flow:arg name="param1" value="2" />\r
+               </flow:flow>\r
+       </flow:flow>\r
+\r
+       <flow:flow id="flow3" parent="advanced.flowTemplate2">\r
+               <flow:arg name="param1" value="3" />\r
+               <flow:arg name="param2">\r
+                       <bean class="org.argeo.slc.core.test.BasicTestData">\r
+                               <property name="expected" value="tata" />\r
+                               <property name="reached" value="toto" />\r
+                       </bean>         \r
+               </flow:arg>\r
+       </flow:flow>\r
+\r
+</beans>
\ No newline at end of file