--- /dev/null
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+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.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<String, Object> convertValues(
+ ExecutionFlowDescriptor executionFlowDescriptor) {
+ Map<String, Object> values = executionFlowDescriptor.getValues();
+ Map<String, Object> convertedValues = new HashMap<String, Object>();
+ 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<String, ExecutionFlow> executionFlows) {
+ SortedSet<ExecutionFlowDescriptor> set = new TreeSet<ExecutionFlowDescriptor>(
+ 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<String, Object> values = new TreeMap<String, Object>();
+ 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<String, ?> 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<ExecutionFlowDescriptor> {
+ @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;
+ }
+ }
+
+ }
+}