<artifactId>org.argeo.slc.support.simple</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>com.springsource.org.aspectj.runtime</artifactId>
+ <version>1.6.2.RELEASE</version>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>com.springsource.org.aspectj.weaver</artifactId>
+ <version>1.6.2.RELEASE</version>
+ </dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>com.springsource.org.codehaus.groovy</artifactId>
--- /dev/null
+package org.argeo.slc.executionflow;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.After;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+
+@Aspect
+public class ExecutionAspect {
+ private static Log log = LogFactory.getLog(ExecutionAspect.class);
+
+ // @Around("execution(void org.argeo.slc.executionflow.ExecutionFlow.execute()) && target(org.argeo.slc.executionflow.ExecutionFlow)")
+ public void registerFlow(ProceedingJoinPoint pjp) throws Throwable {
+ try {
+ log.debug("registerFlow " + pjp.getTarget().getClass());
+ ExecutionContext.enterFlow((ExecutionFlow) pjp.getTarget());
+ pjp.proceed();
+ } finally {
+ ExecutionContext.leaveFlow((ExecutionFlow) pjp.getTarget());
+ }
+ }
+
+ @Before("flowExecution()")
+ public void beforeFlow(JoinPoint jp) throws Throwable {
+ //log.debug("this " + jp.getThis().getClass());
+ //log.debug("target " + jp.getTarget().getClass());
+ // Thread.dumpStack();
+ ExecutionFlow executionFlow = (ExecutionFlow) jp.getTarget();
+ ExecutionContext.enterFlow(executionFlow);
+ }
+
+ @After("flowExecution()")
+ public void afterFlow(JoinPoint jp) throws Throwable {
+ //log.debug("this " + jp.getThis().getClass());
+ //log.debug("target " + jp.getTarget().getClass());
+ ExecutionFlow executionFlow = (ExecutionFlow) jp.getTarget();
+ ExecutionContext.leaveFlow(executionFlow);
+ }
+
+ @Pointcut("execution(void org.argeo.slc.executionflow.ExecutionFlow.execute())")
+ public void flowExecution() {
+ }
+}
--- /dev/null
+package org.argeo.slc.executionflow;
+
+import java.util.Stack;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+
+public class ExecutionContext {
+ private final static Log log = LogFactory.getLog(ExecutionContext.class);
+
+ private final static ThreadLocal<ExecutionContext> executionContext = new ThreadLocal<ExecutionContext>();
+
+ private final Stack<ExecutionFlow> stack = new Stack<ExecutionFlow>();
+
+ public static ExecutionFlow getCurrentFlow() {
+ if (executionContext.get() == null)
+ return null;
+ return executionContext.get().stack.peek();
+ }
+
+ public static void enterFlow(ExecutionFlow executionFlow) {
+ if (executionContext.get() == null) {
+ // TODO: deal with parallell flows
+ executionContext.set(new ExecutionContext());
+ }
+ Stack<ExecutionFlow> stack = executionContext.get().stack;
+ stack.push(executionFlow);
+ if (log.isDebugEnabled())
+ log.debug("Depth: " + stack.size() + ". Enter " + executionFlow);
+ }
+
+ public static void leaveFlow(ExecutionFlow executionFlow) {
+ Stack<ExecutionFlow> stack = executionContext.get().stack;
+ if (log.isDebugEnabled())
+ log.debug("Depth: " + stack.size() + ". Leave " + executionFlow);
+ ExecutionFlow leftEf = stack.pop();
+ leftEf.getScopedObjects().clear();
+ if (!leftEf.getUuid().equals(executionFlow.getUuid())) {
+ throw new SlcException("Asked to leave " + executionFlow
+ + " but last is " + leftEf);
+ }
+ }
+}
public interface ExecutionFlow extends Executable{
public Map<String, Object> getAttributes();
+ public ExecutionSpec getExecutionSpec();
public String getUuid();
+ public Map<String, Object> getScopedObjects();
}
--- /dev/null
+package org.argeo.slc.executionflow;
+
+import java.beans.PropertyDescriptor;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
+import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
+import org.springframework.beans.factory.config.TypedStringValue;
+
+public class ExecutionParameterPostProcessor extends
+ InstantiationAwareBeanPostProcessorAdapter {
+ private final static Log log = LogFactory
+ .getLog(ExecutionParameterPostProcessor.class);
+
+ private String placeholderPrefix = "@{";
+ private String placeholderSuffix = "}";
+ private String nullValue;
+
+ @Override
+ public PropertyValues postProcessPropertyValues(PropertyValues pvs,
+ PropertyDescriptor[] pds, Object bean, String beanName)
+ throws BeansException {
+ ExecutionFlow currentFlow = ExecutionContext.getCurrentFlow();
+ if (currentFlow == null)
+ return pvs;
+
+ Properties props = new Properties();
+ Map<String, Object> attributes = currentFlow.getAttributes();
+ Map<String, ExecutionSpecAttribute> specAttributes = currentFlow
+ .getExecutionSpec().getAttributes();
+
+ for (String key : specAttributes.keySet()) {
+ ExecutionSpecAttribute obj = specAttributes.get(key);
+ if (!(obj instanceof RefSpecAttribute)) {
+ if (!attributes.containsKey(key))
+ throw new SlcException("Specified attribute " + key
+ + " is not set in " + currentFlow);
+
+ props.setProperty(key, attributes.get(key).toString());
+// if (log.isTraceEnabled())
+// log.trace("Use attribute " + key);
+ }
+ }
+ CustomPpc ppc = new CustomPpc(props);
+
+ for (PropertyValue pv : pvs.getPropertyValues()) {
+ if (pv.getValue() instanceof TypedStringValue) {
+ TypedStringValue tsv = (TypedStringValue) pv.getValue();
+ String originalValue = tsv.getValue();
+ String convertedValue = ppc.process(originalValue);
+ tsv.setValue(convertedValue);
+ if (log.isTraceEnabled()) {
+ if (!originalValue.equals(convertedValue))
+ log.trace("Converted " + beanName + "[" + pv.getName()
+ + "]: " + originalValue + " to "
+ + convertedValue);
+ }
+ } else {
+// if (log.isTraceEnabled())
+// log.trace(beanName + "[" + pv.getName() + "]: "
+// + pv.getValue().getClass());
+ }
+ }
+
+ return pvs;
+ }
+
+ public void setPlaceholderPrefix(String placeholderPrefix) {
+ this.placeholderPrefix = placeholderPrefix;
+ }
+
+ public void setPlaceholderSuffix(String placeholderSuffix) {
+ this.placeholderSuffix = placeholderSuffix;
+ }
+
+ public void setNullValue(String nullValue) {
+ this.nullValue = nullValue;
+ }
+
+ private class CustomPpc extends PropertyPlaceholderConfigurer {
+ private final Properties props;
+
+ public CustomPpc(Properties props) {
+ super();
+ this.props = props;
+ setPlaceholderPrefix(placeholderPrefix);
+ setPlaceholderSuffix(placeholderSuffix);
+ setSystemPropertiesMode(SYSTEM_PROPERTIES_MODE_NEVER);
+ }
+
+ public String process(String strVal) {
+ String value = parseStringValue(strVal, this.props, new HashSet<String>());
+ return (value.equals(nullValue) ? null : value);
+ }
+ }
+}
public class ExecutionScope implements Scope {
private final static Log log = LogFactory.getLog(ExecutionScope.class);
-
+
public Object get(String name, ObjectFactory objectFactory) {
- log.info("Getting bean "+name);
- ExecutionFlow executionFlow = SimpleExecutionFlow.getCurrentExecutionFlow();
- Object obj = executionFlow.getAttributes().get(name);
- log.info("Scoped object "+obj);
+
+ if (log.isTraceEnabled())
+ log.info("Getting scoped bean " + name);
+ ExecutionFlow executionFlow = ExecutionContext.getCurrentFlow();
+ // returns cached instance
+ if (executionFlow.getScopedObjects().containsKey(name)) {
+ Object obj = executionFlow.getScopedObjects().get(name);
+ if (log.isTraceEnabled())
+ log.info("Return cached scoped object " + obj);
+ return obj;
+ }
+ // creates instance
+ Object obj = objectFactory.getObject();
+ if (obj instanceof ExecutionFlow) {
+ // add to itself (it is not yet the current flow)
+ ((ExecutionFlow) obj).getScopedObjects().put(name, obj);
+ if (log.isTraceEnabled())
+ log.info("Cached flow object " + obj + " in itself");
+ } else {
+ executionFlow.getScopedObjects().put(name, obj);
+ if (log.isTraceEnabled())
+ log.info("Created regular scoped object " + obj);
+ }
return obj;
}
public String getConversationId() {
- ExecutionFlow executionFlow = SimpleExecutionFlow.getCurrentExecutionFlow();
+ ExecutionFlow executionFlow = ExecutionContext.getCurrentFlow();
return executionFlow.getUuid();
}
public void registerDestructionCallback(String name, Runnable callback) {
- // TODO Auto-generated method stub
-
+ throw new UnsupportedOperationException();
}
public Object remove(String name) {
- // TODO Auto-generated method stub
- return null;
+ log.debug("Remove object " + name);
+ throw new UnsupportedOperationException();
}
}
public Object getTarget() throws Exception {
if (log.isTraceEnabled())
log.trace("Getting object " + name);
- ExecutionFlow executionFlow = SimpleExecutionFlow
- .getCurrentExecutionFlow();
+ ExecutionFlow executionFlow = ExecutionContext.getCurrentFlow();
Object obj = executionFlow.getAttributes().get(name);
if (log.isTraceEnabled())
log.trace("Target object " + obj);
import org.argeo.slc.SlcException;
import org.argeo.slc.process.Executable;
import org.argeo.slc.test.ExecutableTestRun;
+import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.validation.MapBindingResult;
-public class SimpleExecutionFlow implements ExecutionFlow, InitializingBean {
- private static ThreadLocal<ExecutionFlow> executionFlow = new ThreadLocal<ExecutionFlow>();
+public class SimpleExecutionFlow implements ExecutionFlow, InitializingBean,
+ BeanNameAware {
+ // private static ThreadLocal<ExecutionFlow> executionFlow = new
+ // ThreadLocal<ExecutionFlow>();
- private ExecutionSpec executionSpec;
+ private ExecutionSpec executionSpec = new SimpleExecutionSpec();
+ private String name = null;
private Map<String, Object> attributes = new HashMap<String, Object>();
+ private Map<String, Object> scopedObjects = new HashMap<String, Object>();
private List<Executable> executables = new ArrayList<Executable>();
private final String uuid = UUID.randomUUID().toString();
public void execute() {
try {
- executionFlow.set(this);
+ // ExecutionContext.enterFlow(this);
for (Executable executable : executables) {
executable.execute();
}
} finally {
- executionFlow.set(null);
+ // ExecutionContext.leaveFlow(this);
}
}
+ errors.toString());
}
+ public void setBeanName(String name) {
+ this.name = name;
+ }
+
public void setExecutables(List<Executable> executables) {
this.executables = executables;
}
this.attributes = attributes;
}
- public static ExecutionFlow getCurrentExecutionFlow() {
- return executionFlow.get();
- }
-
public Map<String, Object> getAttributes() {
return attributes;
}
return uuid;
}
+ public Map<String, Object> getScopedObjects() {
+ return scopedObjects;
+ }
+
+ public ExecutionSpec getExecutionSpec() {
+ return executionSpec;
+ }
+
+ public String toString() {
+ return new StringBuffer("Flow ").append(name).append(" [#")
+ .append(uuid).append(']').toString();
+ }
}
<?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"\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/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
\r
<import resource="common.xml" />\r
\r
<bean id="testDef" class="org.argeo.slc.core.test.BasicTestDefinition"\r
scope="prototype" />\r
\r
- <bean\r
- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />\r
-\r
<bean id="testResult" parent="slcDefault.test.basicSimpleTestResult" />\r
\r
<bean id="testRun" class="org.argeo.slc.core.test.SimpleTestRun"\r
<property name="proxyTargetClass" value="true" /> </bean>\r
-->\r
\r
- <!--\r
- <bean\r
- class="org.springframework.beans.factory.config.CustomScopeConfigurer">\r
- <property name="scopes"> <map> <entry key="execution"> <bean\r
- class="org.argeo.slc.executionflow.ExecutionScope" /> </entry> </map>\r
- </property> </bean>\r
- -->\r
-\r
-\r
</beans>
\ No newline at end of file
<?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"\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/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
\r
<import\r
resource="classpath:/org/argeo/slc/core/test/spring/applicationContext.xml" />\r
<?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:lang="http://www.springframework.org/schema/lang"\r
+ xmlns:aop="http://www.springframework.org/schema/aop"\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
- ">\r
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">\r
\r
<import resource="testCases/basic-001.xml" />\r
<import resource="testCases/basic-002.xml" />\r
<bean id="main" class="org.argeo.slc.executionflow.SimpleExecutionFlow">\r
<property name="executables">\r
<list>\r
+ <ref bean="basic.001" />\r
<ref bean="basic.001" />\r
<ref bean="basic.002" />\r
</list>\r
</property>\r
</bean>\r
+\r
+ <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">\r
+ <property name="scopes">\r
+ <map>\r
+ <entry key="execution">\r
+ <bean class="org.argeo.slc.executionflow.ExecutionScope" />\r
+ </entry>\r
+ </map>\r
+ </property>\r
+ </bean>\r
+\r
+ <bean\r
+ class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />\r
+ <bean class="org.argeo.slc.executionflow.ExecutionParameterPostProcessor" />\r
+\r
+ <bean class="org.argeo.slc.executionflow.ExecutionAspect"></bean>\r
+ <aop:aspectj-autoproxy />\r
+\r
</beans>
\ No newline at end of file
## Levels
log4j.logger.org.argeo=DEBUG
+log4j.logger.org.argeo.slc.executionflow.ExecutionParameterPostProcessor=TRACE
## Appenders
# console is set to be a ConsoleAppender.
<?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
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
+ xmlns:aop="http://www.springframework.org/schema/aop"\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
\r
<import resource="../basic.xml" />\r
\r
- <bean id="basic.001" parent="basic.executionFlowTemplate">\r
+ <bean id="basic.001" parent="basic.executionFlowTemplate" scope="execution">\r
+ <aop:scoped-proxy />\r
<property name="attributes">\r
<map>\r
- <entry key="testData1" value-ref="basic.001.testData"/>\r
+ <entry key="testData1" value-ref="basic.001.testData" />\r
<entry key="testData2">\r
<bean class="org.argeo.slc.core.test.BasicTestData">\r
<property name="expected" value="tata" />\r
</property>\r
</bean>\r
\r
- <bean id="basic.001.testData" class="org.argeo.slc.core.test.BasicTestData">\r
- <property name="expected" value="toto" />\r
- <property name="reached" value="toto" />\r
+ <bean id="basic.001.testData" class="org.argeo.slc.core.test.BasicTestData"\r
+ scope="execution">\r
+ <aop:scoped-proxy />\r
+ <property name="expected" value="tata100" />\r
+ <property name="reached" value="tata@{testedComponentId}" />\r
</bean>\r
\r
<bean id="basic.001.testData2" class="org.argeo.slc.core.test.context.DefaultContextTestData">\r