import org.argeo.slc.structure.StructureRegistry;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
-import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
import org.springframework.validation.MapBindingResult;
public class DefaultExecutionFlow implements ExecutionFlow, InitializingBean,
- BeanNameAware, StructureAware<TreeSPath>, ResourceLoaderAware {
+ BeanNameAware, StructureAware<TreeSPath> {
private final ExecutionSpec executionSpec;
private String name = null;
private String path;
private StructureRegistry<TreeSPath> registry = new TreeSRegistry();
- private ResourceLoader resourceLoader = null;
-
public DefaultExecutionFlow() {
this.executionSpec = new DefaultExecutionSpec();
}
}
}
+ @SuppressWarnings(value = { "unchecked" })
public void afterPropertiesSet() throws Exception {
if (path != null) {
for (Runnable executable : executables) {
}
}
- public void setResourceLoader(ResourceLoader resourceLoader) {
- this.resourceLoader = resourceLoader;
- }
-
}
--- /dev/null
+package org.argeo.slc.core.execution;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+import java.util.UUID;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.execution.ExecutionFlow;
+import org.argeo.slc.execution.ExecutionSpecAttribute;
+import org.argeo.slc.execution.ExecutionStack;
+
+public class DefaultExecutionStack implements ExecutionStack {
+
+ private final static Log log = LogFactory
+ .getLog(DefaultExecutionStack.class);
+
+ private final Stack<ExecutionFlowRuntime> stack = new Stack<ExecutionFlowRuntime>();
+
+ public synchronized void enterFlow(ExecutionFlow executionFlow) {
+ ExecutionFlowRuntime runtime = new ExecutionFlowRuntime(executionFlow);
+ stack.push(runtime);
+
+ Map<String, ExecutionSpecAttribute> specAttrs = executionFlow
+ .getExecutionSpec().getAttributes();
+ for (String key : specAttrs.keySet()) {
+ if (executionFlow.isSetAsParameter(key)) {
+ runtime.getLocalVariables().put(key,
+ executionFlow.getParameter(key));
+ }
+ }
+ }
+
+ public synchronized String getCurrentStackLevelUuid() {
+ return stack.peek().getUuid();
+ }
+
+ public synchronized Integer getStackSize() {
+ return stack.size();
+ }
+
+ /**
+ * Looks for a set variable in the stack, starting at the upper flows
+ *
+ * @return the variable or <code>null</code> if not found
+ */
+ public synchronized Object findLocalVariable(String key) {
+ Object obj = null;
+ for (int i = 0; i < stack.size(); i++) {
+ if (stack.get(i).getLocalVariables().containsKey(key)) {
+ obj = stack.get(i).getLocalVariables().get(key);
+ break;
+ }
+ }
+ return obj;
+ }
+
+ public synchronized void leaveFlow(ExecutionFlow executionFlow) {
+ ExecutionFlowRuntime leftEf = stack.pop();
+
+ if (!leftEf.getExecutionFlow().getName()
+ .equals(executionFlow.getName()))
+ throw new SlcException("Asked to leave " + executionFlow
+ + " but last is " + leftEf);
+
+ leftEf.getScopedObjects().clear();
+ leftEf.getLocalVariables().clear();
+ }
+
+ public synchronized void addScopedObject(String name, Object obj) {
+ ExecutionFlowRuntime runtime = stack.peek();
+ // TODO: check that the object is not set yet ?
+ if (log.isDebugEnabled()) {
+ Object existing = findScopedObject(name);
+ if (existing != null)
+ log.warn("Scoped object " + name + " of type " + obj.getClass()
+ + " already registered in " + runtime);
+ }
+ runtime.getScopedObjects().put(name, obj);
+ }
+
+ /** @return </code>null<code> if not found */
+ public synchronized Object findScopedObject(String name) {
+ Object obj = null;
+ for (int i = stack.size() - 1; i >= 0; i--) {
+ if (stack.get(i).getScopedObjects().containsKey(name)) {
+ obj = stack.get(i).getScopedObjects().get(name);
+ break;
+ }
+ }
+ return obj;
+ }
+
+ protected static class ExecutionFlowRuntime {
+ private final ExecutionFlow executionFlow;
+ private final Map<String, Object> scopedObjects = new HashMap<String, Object>();
+ private final Map<String, Object> localVariables = new HashMap<String, Object>();
+ private final String uuid = UUID.randomUUID().toString();
+
+ public ExecutionFlowRuntime(ExecutionFlow executionFlow) {
+ this.executionFlow = executionFlow;
+ }
+
+ public ExecutionFlow getExecutionFlow() {
+ return executionFlow;
+ }
+
+ public Map<String, Object> getScopedObjects() {
+ return scopedObjects;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public Map<String, Object> getLocalVariables() {
+ return localVariables;
+ }
+
+ @Override
+ public String toString() {
+ return "Stack Level #" + uuid;
+ }
+
+ }
+}
package org.argeo.slc.core.execution;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.argeo.slc.execution.ExecutionContext;
import org.argeo.slc.execution.ExecutionFlow;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.annotation.After;
+import org.argeo.slc.execution.ExecutionStack;
+import org.aspectj.lang.ProceedingJoinPoint;
+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 {
- static ThreadLocal<Boolean> inModuleExecution = new ThreadLocal<Boolean>() {
- protected Boolean initialValue() {
- return false;
- }
- };
+ private final static Log log = LogFactory.getLog(ExecutionAspect.class);
+ private ExecutionStack executionStack;
private ExecutionContext executionContext;
- public ExecutionContext getExecutionContext() {
- return executionContext;
+ @Around("flowExecution()")
+ public void aroundFlow(ProceedingJoinPoint pjp) throws Throwable {
+ // IMPORTANT: Make sure that the execution context is called before the execution stack
+ executionContext.getUuid();
+
+ ExecutionFlow executionFlow = (ExecutionFlow) pjp.getTarget();
+ executionStack.enterFlow(executionFlow);
+ executionContext.setVariable(ExecutionContext.VAR_FLOW_ID,
+ executionStack.getCurrentStackLevelUuid());
+ executionContext.setVariable(ExecutionContext.VAR_FLOW_NAME,
+ executionFlow.getName());
+
+ if (log.isDebugEnabled())
+ logStackEvent("=> ", executionFlow);
+ // Actually execute the flow
+ pjp.proceed();
+ if (log.isDebugEnabled())
+ logStackEvent("<= ", executionFlow);
+
+ executionStack.leaveFlow(executionFlow);
}
- public void setExecutionContext(ExecutionContext executionContext) {
- this.executionContext = executionContext;
+ @Around("getVariable()")
+ public Object aroundGetVariable(ProceedingJoinPoint pjp) throws Throwable {
+ Object obj = pjp.proceed();
+ // if the variable was not found, look in the stack starting at the
+ // upper flows
+ if (obj == null) {
+ String key = pjp.getArgs()[0].toString();
+ obj = executionStack.findLocalVariable(key);
+ }
+ return obj;
}
- @Before("flowExecution()")
- public void beforeFlow(JoinPoint jp) throws Throwable {
- ExecutionFlow executionFlow = (ExecutionFlow) jp.getTarget();
- executionContext.enterFlow(executionFlow);
+ @Pointcut("execution(void org.argeo.slc.execution.ExecutionFlow.run())")
+ public void flowExecution() {
}
- @After("flowExecution()")
- public void afterFlow(JoinPoint jp) throws Throwable {
- ExecutionFlow executionFlow = (ExecutionFlow) jp.getTarget();
- executionContext.leaveFlow(executionFlow);
+ @Pointcut("execution(* org.argeo.slc.execution.ExecutionContext.getVariable(..))")
+ public void getVariable() {
}
- @Before("moduleExecution()")
- public void beforeModuleExecution(JoinPoint jp) throws Throwable {
- inModuleExecution.set(true);
+ public void setExecutionStack(ExecutionStack executionStack) {
+ this.executionStack = executionStack;
}
- @After("moduleExecution()")
- public void afterModuleExecution(JoinPoint jp) throws Throwable {
- inModuleExecution.set(false);
+ public void setExecutionContext(ExecutionContext executionContext) {
+ this.executionContext = executionContext;
}
- @Pointcut("execution(void org.argeo.slc.execution.ExecutionFlow.run())")
- public void flowExecution() {
+ protected void logStackEvent(String symbol, ExecutionFlow executionFlow) {
+ Integer stackSize = executionStack.getStackSize();
+ log.debug(depthSpaces(stackSize) + symbol + executionFlow + " #"
+ + executionStack.getCurrentStackLevelUuid() + ", depth="
+ + stackSize);
}
- @Pointcut("execution(void org.argeo.slc.execution.ExecutionModule.execute(..))")
- public void moduleExecution() {
+ private String depthSpaces(int depth) {
+ StringBuffer buf = new StringBuffer(depth * 2);
+ for (int i = 0; i < depth; i++)
+ buf.append(" ");
+ return buf.toString();
}
}
+++ /dev/null
-package org.argeo.slc.core.execution;
-
-import org.argeo.slc.execution.ExecutionContext;
-import org.springframework.context.ApplicationEvent;
-
-public class ExecutionFinishedEvent extends ApplicationEvent {
- static final long serialVersionUID = 012;
-
- private final ExecutionContext executionContext;
-
- public ExecutionFinishedEvent(Object source, ExecutionContext executionContext) {
- super(source);
- this.executionContext = executionContext;
- }
-
- public ExecutionContext getExecutionContext() {
- return executionContext;
- }
-
-}
import java.util.Iterator;\r
import java.util.List;\r
import java.util.Map;\r
-import java.util.Properties;\r
import java.util.Set;\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.execution.ExecutionContext;\r
+import org.argeo.slc.execution.ExecutionFlow;\r
import org.springframework.beans.BeansException;\r
import org.springframework.beans.MutablePropertyValues;\r
import org.springframework.beans.PropertyValue;\r
import org.springframework.beans.PropertyValues;\r
+import org.springframework.beans.factory.BeanDefinitionStoreException;\r
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;\r
-import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;\r
import org.springframework.beans.factory.config.TypedStringValue;\r
import org.springframework.beans.factory.support.ManagedList;\r
import org.springframework.beans.factory.support.ManagedMap;\r
import org.springframework.beans.factory.support.ManagedSet;\r
import org.springframework.util.ObjectUtils;\r
+import org.springframework.util.StringUtils;\r
\r
public class ExecutionParameterPostProcessor extends\r
InstantiationAwareBeanPostProcessorAdapter {\r
\r
private final static Log log = LogFactory\r
.getLog(ExecutionParameterPostProcessor.class);\r
- \r
- private ExecutionContext executionContext;\r
-\r
- private ExecutionScope executionScope;\r
\r
+ private ExecutionContext executionContext;\r
private InstantiationManager instantiationManager;\r
- \r
- public InstantiationManager getInstantiationManager() {\r
- return instantiationManager;\r
- }\r
-\r
- public void setInstantiationManager(\r
- InstantiationManager instantiationManager) {\r
- this.instantiationManager = instantiationManager;\r
- }\r
-\r
- public void setExecutionScope(ExecutionScope executionScope) {\r
- this.executionScope = executionScope;\r
- }\r
-\r
- public ExecutionContext getExecutionContext() {\r
- return executionContext;\r
- }\r
-\r
- public void setExecutionContext(ExecutionContext executionContext) {\r
- this.executionContext = executionContext;\r
- }\r
\r
private String placeholderPrefix = "@{";\r
private String placeholderSuffix = "}";\r
PropertyDescriptor[] pds, Object bean, String beanName)\r
throws BeansException {\r
\r
- //TODO: resolve at execution only if scope is execution\r
- //TODO: deal with placeholders in RuntimeBeanReference and RuntimeBeanNameReference\r
- \r
+ // TODO: resolve at execution only if scope is execution\r
+ // TODO: deal with placeholders in RuntimeBeanReference and\r
+ // RuntimeBeanNameReference\r
+\r
MutablePropertyValues newPvs = new MutablePropertyValues();\r
- \r
+\r
boolean changesOccured = false;\r
- \r
- CustomPpc ppc = new CustomPpc(beanName);\r
- \r
- for(PropertyValue pv : pvs.getPropertyValues()) {\r
- Object convertedValue = ppc.resolveValue(pv.getValue());\r
+\r
+ for (PropertyValue pv : pvs.getPropertyValues()) {\r
+ Object convertedValue = resolveValue(beanName, bean, pv.getValue());\r
newPvs.addPropertyValue(new PropertyValue(pv, convertedValue));\r
- if(convertedValue != pv.getValue()) {\r
+ if (convertedValue != pv.getValue()) {\r
changesOccured = true;\r
}\r
}\r
- \r
+\r
return changesOccured ? newPvs : pvs;\r
}\r
\r
+ @Override\r
+ public boolean postProcessAfterInstantiation(Object bean, String beanName)\r
+ throws BeansException {\r
+ if (bean instanceof ExecutionFlow)\r
+ instantiationManager.flowInitializationStarted(\r
+ (ExecutionFlow) bean, beanName);\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public Object postProcessAfterInitialization(Object bean, String beanName)\r
+ throws BeansException {\r
+ if (bean instanceof ExecutionFlow)\r
+ instantiationManager.flowInitializationFinished(\r
+ (ExecutionFlow) bean, beanName);\r
+ return bean;\r
+ }\r
+\r
+ protected String resolvePlaceholder(Object bean, String placeholder) {\r
+ if (instantiationManager.isInFlowInitialization())\r
+ return instantiationManager.getInitializingFlowParameter(\r
+ placeholder).toString();\r
+\r
+ else {// execution\r
+ log.debug("Bean class: " + bean.getClass());\r
+ // next call fail if no execution context available\r
+ Object obj = executionContext.getVariable(placeholder);\r
+ if (obj != null) {\r
+ return obj.toString();\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ @SuppressWarnings(value = { "unchecked" })\r
+ public Object resolveValue(String beanName, Object bean, Object value) {\r
+ if (value instanceof TypedStringValue) {\r
+ TypedStringValue tsv = (TypedStringValue) value;\r
+ String originalValue = tsv.getValue();\r
+\r
+ String convertedValue = resolveString(beanName, bean, originalValue);\r
+ return convertedValue.equals(originalValue) ? value\r
+ : new TypedStringValue(convertedValue);\r
+ } else if (value instanceof String) {\r
+ String originalValue = value.toString();\r
+ String convertedValue = resolveString(beanName, bean, originalValue);\r
+ return convertedValue.equals(originalValue) ? value\r
+ : convertedValue;\r
+ } else if (value instanceof ManagedMap) {\r
+ Map mapVal = (Map) value;\r
+\r
+ Map newContent = new ManagedMap();\r
+ boolean entriesModified = false;\r
+ for (Iterator it = mapVal.entrySet().iterator(); it.hasNext();) {\r
+ Map.Entry entry = (Map.Entry) it.next();\r
+ Object key = entry.getKey();\r
+ int keyHash = (key != null ? key.hashCode() : 0);\r
+ Object newKey = resolveValue(beanName, bean, key);\r
+ int newKeyHash = (newKey != null ? newKey.hashCode() : 0);\r
+ Object val = entry.getValue();\r
+ Object newVal = resolveValue(beanName, bean, val);\r
+ newContent.put(newKey, newVal);\r
+ entriesModified = entriesModified\r
+ || (newVal != val || newKey != key || newKeyHash != keyHash);\r
+ }\r
+\r
+ return entriesModified ? newContent : value;\r
+ } else if (value instanceof ManagedList) {\r
+ List listVal = (List) value;\r
+ List newContent = new ManagedList();\r
+ boolean valueModified = false;\r
+\r
+ for (int i = 0; i < listVal.size(); i++) {\r
+ Object elem = listVal.get(i);\r
+ Object newVal = resolveValue(beanName, bean, elem);\r
+ newContent.add(newVal);\r
+ if (!ObjectUtils.nullSafeEquals(newVal, elem)) {\r
+ valueModified = true;\r
+ }\r
+ }\r
+ return valueModified ? newContent : value;\r
+ } else if (value instanceof ManagedSet) {\r
+ Set setVal = (Set) value;\r
+ Set newContent = new ManagedSet();\r
+ boolean entriesModified = false;\r
+ for (Iterator it = setVal.iterator(); it.hasNext();) {\r
+ Object elem = it.next();\r
+ int elemHash = (elem != null ? elem.hashCode() : 0);\r
+ Object newVal = resolveValue(beanName, bean, elem);\r
+ int newValHash = (newVal != null ? newVal.hashCode() : 0);\r
+ newContent.add(newVal);\r
+ entriesModified = entriesModified\r
+ || (newVal != elem || newValHash != elemHash);\r
+ }\r
+ return entriesModified ? newContent : value;\r
+ } else {\r
+ //log.debug(beanName + ": " + value.getClass() + " : " + value);\r
+ return value;\r
+ }\r
+\r
+ }\r
+\r
+ private String resolveString(String beanName, Object bean, String strVal) {\r
+ String value = parseStringValue(bean, strVal, new HashSet<String>());\r
+\r
+ if (value == null)\r
+ throw new SlcException("Could not resolve placeholder '" + strVal\r
+ + "' in bean '" + beanName + "'");\r
+\r
+ return (value.equals(nullValue) ? null : value);\r
+ }\r
+\r
public void setPlaceholderPrefix(String placeholderPrefix) {\r
this.placeholderPrefix = placeholderPrefix;\r
}\r
public void setNullValue(String nullValue) {\r
this.nullValue = nullValue;\r
}\r
- \r
- private class CustomPpc extends PropertyPlaceholderConfigurer {\r
- private final Properties props;\r
- String beanName;\r
- \r
- public CustomPpc(String beanName) {\r
- super();\r
- this.props = new Properties();\r
- this.beanName = beanName;\r
- setPlaceholderPrefix(placeholderPrefix);\r
- setPlaceholderSuffix(placeholderSuffix);\r
- setSystemPropertiesMode(SYSTEM_PROPERTIES_MODE_NEVER);\r
- }\r
\r
- /** Public access to the internals of PropertyPlaceholderConfigurer*/\r
- public String resolveString(String strVal) {\r
- String value = parseStringValue(strVal, this.props,\r
- new HashSet<String>());\r
- return (value.equals(nullValue) ? null : value);\r
- }\r
- \r
- public Object resolveValue(Object value) {\r
- if (value instanceof TypedStringValue) {\r
- TypedStringValue tsv = (TypedStringValue) value;\r
- String originalValue = tsv.getValue();\r
-\r
- String convertedValue = resolveString(originalValue);\r
- return convertedValue.equals(originalValue) ? value : new TypedStringValue(convertedValue);\r
- }\r
- else if (value instanceof String) {\r
- String originalValue = value.toString(); \r
- String convertedValue = resolveString(originalValue);\r
- return convertedValue.equals(originalValue) ? value : convertedValue;\r
- } \r
- else if (value instanceof ManagedMap) {\r
- Map mapVal = (Map) value;\r
- \r
- Map newContent = new ManagedMap();\r
- boolean entriesModified = false;\r
- for (Iterator it = mapVal.entrySet().iterator(); it.hasNext();) {\r
- Map.Entry entry = (Map.Entry) it.next();\r
- Object key = entry.getKey();\r
- int keyHash = (key != null ? key.hashCode() : 0);\r
- Object newKey = resolveValue(key);\r
- int newKeyHash = (newKey != null ? newKey.hashCode() : 0);\r
- Object val = entry.getValue();\r
- Object newVal = resolveValue(val);\r
- newContent.put(newKey, newVal);\r
- entriesModified = entriesModified || (newVal != val || newKey != key || newKeyHash != keyHash);\r
+ public void setInstantiationManager(\r
+ InstantiationManager instantiationManager) {\r
+ this.instantiationManager = instantiationManager;\r
+ }\r
+\r
+ public void setExecutionContext(ExecutionContext executionContext) {\r
+ this.executionContext = executionContext;\r
+ }\r
+\r
+ //\r
+ // Following methods hacked from the internals of\r
+ // PropertyPlaceholderConfigurer\r
+ //\r
+\r
+ protected String parseStringValue(Object bean, String strVal,\r
+ Set<String> visitedPlaceholders)\r
+ throws BeanDefinitionStoreException {\r
+\r
+ StringBuffer buf = new StringBuffer(strVal);\r
+\r
+ int startIndex = strVal.indexOf(placeholderPrefix);\r
+ while (startIndex != -1) {\r
+ int endIndex = findPlaceholderEndIndex(buf, startIndex);\r
+ if (endIndex != -1) {\r
+ String placeholder = buf.substring(startIndex\r
+ + placeholderPrefix.length(), endIndex);\r
+ if (!visitedPlaceholders.add(placeholder)) {\r
+ throw new BeanDefinitionStoreException(\r
+ "Circular placeholder reference '" + placeholder\r
+ + "' in property definitions");\r
}\r
- \r
- return entriesModified ? newContent : value;\r
- }\r
- else if (value instanceof ManagedList) {\r
- List listVal = (List) value;\r
- List newContent = new ManagedList();\r
- boolean valueModified = false;\r
- \r
- for (int i = 0; i < listVal.size(); i++) {\r
- Object elem = listVal.get(i);\r
- Object newVal = resolveValue(elem);\r
- newContent.add(newVal);\r
- if (!ObjectUtils.nullSafeEquals(newVal, elem)) {\r
- valueModified = true;\r
+ // Recursive invocation, parsing placeholders contained in\r
+ // the placeholder key.\r
+ placeholder = parseStringValue(bean, placeholder,\r
+ visitedPlaceholders);\r
+ // Now obtain the value for the fully resolved key...\r
+ String propVal = resolvePlaceholder(bean, placeholder);\r
+ if (propVal != null) {\r
+ // Recursive invocation, parsing placeholders contained\r
+ // in the\r
+ // previously resolved placeholder value.\r
+ propVal = parseStringValue(bean, propVal,\r
+ visitedPlaceholders);\r
+ buf.replace(startIndex, endIndex\r
+ + placeholderSuffix.length(), propVal);\r
+ if (log.isTraceEnabled()) {\r
+ log.trace("Resolved placeholder '" + placeholder + "'");\r
}\r
- } \r
- return valueModified ? newContent : value;\r
- }\r
- else if (value instanceof ManagedSet) {\r
- Set setVal = (Set) value;\r
- Set newContent = new ManagedSet();\r
- boolean entriesModified = false;\r
- for (Iterator it = setVal.iterator(); it.hasNext();) {\r
- Object elem = it.next();\r
- int elemHash = (elem != null ? elem.hashCode() : 0);\r
- Object newVal = resolveValue(elem);\r
- int newValHash = (newVal != null ? newVal.hashCode() : 0);\r
- newContent.add(newVal);\r
- entriesModified = entriesModified || (newVal != elem || newValHash != elemHash);\r
- } \r
- return entriesModified ? newContent : value;\r
+ startIndex = buf.indexOf(placeholderPrefix, startIndex\r
+ + propVal.length());\r
+ } else {\r
+ throw new BeanDefinitionStoreException(\r
+ "Could not resolve placeholder '" + placeholder\r
+ + "'");\r
+ }\r
+ visitedPlaceholders.remove(placeholder);\r
+ } else {\r
+ startIndex = -1;\r
}\r
- else {\r
- return value;\r
- } \r
}\r
\r
- @Override\r
- protected String resolvePlaceholder(String placeholder, Properties props) { \r
- if ((executionScope != null)\r
- && (executionScope.hasExecutionContext())) {\r
- Object obj = executionContext.findVariable(placeholder);\r
- if(obj != null) {\r
- return obj.toString();\r
+ return buf.toString();\r
+ }\r
+\r
+ private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {\r
+ int index = startIndex + placeholderPrefix.length();\r
+ int withinNestedPlaceholder = 0;\r
+ while (index < buf.length()) {\r
+ if (StringUtils.substringMatch(buf, index, placeholderSuffix)) {\r
+ if (withinNestedPlaceholder > 0) {\r
+ withinNestedPlaceholder--;\r
+ index = index + placeholderSuffix.length();\r
+ } else {\r
+ return index;\r
}\r
+ } else if (StringUtils\r
+ .substringMatch(buf, index, placeholderPrefix)) {\r
+ withinNestedPlaceholder++;\r
+ index = index + placeholderPrefix.length();\r
+ } else {\r
+ index++;\r
}\r
- if (instantiationManager.isInFlowInitialization())\r
- return instantiationManager.getInitializingFlowParameter(\r
- placeholder).toString();\r
- else\r
- throw new SlcException("Could not resolve placeholder '" \r
- + placeholder + "' in bean '" + beanName + "'");\r
}\r
+ return -1;\r
}\r
+\r
}\r
package org.argeo.slc.core.execution;
-import java.util.HashMap;
-import java.util.Map;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.argeo.slc.SlcException;
+import org.argeo.slc.UnsupportedException;
import org.argeo.slc.execution.ExecutionContext;
+import org.argeo.slc.execution.ExecutionFlow;
+import org.argeo.slc.execution.ExecutionSpec;
+import org.argeo.slc.execution.ExecutionStack;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class ExecutionScope implements Scope {
private final static Log log = LogFactory.getLog(ExecutionScope.class);
- private final ThreadLocal<ExecutionContext> executionContext = new ThreadLocal<ExecutionContext>();
+ private final ThreadLocal<ExecutionStack> executionStack = new ThreadLocal<ExecutionStack>();
+ public final ThreadLocal<String> executionStackBeanName = new ThreadLocal<String>();
- public final ThreadLocal<String> executionContextBeanName = new ThreadLocal<String>();
+ private final ThreadLocal<ExecutionContext> executionContext = new ThreadLocal<ExecutionContext>();
+ private final ThreadLocal<String> executionContextBeanName = new ThreadLocal<String>();
public Object get(String name, ObjectFactory objectFactory) {
-
if (log.isTraceEnabled())
- log.trace("Getting scoped bean " + name);
+ log.debug("Get execution scoped bean " + name);
+
+ // shortcuts
+ if (executionStackBeanName.get() != null
+ && name.equals(executionStackBeanName.get())) {
+ return executionStack.get();
+ }
+
+ if (executionContextBeanName.get() != null
+ && name.equals(executionContextBeanName.get())) {
+ return executionContext.get();
+ }
- // check if an execution context is defined for this thread
+ // execution context must be defined first
if (executionContext.get() == null) {
- // if not, we expect objectFactory to produce an ExecutionContext
Object obj = objectFactory.getObject();
if (obj instanceof ExecutionContext) {
- // Check whether we are in an execution
- // FIXME: do it more properly (not static)
- // see https://www.argeo.org/bugzilla/show_bug.cgi?id=82
- if (!ExecutionAspect.inModuleExecution.get()) {
- log
- .error("An execution context is being instantiated outside a module execution."
- + " Please check your references to execution contexts."
- + " This may lead to unexpected behaviour and will be rejected in the future.");
- //Thread.dumpStack();
- }
-
- // store the ExecutionContext in the ThreadLocal
- executionContext.set((ExecutionContext) obj);
- executionContextBeanName.set(name);
- if (log.isDebugEnabled()) {
- log.debug("Execution context #"
- + executionContext.get().getUuid()
- + " instantiated. (beanName="
- + executionContextBeanName.get() + ")");
- // Thread.dumpStack();
- }
- return obj;
+ return dealWithSpecialScopedObject(name, executionContext,
+ executionContextBeanName, (ExecutionContext) obj);
} else {
- throw new SlcException(
- "Expected an ExecutionContext, got an object of class "
- + obj.getClass()
- + " for bean "
- + name
- + ": make sure that you have porperly set scope=\"execution\" where required");
+ // TODO: use execution context wrapper
+ throw new SlcException("No execution context has been defined.");
}
}
- if (name.equals(executionContextBeanName.get())) {
- return executionContext.get();
- } else {
- // see if the executionContext already knows the object
- Object obj = executionContext.get().findScopedObject(name);
- if (obj == null) {
- obj = objectFactory.getObject();
- if (!(obj instanceof ExecutionContext)) {
- executionContext.get().addScopedObject(name, obj);
- } else {
- throw new SlcException(
- "Only one ExecutionContext can be defined per Thread");
- }
+ // for other scoped objects, an executions stack must be available
+ if (executionStack.get() == null) {
+ Object obj = objectFactory.getObject();
+ if (obj instanceof ExecutionStack) {
+ return dealWithSpecialScopedObject(name, executionStack,
+ executionStackBeanName, (ExecutionStack) obj);
+ } else {
+ throw new SlcException("No execution stack has been defined.");
}
- return obj;
}
- // if (ExecutionContext.getScopedObjects().containsKey(name)) {
- // // returns cached instance
- // Object obj = ExecutionContext.getScopedObjects().get(name);
- // if (log.isTraceEnabled())
- // log.trace("Return cached scoped object " + obj);
- // return obj;
- // } else {
- // // creates instance
- // Object obj = objectFactory.getObject();
- // ExecutionContext.getScopedObjects().put(name, obj);
- // if (log.isTraceEnabled())
- // log.trace("Created regular scoped object " + obj);
- // return obj;
- // }
+ // see if the execution stack already knows the object
+ Object obj = executionStack.get().findScopedObject(name);
+ if (obj == null) {
+ obj = objectFactory.getObject();
+ if (obj instanceof ExecutionContext)
+ throw new SlcException(
+ "Only one execution context can be defined per thread");
+ if (obj instanceof ExecutionStack)
+ throw new SlcException(
+ "Only one execution stack can be defined per thread");
+
+ checkForbiddenClasses(obj);
+
+ executionStack.get().addScopedObject(name, obj);
+ }
+ return obj;
+
}
- public String getConversationId() {
+ protected <T> T dealWithSpecialScopedObject(String name,
+ ThreadLocal<T> threadLocal,
+ ThreadLocal<String> threadLocalBeanName, T newObj) {
+
+ T obj = threadLocal.get();
+ if (obj == null) {
+ obj = newObj;
+ threadLocal.set(obj);
+ threadLocalBeanName.set(name);
+ if (log.isDebugEnabled()) {
+ log.debug(obj.getClass() + " instantiated. (beanName=" + name
+ + ")");
+ }
+ return obj;
+ } else {
+ throw new SlcException("Only one scoped " + obj.getClass()
+ + " can be defined per thread");
+ }
- return executionContext.get().getUuid();
}
- public Boolean hasExecutionContext() {
- return executionContext.get() != null;
+ protected void checkForbiddenClasses(Object obj) {
+ Class<?> clss = obj.getClass();
+ if (ExecutionFlow.class.isAssignableFrom(clss)
+ || ExecutionSpec.class.isAssignableFrom(clss)) {
+ throw new UnsupportedException("Execution scoped object", clss);
+ }
+ }
+
+ public String getConversationId() {
+ // TODO: is it the most relevant?
+ return executionContext.get().getUuid();
}
public void registerDestructionCallback(String name, Runnable callback) {
}
public Object remove(String name) {
- log.debug("Remove object " + name);
+ if (log.isDebugEnabled())
+ log.debug("Remove object " + name);
throw new UnsupportedOperationException();
}
+++ /dev/null
-package org.argeo.slc.core.execution;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.slc.execution.ExecutionContext;
-import org.argeo.slc.execution.ExecutionFlow;
-import org.argeo.slc.process.SlcExecution;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.context.ApplicationEvent;
-import org.springframework.context.ApplicationListener;
-
-public class Executor implements ApplicationListener, ApplicationContextAware {
- private final static Log log = LogFactory.getLog(Executor.class);
-
- private ApplicationContext applicationContext;
-
- public void onApplicationEvent(ApplicationEvent event) {
- if (event instanceof NewExecutionEvent) {
- SlcExecution slcExecution = ((NewExecutionEvent) event)
- .getSlcExecution();
- MapExecutionContext executionContext = new MapExecutionContext();
- ExecutionThread thread = new ExecutionThread(executionContext,
- slcExecution);
- thread.start();
- }
-
- }
-
- public void setApplicationContext(ApplicationContext applicationContext)
- throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- private class ExecutionThread extends Thread {
- private final SlcExecution slcExecution;
- private final ExecutionContext executionContext;
-
- public ExecutionThread(MapExecutionContext executionContext,
- SlcExecution slcExecution) {
- super("SLC Execution #" + executionContext.getUuid());
- this.slcExecution = slcExecution;
- this.executionContext = executionContext;
- }
-
- public void run() {/*
- // Initialize from SlcExecution
- MapExecutionContext.registerExecutionContext(executionContext);
- MapExecutionContext.getVariables()
- .putAll(slcExecution.getAttributes());
-
- try {
- log.info("Start execution #"
- + MapExecutionContext.getExecutionUuid());
- String executionBean = slcExecution.getAttributes().get(
- "slc.flows");
- ExecutionFlow main = (ExecutionFlow) applicationContext
- .getBean(executionBean);
- main.execute();
- } catch (Exception e) {
- log.error("Execution " + executionContext.getUuid()
- + " failed.", e);
- } finally {
- applicationContext.publishEvent(new ExecutionFinishedEvent(
- this, executionContext));
- }
-*/
- }
- }
-
-}
if (withExecutionSubdirectory) {
Assert.notNull(executionContext, "execution context is null");
- String path = baseDir.getPath() + File.separator
- + sdf().format(executionContext.getCreationDate())
+ String path = baseDir.getPath()
+ + File.separator
+ + sdf()
+ .format(
+ executionContext
+ .getVariable(ExecutionContext.VAR_EXECUTION_CONTEXT_CREATION_DATE))
+ executionContext.getUuid();
File executionDir = new File(path);
return true;
}
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName)
- throws BeansException {
- return bean;
- }
-
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
package org.argeo.slc.core.execution;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
-import java.util.Stack;
import java.util.UUID;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.slc.SlcException;
import org.argeo.slc.execution.ExecutionContext;
-import org.argeo.slc.execution.ExecutionFlow;
-import org.argeo.slc.execution.ExecutionSpecAttribute;
-public class MapExecutionContext implements ExecutionContext {
-
- private final static Log log = LogFactory.getLog(MapExecutionContext.class);
-
- private final Stack<ExecutionFlowRuntime> stack = new Stack<ExecutionFlowRuntime>();
-
- // TODO: make it thread safe?
- private final Map<String, Object> variables = new HashMap<String, Object>();
+public class MapExecutionContext implements
+ ExecutionContext {
+ private final Map<String, Object> variables = Collections
+ .synchronizedMap(new HashMap<String, Object>());
private final String uuid;
- private final Date creationDate = new Date();
-
public MapExecutionContext() {
uuid = UUID.randomUUID().toString();
variables.put(VAR_EXECUTION_CONTEXT_ID, uuid);
+ variables.put(VAR_EXECUTION_CONTEXT_CREATION_DATE, new Date());
}
- public void addVariables(
- Map<? extends String, ? extends Object> variablesToAdd) {
- variables.putAll(variablesToAdd);
- }
-
- public void enterFlow(ExecutionFlow executionFlow) {
- ExecutionFlowRuntime runtime = new ExecutionFlowRuntime(executionFlow);
- stack.push(runtime);
- variables.put(VAR_FLOW_ID, runtime.getUuid());
- variables.put(VAR_FLOW_NAME, runtime.getExecutionFlow().getName());
-
- if (log.isDebugEnabled())
- log.debug(depthSpaces(stack.size()) + "=> " + executionFlow + " #"
- + uuid + ", depth=" + stack.size());
-
- Map<String, ExecutionSpecAttribute> specAttrs = executionFlow
- .getExecutionSpec().getAttributes();
- for (String key : specAttrs.keySet()) {
- // ExecutionSpecAttribute esa = specAttrs.get(key);
- if (executionFlow.isSetAsParameter(key)) {
- runtime.getLocalVariables().put(key,
- executionFlow.getParameter(key));
- if (log.isTraceEnabled())
- log.trace(depthSpaces(stack.size()) + "Add '" + key
- + "' as local variable.");
- }
- }
-
+ public void setVariable(String key, Object value) {
+ variables.put(key, value);
}
public Object getVariable(String key) {
- Object obj = findVariable(key);
- if (obj == null)
- throw new SlcException("Variable '" + key + "' not found.");
- return obj;
- }
-
- public Object findVariable(String key) {
- Object obj = null;
-
- // Look if the variable is set in the global execution variables
- // (i.e. the variable was overridden)
- if (variables.containsKey(key))
- obj = variables.get(key);
-
- // if the variable was not found, look in the stack starting at the
- // upper flows
- if (obj == null) {
- for (int i = 0; i < stack.size(); i++) {
- if (stack.get(i).getLocalVariables().containsKey(key)) {
- obj = stack.get(i).getLocalVariables().get(key);
- break;
- }
- }
- }
-
- return obj;
- }
-
- private static String depthSpaces(int depth) {
- StringBuffer buf = new StringBuffer(depth * 2);
- for (int i = 0; i < depth; i++)
- buf.append(" ");
- return buf.toString();
- }
-
- public void leaveFlow(ExecutionFlow executionFlow) {
- if (log.isDebugEnabled())
- log.debug(depthSpaces(stack.size()) + "<= " + executionFlow + " #"
- + uuid + ", depth=" + stack.size());
-
- ExecutionFlowRuntime leftEf = stack.pop();
- if (!leftEf.getExecutionFlow().getName()
- .equals(executionFlow.getName()))
- throw new SlcException("Asked to leave " + executionFlow
- + " but last is " + leftEf);
-
- leftEf.getScopedObjects().clear();
- leftEf.getLocalVariables().clear();
-
- }
-
- public void addScopedObject(String name, Object obj) {
- // TODO: check that the object is not set yet ?
- stack.peek().getScopedObjects().put(name, obj);
- }
-
- /** return null if not found */
- public Object findScopedObject(String name) {
- Object obj = null;
- for (int i = stack.size() - 1; i >= 0; i--) {
- if (stack.get(i).getScopedObjects().containsKey(name)) {
- obj = stack.get(i).getScopedObjects().get(name);
- break;
- }
- }
- return obj;
+ return variables.get(key);
}
public String getUuid() {
return uuid;
}
-
- public Date getCreationDate() {
- return creationDate;
- }
-
- private static class ExecutionFlowRuntime {
- private final ExecutionFlow executionFlow;
- private final Map<String, Object> scopedObjects = new HashMap<String, Object>();
- private final Map<String, Object> localVariables = new HashMap<String, Object>();
- private final String uuid = UUID.randomUUID().toString();
-
- public ExecutionFlowRuntime(ExecutionFlow executionFlow) {
- this.executionFlow = executionFlow;
- }
-
- public ExecutionFlow getExecutionFlow() {
- return executionFlow;
- }
-
- public Map<String, Object> getScopedObjects() {
- return scopedObjects;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public Map<String, Object> getLocalVariables() {
- return localVariables;
- }
-
- }
}
+++ /dev/null
-package org.argeo.slc.core.execution;
-
-import org.argeo.slc.process.SlcExecution;
-import org.springframework.context.ApplicationEvent;
-
-public class NewExecutionEvent extends ApplicationEvent {
- static final long serialVersionUID = 012;
-
- private final SlcExecution slcExecution;
-
- public NewExecutionEvent(Object source, SlcExecution slcExecution) {
- super(source);
- this.slcExecution = slcExecution;
- }
-
- public SlcExecution getSlcExecution() {
- return slcExecution;
- }
-
-}
scope="execution">
<aop:scoped-proxy />
</bean>
+ <bean id="executionStack" class="org.argeo.slc.core.execution.DefaultExecutionStack"
+ scope="execution">
+ <aop:scoped-proxy />
+ </bean>
<bean id="instantiationManager" class="org.argeo.slc.core.execution.InstantiationManager" />
<bean class="org.argeo.slc.core.execution.ExecutionParameterPostProcessor">
<property name="executionContext" ref="executionContext" />
- <property name="executionScope" ref="executionScope" />
- <property name="instantiationManager" ref="instantiationManager" />
- </bean>
-
- <bean class="org.argeo.slc.core.execution.InstantiationPostProcessor">
<property name="instantiationManager" ref="instantiationManager" />
</bean>
<bean class="org.argeo.slc.core.execution.ExecutionAspect">
+ <property name="executionStack" ref="executionStack" />
<property name="executionContext" ref="executionContext" />
</bean>
executionParameters.put("p6", "e6");\r
executionParameters.put("p7", "e7");\r
executionParameters.put("p8", "e8");\r
- executionContext.addVariables(executionParameters);\r
+ addVariables(executionContext, executionParameters);\r
\r
((ExecutionFlow) applicationContext.getBean("flowA")).run();\r
validateTestResult((SimpleTestResult) applicationContext\r
executionParameters.put("p4", "e4");\r
executionParameters.put("p5", "e5");\r
executionParameters.put("p6", "e6");\r
- executionContext.addVariables(executionParameters);\r
+ addVariables(executionContext, executionParameters);\r
\r
((ExecutionFlow) applicationContext.getBean("flowA")).run();\r
validateTestResult((SimpleTestResult) applicationContext\r
validateTestResult(res, TestStatus.FAILED);\r
applicationContext.close();\r
}\r
+\r
+ protected void addVariables(ExecutionContext executionContext,\r
+ Map<String, String> vars) {\r
+ for (String key : vars.keySet())\r
+ executionContext.setVariable(key, vars.get(key));\r
+ }\r
}\r
package org.argeo.slc.execution;\r
\r
-import java.util.Date;\r
-import java.util.Map;\r
-\r
public interface ExecutionContext {\r
- public final static String VAR_PROCESS_ID = "slcVar.process.id";\r
public final static String VAR_EXECUTION_CONTEXT_ID = "slcVar.executionContext.id";\r
+ public final static String VAR_EXECUTION_CONTEXT_CREATION_DATE = "slcVar.executionContext.creationDate";\r
public final static String VAR_FLOW_ID = "slcVar.flow.id";\r
public final static String VAR_FLOW_NAME = "slcVar.flow.name";\r
\r
- /**\r
- * @param name\r
- * @return null if no object is found\r
- */\r
- public Object findScopedObject(String name);\r
-\r
- public void addScopedObject(String name, Object obj);\r
-\r
public String getUuid();\r
\r
- public void enterFlow(ExecutionFlow executionFlow);\r
-\r
- public void leaveFlow(ExecutionFlow executionFlow);\r
-\r
+ /** @return the variable value, or <code>null</code> if not found. */\r
public Object getVariable(String key);\r
\r
- public Object findVariable(String key);\r
-\r
- // TODO: replace with setVariable(String Key, Object value)\r
- public void addVariables(\r
- Map<? extends String, ? extends Object> variablesToAdd);\r
-\r
- public Date getCreationDate();\r
+ public void setVariable(String key, Object value);\r
}\r
--- /dev/null
+package org.argeo.slc.execution;
+
+public interface ExecutionStack {
+ /**
+ * @param name
+ * @return null if no object is found
+ */
+ public Object findScopedObject(String name);
+
+ public void addScopedObject(String name, Object obj);
+
+ public void enterFlow(ExecutionFlow executionFlow);
+
+ /** @return internal stack level UUID. */
+ public String getCurrentStackLevelUuid();
+
+ public Integer getStackSize();
+
+ public void leaveFlow(ExecutionFlow executionFlow);
+
+ Object findLocalVariable(String key);
+}
--- /dev/null
+package org.argeo.slc.execution;
+
+import java.util.Map;
+
+public interface ExecutionStackLevel {
+ public ExecutionFlow getExecutionFlow();
+
+ public Map<String, Object> getScopedObjects();
+
+ public String getUuid();
+
+ public Map<String, Object> getLocalVariables();
+
+}
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
-public class OsgiExecutionModulesManager extends AbstractExecutionModulesManager implements
- InitializingBean, DisposableBean {
- private final static Log log = LogFactory.getLog(OsgiExecutionModulesManager.class);
+public class OsgiExecutionModulesManager extends
+ AbstractExecutionModulesManager implements InitializingBean,
+ DisposableBean {
+ private final static Log log = LogFactory
+ .getLog(OsgiExecutionModulesManager.class);
private BundlesManager bundlesManager;
private ServiceTracker executionContexts;
String moduleName = realizedFlow.getModuleName();
String moduleVersion = realizedFlow.getModuleVersion();
- ExecutionContext executionContext = findExecutionContext(moduleName,
- moduleVersion);
-
// Check whether a descriptor converter is published by this module
ExecutionFlowDescriptorConverter descriptorConverter = findExecutionFlowDescriptorConverter(
moduleName, moduleVersion);
variablesToAdd = defaultDescriptorConverter
.convertValues(realizedFlow.getFlowDescriptor());
- executionContext.addVariables(variablesToAdd);
+ ExecutionContext executionContext = findExecutionContext(moduleName,
+ moduleVersion);
+ for (String key : variablesToAdd.keySet())
+ executionContext.setVariable(key, variablesToAdd.get(key));
ExecutionFlow flow = findExecutionFlow(moduleName, moduleVersion,
realizedFlow.getFlowDescriptor().getName());