1 package org
.argeo
.slc
.core
.execution
;
3 import java
.util
.Comparator
;
4 import java
.util
.HashMap
;
6 import java
.util
.SortedSet
;
7 import java
.util
.TreeMap
;
8 import java
.util
.TreeSet
;
10 import org
.apache
.commons
.logging
.Log
;
11 import org
.apache
.commons
.logging
.LogFactory
;
12 import org
.argeo
.slc
.execution
.ExecutionFlow
;
13 import org
.argeo
.slc
.execution
.ExecutionFlowDescriptor
;
14 import org
.argeo
.slc
.execution
.ExecutionFlowDescriptorConverter
;
15 import org
.argeo
.slc
.execution
.ExecutionModuleDescriptor
;
16 import org
.argeo
.slc
.execution
.ExecutionSpec
;
17 import org
.argeo
.slc
.execution
.ExecutionSpecAttribute
;
18 import org
.argeo
.slc
.execution
.FlowConfigurationException
;
19 import org
.argeo
.slc
.execution
.RefSpecAttribute
;
20 import org
.argeo
.slc
.execution
.RefValue
;
21 import org
.argeo
.slc
.primitive
.PrimitiveSpecAttribute
;
22 import org
.argeo
.slc
.primitive
.PrimitiveUtils
;
23 import org
.argeo
.slc
.primitive
.PrimitiveValue
;
24 import org
.springframework
.aop
.scope
.ScopedObject
;
25 import org
.springframework
.beans
.BeansException
;
26 import org
.springframework
.beans
.factory
.BeanFactory
;
27 import org
.springframework
.beans
.factory
.config
.BeanDefinition
;
28 import org
.springframework
.beans
.factory
.config
.ConfigurableListableBeanFactory
;
29 import org
.springframework
.context
.ApplicationContext
;
30 import org
.springframework
.context
.ApplicationContextAware
;
31 import org
.springframework
.context
.ConfigurableApplicationContext
;
32 import org
.springframework
.util
.StringUtils
;
35 * Performs conversion in both direction between data exchanged with the agent
36 * and the data in the application context.
38 public class DefaultExecutionFlowDescriptorConverter
implements
39 ExecutionFlowDescriptorConverter
, ApplicationContextAware
{
40 public final static String REF_VALUE_TYPE_BEAN_NAME
= "beanName";
42 /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */
43 private final static String REF_VALUE_INTERNAL
= "[internal]";
45 private final static Log log
= LogFactory
46 .getLog(DefaultExecutionFlowDescriptorConverter
.class);
48 private ApplicationContext applicationContext
;
50 @SuppressWarnings("unused")
51 public Map
<String
, Object
> convertValues(
52 ExecutionFlowDescriptor executionFlowDescriptor
) {
53 Map
<String
, Object
> values
= executionFlowDescriptor
.getValues();
54 Map
<String
, Object
> convertedValues
= new HashMap
<String
, Object
>();
55 ExecutionSpec executionSpec
= executionFlowDescriptor
58 if (executionSpec
== null && log
.isTraceEnabled())
59 log
.warn("Execution spec is null for " + executionFlowDescriptor
);
61 if (values
!= null && executionSpec
!= null) {
62 values
: for (String key
: values
.keySet()) {
63 ExecutionSpecAttribute attribute
= executionSpec
64 .getAttributes().get(key
);
66 if (attribute
== null)
67 throw new FlowConfigurationException(
68 "No spec attribute defined for '" + key
+ "'");
70 if (attribute
.getIsConstant())
73 Object value
= values
.get(key
);
74 if (value
instanceof PrimitiveValue
) {
75 PrimitiveValue primitiveValue
= (PrimitiveValue
) value
;
76 // TODO: check class <=> type
77 convertedValues
.put(key
, primitiveValue
.getValue());
78 } else if (value
instanceof RefValue
) {
79 RefValue refValue
= (RefValue
) value
;
80 String type
= refValue
.getType();
81 if (REF_VALUE_TYPE_BEAN_NAME
.equals(type
)) {
82 // FIXME: UI should send all information about spec
85 // String executionSpecName = executionSpec.getName();
86 // ExecutionSpec localSpec = (ExecutionSpec)
88 // .getBean(executionSpecName);
89 // RefSpecAttribute localAttr = (RefSpecAttribute)
91 // .getAttributes().get(key);
92 // Class<?> targetClass = localAttr.getTargetClass();
94 // String primitiveType = PrimitiveUtils
95 // .classAsType(targetClass);
96 String primitiveType
= null;
97 if (primitiveType
!= null) {
99 String ref
= refValue
.getRef();
100 Object obj
= PrimitiveUtils
.convert(primitiveType
,
102 convertedValues
.put(key
, obj
);
104 String ref
= refValue
.getRef();
105 if (ref
!= null && !ref
.equals(REF_VALUE_INTERNAL
)) {
107 if (applicationContext
.containsBean(ref
)) {
108 obj
= applicationContext
.getBean(ref
);
110 // FIXME: hack in order to pass primitive
113 convertedValues
.put(key
, obj
);
115 log
.warn("Cannot interpret " + refValue
);
118 } else if (PrimitiveUtils
.typeAsClass(type
) != null) {
119 String ref
= refValue
.getRef();
120 Object obj
= PrimitiveUtils
.convert(type
, ref
);
121 convertedValues
.put(key
, obj
);
123 throw new FlowConfigurationException(
124 "Ref value type not supported: "
125 + refValue
.getType());
128 // default is to take the value as is
129 convertedValues
.put(key
, value
);
133 return convertedValues
;
136 public void addFlowsToDescriptor(ExecutionModuleDescriptor md
,
137 Map
<String
, ExecutionFlow
> executionFlows
) {
138 SortedSet
<ExecutionFlowDescriptor
> set
= new TreeSet
<ExecutionFlowDescriptor
>(
139 new ExecutionFlowDescriptorComparator());
140 for (String name
: executionFlows
.keySet()) {
141 ExecutionFlow executionFlow
= executionFlows
.get(name
);
143 ExecutionFlowDescriptor efd
= getExecutionFlowDescriptor(executionFlow
);
144 ExecutionSpec executionSpec
= efd
.getExecutionSpec();
146 // Add execution spec if necessary
147 if (!md
.getExecutionSpecs().contains(executionSpec
))
148 md
.getExecutionSpecs().add(executionSpec
);
150 // Add execution flow
152 // md.getExecutionFlows().add(efd);
154 md
.getExecutionFlows().addAll(set
);
157 public ExecutionFlowDescriptor
getExecutionFlowDescriptor(
158 ExecutionFlow executionFlow
) {
159 if (executionFlow
.getName() == null)
160 throw new FlowConfigurationException("Flow name is null: "
162 String name
= executionFlow
.getName();
164 ExecutionSpec executionSpec
= executionFlow
.getExecutionSpec();
165 if (executionSpec
== null)
166 throw new FlowConfigurationException("Execution spec is null: "
168 if (executionSpec
.getName() == null)
169 throw new FlowConfigurationException(
170 "Execution spec name is null: " + executionSpec
);
172 Map
<String
, Object
> values
= new TreeMap
<String
, Object
>();
173 for (String key
: executionSpec
.getAttributes().keySet()) {
174 ExecutionSpecAttribute attribute
= executionSpec
.getAttributes()
177 if (attribute
instanceof PrimitiveSpecAttribute
) {
178 if (executionFlow
.isSetAsParameter(key
)) {
179 Object value
= executionFlow
.getParameter(key
);
180 PrimitiveValue primitiveValue
= new PrimitiveValue();
181 primitiveValue
.setType(((PrimitiveSpecAttribute
) attribute
)
183 primitiveValue
.setValue(value
);
184 values
.put(key
, primitiveValue
);
186 // no need to add a primitive value if it is not set,
187 // all necessary information is in the spec
189 } else if (attribute
instanceof RefSpecAttribute
) {
190 if (attribute
.getIsConstant()) {
191 values
.put(key
, new RefValue(REF_VALUE_INTERNAL
));
195 buildRefValue((RefSpecAttribute
) attribute
,
196 executionFlow
, key
));
198 throw new FlowConfigurationException(
199 "Unkown spec attribute type " + attribute
.getClass());
204 ExecutionFlowDescriptor efd
= new ExecutionFlowDescriptor(name
, null,
205 values
, executionSpec
);
206 // Takes description from spring
207 BeanFactory bf
= getBeanFactory();
209 BeanDefinition bd
= getBeanFactory().getBeanDefinition(name
);
210 efd
.setDescription(bd
.getDescription());
215 protected RefValue
buildRefValue(RefSpecAttribute rsa
,
216 ExecutionFlow executionFlow
, String key
) {
217 RefValue refValue
= new RefValue();
218 // FIXME: UI should be able to deal with other types
219 refValue
.setType(REF_VALUE_TYPE_BEAN_NAME
);
220 Class
<?
> targetClass
= rsa
.getTargetClass();
221 String primitiveType
= PrimitiveUtils
.classAsType(targetClass
);
222 if (primitiveType
!= null) {
223 if (executionFlow
.isSetAsParameter(key
)) {
224 Object value
= executionFlow
.getParameter(key
);
225 refValue
.setRef(value
.toString());
227 refValue
.setType(primitiveType
);
231 if (executionFlow
.isSetAsParameter(key
)) {
233 Object value
= executionFlow
.getParameter(key
);
234 if (applicationContext
== null) {
235 log
.warn("No application context declared, cannot scan ref value.");
236 ref
= value
.toString();
239 // look for a ref to the value
240 Map
<String
, ?
> beans
= getBeanFactory()
241 .getBeansOfType(targetClass
, false, false);
242 // TODO: also check scoped beans
243 beans
: for (String beanName
: beans
.keySet()) {
244 Object obj
= beans
.get(beanName
);
245 if (value
instanceof ScopedObject
) {
246 // don't call methods of the target of the scope
247 if (obj
instanceof ScopedObject
)
253 if (obj
.equals(value
)) {
261 if (log
.isTraceEnabled())
262 log
.trace("Cannot define reference for ref spec attribute "
269 + " If it is an inner bean consider put it frozen.");
270 ref
= REF_VALUE_INTERNAL
;
272 if (log
.isTraceEnabled())
274 + " is the reference for ref spec attribute "
275 + key
+ " in " + executionFlow
+ " (" + rsa
278 refValue
.setRef(ref
);
284 /** @return can be null */
285 private ConfigurableListableBeanFactory
getBeanFactory() {
286 if (applicationContext
== null)
288 return ((ConfigurableApplicationContext
) applicationContext
)
292 /** Must be use within the execution application context */
293 public void setApplicationContext(ApplicationContext applicationContext
)
294 throws BeansException
{
295 this.applicationContext
= applicationContext
;
298 private static class ExecutionFlowDescriptorComparator
implements
299 Comparator
<ExecutionFlowDescriptor
> {
300 @SuppressWarnings("deprecation")
301 public int compare(ExecutionFlowDescriptor o1
,
302 ExecutionFlowDescriptor o2
) {
303 // TODO: write unit tests for this
305 String name1
= o1
.getName();
306 String name2
= o2
.getName();
308 String path1
= o1
.getPath();
309 String path2
= o2
.getPath();
311 // Check whether name include path
312 int lastIndex1
= name1
.lastIndexOf('/');
313 // log.debug(name1+", "+lastIndex1);
314 if (!StringUtils
.hasText(path1
) && lastIndex1
>= 0) {
315 path1
= name1
.substring(0, lastIndex1
);
316 name1
= name1
.substring(lastIndex1
+ 1);
319 int lastIndex2
= name2
.lastIndexOf('/');
320 if (!StringUtils
.hasText(path2
) && lastIndex2
>= 0) {
321 path2
= name2
.substring(0, lastIndex2
);
322 name2
= name2
.substring(lastIndex2
+ 1);
325 // Perform the actual comparison
326 if (StringUtils
.hasText(path1
) && StringUtils
.hasText(path2
)) {
327 if (path1
.equals(path2
))
328 return name1
.compareTo(name2
);
329 else if (path1
.startsWith(path2
))
331 else if (path2
.startsWith(path1
))
334 return path1
.compareTo(path2
);
335 } else if (!StringUtils
.hasText(path1
)
336 && StringUtils
.hasText(path2
)) {
338 } else if (StringUtils
.hasText(path1
)
339 && !StringUtils
.hasText(path2
)) {
341 } else if (!StringUtils
.hasText(path1
)
342 && !StringUtils
.hasText(path2
)) {
343 return name1
.compareTo(name2
);