X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=legacy%2Forg.argeo.slc.spring%2Fsrc%2Forg%2Fargeo%2Fslc%2Fcore%2Fexecution%2FDefaultExecutionFlowDescriptorConverter.java;fp=legacy%2Forg.argeo.slc.spring%2Fsrc%2Forg%2Fargeo%2Fslc%2Fcore%2Fexecution%2FDefaultExecutionFlowDescriptorConverter.java;h=0bdeb53bb749a444b2165bbfca35afe3502e8b6a;hb=6fc94d69efe089414ac9e63bde3efab1cbf7b7ca;hp=0000000000000000000000000000000000000000;hpb=b36c62642bd0db11b3133b369cc026fd4b7a1ec6;p=gpl%2Fargeo-slc.git diff --git a/legacy/org.argeo.slc.spring/src/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java b/legacy/org.argeo.slc.spring/src/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java new file mode 100644 index 000000000..0bdeb53bb --- /dev/null +++ b/legacy/org.argeo.slc.spring/src/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java @@ -0,0 +1,350 @@ +package org.argeo.slc.core.execution; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.slc.execution.ExecutionFlow; +import org.argeo.slc.execution.ExecutionFlowDescriptor; +import org.argeo.slc.execution.ExecutionFlowDescriptorConverter; +import org.argeo.slc.execution.ExecutionModuleDescriptor; +import org.argeo.slc.execution.ExecutionSpec; +import org.argeo.slc.execution.ExecutionSpecAttribute; +import org.argeo.slc.execution.FlowConfigurationException; +import org.argeo.slc.execution.RefSpecAttribute; +import org.argeo.slc.execution.RefValue; +import org.argeo.slc.primitive.PrimitiveSpecAttribute; +import org.argeo.slc.primitive.PrimitiveUtils; +import org.argeo.slc.primitive.PrimitiveValue; +import org.springframework.aop.scope.ScopedObject; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.StringUtils; + +/** + * Performs conversion in both direction between data exchanged with the agent + * and the data in the application context. + */ +public class DefaultExecutionFlowDescriptorConverter implements + ExecutionFlowDescriptorConverter, ApplicationContextAware { + public final static String REF_VALUE_TYPE_BEAN_NAME = "beanName"; + + /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */ + private final static String REF_VALUE_INTERNAL = "[internal]"; + + private final static Log log = LogFactory + .getLog(DefaultExecutionFlowDescriptorConverter.class); + + private ApplicationContext applicationContext; + + @SuppressWarnings("unused") + public Map convertValues( + ExecutionFlowDescriptor executionFlowDescriptor) { + Map values = executionFlowDescriptor.getValues(); + Map convertedValues = new HashMap(); + ExecutionSpec executionSpec = executionFlowDescriptor + .getExecutionSpec(); + + if (executionSpec == null && log.isTraceEnabled()) + log.warn("Execution spec is null for " + executionFlowDescriptor); + + if (values != null && executionSpec != null) { + values: for (String key : values.keySet()) { + ExecutionSpecAttribute attribute = executionSpec + .getAttributes().get(key); + + if (attribute == null) + throw new FlowConfigurationException( + "No spec attribute defined for '" + key + "'"); + + if (attribute.getIsConstant()) + continue values; + + Object value = values.get(key); + if (value instanceof PrimitiveValue) { + PrimitiveValue primitiveValue = (PrimitiveValue) value; + // TODO: check class <=> type + convertedValues.put(key, primitiveValue.getValue()); + } else if (value instanceof RefValue) { + RefValue refValue = (RefValue) value; + String type = refValue.getType(); + if (REF_VALUE_TYPE_BEAN_NAME.equals(type)) { + // FIXME: UI should send all information about spec + // - targetClass + // - name + // String executionSpecName = executionSpec.getName(); + // ExecutionSpec localSpec = (ExecutionSpec) + // applicationContext + // .getBean(executionSpecName); + // RefSpecAttribute localAttr = (RefSpecAttribute) + // localSpec + // .getAttributes().get(key); + // Class targetClass = localAttr.getTargetClass(); + // + // String primitiveType = PrimitiveUtils + // .classAsType(targetClass); + String primitiveType = null; + if (primitiveType != null) { + // not active + String ref = refValue.getRef(); + Object obj = PrimitiveUtils.convert(primitiveType, + ref); + convertedValues.put(key, obj); + } else { + String ref = refValue.getRef(); + if (ref != null && !ref.equals(REF_VALUE_INTERNAL)) { + Object obj = null; + if (applicationContext.containsBean(ref)) { + obj = applicationContext.getBean(ref); + } else { + // FIXME: hack in order to pass primitive + obj = ref; + } + convertedValues.put(key, obj); + } else { + log.warn("Cannot interpret " + refValue); + } + } + } else if (PrimitiveUtils.typeAsClass(type) != null) { + String ref = refValue.getRef(); + Object obj = PrimitiveUtils.convert(type, ref); + convertedValues.put(key, obj); + } else { + throw new FlowConfigurationException( + "Ref value type not supported: " + + refValue.getType()); + } + } else { + // default is to take the value as is + convertedValues.put(key, value); + } + } + } + return convertedValues; + } + + public void addFlowsToDescriptor(ExecutionModuleDescriptor md, + Map executionFlows) { + SortedSet set = new TreeSet( + new ExecutionFlowDescriptorComparator()); + for (String name : executionFlows.keySet()) { + ExecutionFlow executionFlow = executionFlows.get(name); + + ExecutionFlowDescriptor efd = getExecutionFlowDescriptor(executionFlow); + ExecutionSpec executionSpec = efd.getExecutionSpec(); + + // Add execution spec if necessary + if (!md.getExecutionSpecs().contains(executionSpec)) + md.getExecutionSpecs().add(executionSpec); + + // Add execution flow + set.add(efd); + // md.getExecutionFlows().add(efd); + } + md.getExecutionFlows().addAll(set); + } + + public ExecutionFlowDescriptor getExecutionFlowDescriptor( + ExecutionFlow executionFlow) { + if (executionFlow.getName() == null) + throw new FlowConfigurationException("Flow name is null: " + + executionFlow); + String name = executionFlow.getName(); + + ExecutionSpec executionSpec = executionFlow.getExecutionSpec(); + if (executionSpec == null) + throw new FlowConfigurationException("Execution spec is null: " + + executionFlow); + if (executionSpec.getName() == null) + throw new FlowConfigurationException( + "Execution spec name is null: " + executionSpec); + + Map values = new TreeMap(); + for (String key : executionSpec.getAttributes().keySet()) { + ExecutionSpecAttribute attribute = executionSpec.getAttributes() + .get(key); + + if (attribute instanceof PrimitiveSpecAttribute) { + if (executionFlow.isSetAsParameter(key)) { + Object value = executionFlow.getParameter(key); + PrimitiveValue primitiveValue = new PrimitiveValue(); + primitiveValue.setType(((PrimitiveSpecAttribute) attribute) + .getType()); + primitiveValue.setValue(value); + values.put(key, primitiveValue); + } else { + // no need to add a primitive value if it is not set, + // all necessary information is in the spec + } + } else if (attribute instanceof RefSpecAttribute) { + if (attribute.getIsConstant()) { + values.put(key, new RefValue(REF_VALUE_INTERNAL)); + } else + values.put( + key, + buildRefValue((RefSpecAttribute) attribute, + executionFlow, key)); + } else { + throw new FlowConfigurationException( + "Unkown spec attribute type " + attribute.getClass()); + } + + } + + ExecutionFlowDescriptor efd = new ExecutionFlowDescriptor(name, null, + values, executionSpec); + // Takes description from spring + BeanFactory bf = getBeanFactory(); + if (bf != null) { + BeanDefinition bd = getBeanFactory().getBeanDefinition(name); + efd.setDescription(bd.getDescription()); + } + return efd; + } + + protected RefValue buildRefValue(RefSpecAttribute rsa, + ExecutionFlow executionFlow, String key) { + RefValue refValue = new RefValue(); + // FIXME: UI should be able to deal with other types + refValue.setType(REF_VALUE_TYPE_BEAN_NAME); + Class targetClass = rsa.getTargetClass(); + String primitiveType = PrimitiveUtils.classAsType(targetClass); + if (primitiveType != null) { + if (executionFlow.isSetAsParameter(key)) { + Object value = executionFlow.getParameter(key); + refValue.setRef(value.toString()); + } + refValue.setType(primitiveType); + return refValue; + } else { + + if (executionFlow.isSetAsParameter(key)) { + String ref = null; + Object value = executionFlow.getParameter(key); + if (applicationContext == null) { + log.warn("No application context declared, cannot scan ref value."); + ref = value.toString(); + } else { + + // look for a ref to the value + Map beans = getBeanFactory() + .getBeansOfType(targetClass, false, false); + // TODO: also check scoped beans + beans: for (String beanName : beans.keySet()) { + Object obj = beans.get(beanName); + if (value instanceof ScopedObject) { + // don't call methods of the target of the scope + if (obj instanceof ScopedObject) + if (value == obj) { + ref = beanName; + break beans; + } + } else { + if (obj.equals(value)) { + ref = beanName; + break beans; + } + } + } + } + if (ref == null) { + if (log.isTraceEnabled()) + log.trace("Cannot define reference for ref spec attribute " + + key + + " in " + + executionFlow + + " (" + + rsa + + ")." + + " If it is an inner bean consider put it frozen."); + ref = REF_VALUE_INTERNAL; + } else { + if (log.isTraceEnabled()) + log.trace(ref + + " is the reference for ref spec attribute " + + key + " in " + executionFlow + " (" + rsa + + ")"); + } + refValue.setRef(ref); + } + return refValue; + } + } + + /** @return can be null */ + private ConfigurableListableBeanFactory getBeanFactory() { + if (applicationContext == null) + return null; + return ((ConfigurableApplicationContext) applicationContext) + .getBeanFactory(); + } + + /** Must be use within the execution application context */ + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + private static class ExecutionFlowDescriptorComparator implements + Comparator { + @SuppressWarnings("deprecation") + public int compare(ExecutionFlowDescriptor o1, + ExecutionFlowDescriptor o2) { + // TODO: write unit tests for this + + String name1 = o1.getName(); + String name2 = o2.getName(); + + String path1 = o1.getPath(); + String path2 = o2.getPath(); + + // Check whether name include path + int lastIndex1 = name1.lastIndexOf('/'); + // log.debug(name1+", "+lastIndex1); + if (!StringUtils.hasText(path1) && lastIndex1 >= 0) { + path1 = name1.substring(0, lastIndex1); + name1 = name1.substring(lastIndex1 + 1); + } + + int lastIndex2 = name2.lastIndexOf('/'); + if (!StringUtils.hasText(path2) && lastIndex2 >= 0) { + path2 = name2.substring(0, lastIndex2); + name2 = name2.substring(lastIndex2 + 1); + } + + // Perform the actual comparison + if (StringUtils.hasText(path1) && StringUtils.hasText(path2)) { + if (path1.equals(path2)) + return name1.compareTo(name2); + else if (path1.startsWith(path2)) + return -1; + else if (path2.startsWith(path1)) + return 1; + else + return path1.compareTo(path2); + } else if (!StringUtils.hasText(path1) + && StringUtils.hasText(path2)) { + return 1; + } else if (StringUtils.hasText(path1) + && !StringUtils.hasText(path2)) { + return -1; + } else if (!StringUtils.hasText(path1) + && !StringUtils.hasText(path2)) { + return name1.compareTo(name2); + } else { + return 0; + } + } + + } +}