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
.argeo
.api
.cms
.CmsLog
;
11 import org
.argeo
.slc
.execution
.ExecutionFlow
;
12 import org
.argeo
.slc
.execution
.ExecutionFlowDescriptor
;
13 import org
.argeo
.slc
.execution
.ExecutionFlowDescriptorConverter
;
14 import org
.argeo
.slc
.execution
.ExecutionModuleDescriptor
;
15 import org
.argeo
.slc
.execution
.ExecutionSpec
;
16 import org
.argeo
.slc
.execution
.ExecutionSpecAttribute
;
17 import org
.argeo
.slc
.execution
.FlowConfigurationException
;
18 import org
.argeo
.slc
.execution
.RefSpecAttribute
;
19 import org
.argeo
.slc
.execution
.RefValue
;
20 import org
.argeo
.slc
.primitive
.PrimitiveSpecAttribute
;
21 import org
.argeo
.slc
.primitive
.PrimitiveUtils
;
22 import org
.argeo
.slc
.primitive
.PrimitiveValue
;
23 import org
.springframework
.aop
.scope
.ScopedObject
;
24 import org
.springframework
.beans
.BeansException
;
25 import org
.springframework
.beans
.factory
.BeanFactory
;
26 import org
.springframework
.beans
.factory
.config
.BeanDefinition
;
27 import org
.springframework
.beans
.factory
.config
.ConfigurableListableBeanFactory
;
28 import org
.springframework
.context
.ApplicationContext
;
29 import org
.springframework
.context
.ApplicationContextAware
;
30 import org
.springframework
.context
.ConfigurableApplicationContext
;
31 import org
.springframework
.util
.StringUtils
;
34 * Performs conversion in both direction between data exchanged with the agent
35 * and the data in the application context.
37 public class DefaultExecutionFlowDescriptorConverter
implements
38 ExecutionFlowDescriptorConverter
, ApplicationContextAware
{
39 public final static String REF_VALUE_TYPE_BEAN_NAME
= "beanName";
41 /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */
42 private final static String REF_VALUE_INTERNAL
= "[internal]";
44 private final static CmsLog log
= CmsLog
45 .getLog(DefaultExecutionFlowDescriptorConverter
.class);
47 private ApplicationContext applicationContext
;
49 @SuppressWarnings("unused")
50 public Map
<String
, Object
> convertValues(
51 ExecutionFlowDescriptor executionFlowDescriptor
) {
52 Map
<String
, Object
> values
= executionFlowDescriptor
.getValues();
53 Map
<String
, Object
> convertedValues
= new HashMap
<String
, Object
>();
54 ExecutionSpec executionSpec
= executionFlowDescriptor
57 if (executionSpec
== null && log
.isTraceEnabled())
58 log
.warn("Execution spec is null for " + executionFlowDescriptor
);
60 if (values
!= null && executionSpec
!= null) {
61 values
: for (String key
: values
.keySet()) {
62 ExecutionSpecAttribute attribute
= executionSpec
63 .getAttributes().get(key
);
65 if (attribute
== null)
66 throw new FlowConfigurationException(
67 "No spec attribute defined for '" + key
+ "'");
69 if (attribute
.getIsConstant())
72 Object value
= values
.get(key
);
73 if (value
instanceof PrimitiveValue
) {
74 PrimitiveValue primitiveValue
= (PrimitiveValue
) value
;
75 // TODO: check class <=> type
76 convertedValues
.put(key
, primitiveValue
.getValue());
77 } else if (value
instanceof RefValue
) {
78 RefValue refValue
= (RefValue
) value
;
79 String type
= refValue
.getType();
80 if (REF_VALUE_TYPE_BEAN_NAME
.equals(type
)) {
81 // FIXME: UI should send all information about spec
84 // String executionSpecName = executionSpec.getName();
85 // ExecutionSpec localSpec = (ExecutionSpec)
87 // .getBean(executionSpecName);
88 // RefSpecAttribute localAttr = (RefSpecAttribute)
90 // .getAttributes().get(key);
91 // Class<?> targetClass = localAttr.getTargetClass();
93 // String primitiveType = PrimitiveUtils
94 // .classAsType(targetClass);
95 String primitiveType
= null;
96 if (primitiveType
!= null) {
98 String ref
= refValue
.getRef();
99 Object obj
= PrimitiveUtils
.convert(primitiveType
,
101 convertedValues
.put(key
, obj
);
103 String ref
= refValue
.getRef();
104 if (ref
!= null && !ref
.equals(REF_VALUE_INTERNAL
)) {
106 if (applicationContext
.containsBean(ref
)) {
107 obj
= applicationContext
.getBean(ref
);
109 // FIXME: hack in order to pass primitive
112 convertedValues
.put(key
, obj
);
114 log
.warn("Cannot interpret " + refValue
);
117 } else if (PrimitiveUtils
.typeAsClass(type
) != null) {
118 String ref
= refValue
.getRef();
119 Object obj
= PrimitiveUtils
.convert(type
, ref
);
120 convertedValues
.put(key
, obj
);
122 throw new FlowConfigurationException(
123 "Ref value type not supported: "
124 + refValue
.getType());
127 // default is to take the value as is
128 convertedValues
.put(key
, value
);
132 return convertedValues
;
135 public void addFlowsToDescriptor(ExecutionModuleDescriptor md
,
136 Map
<String
, ExecutionFlow
> executionFlows
) {
137 SortedSet
<ExecutionFlowDescriptor
> set
= new TreeSet
<ExecutionFlowDescriptor
>(
138 new ExecutionFlowDescriptorComparator());
139 for (String name
: executionFlows
.keySet()) {
140 ExecutionFlow executionFlow
= executionFlows
.get(name
);
142 ExecutionFlowDescriptor efd
= getExecutionFlowDescriptor(executionFlow
);
143 ExecutionSpec executionSpec
= efd
.getExecutionSpec();
145 // Add execution spec if necessary
146 if (!md
.getExecutionSpecs().contains(executionSpec
))
147 md
.getExecutionSpecs().add(executionSpec
);
149 // Add execution flow
151 // md.getExecutionFlows().add(efd);
153 md
.getExecutionFlows().addAll(set
);
156 public ExecutionFlowDescriptor
getExecutionFlowDescriptor(
157 ExecutionFlow executionFlow
) {
158 if (executionFlow
.getName() == null)
159 throw new FlowConfigurationException("Flow name is null: "
161 String name
= executionFlow
.getName();
163 ExecutionSpec executionSpec
= executionFlow
.getExecutionSpec();
164 if (executionSpec
== null)
165 throw new FlowConfigurationException("Execution spec is null: "
167 if (executionSpec
.getName() == null)
168 throw new FlowConfigurationException(
169 "Execution spec name is null: " + executionSpec
);
171 Map
<String
, Object
> values
= new TreeMap
<String
, Object
>();
172 for (String key
: executionSpec
.getAttributes().keySet()) {
173 ExecutionSpecAttribute attribute
= executionSpec
.getAttributes()
176 if (attribute
instanceof PrimitiveSpecAttribute
) {
177 if (executionFlow
.isSetAsParameter(key
)) {
178 Object value
= executionFlow
.getParameter(key
);
179 PrimitiveValue primitiveValue
= new PrimitiveValue();
180 primitiveValue
.setType(((PrimitiveSpecAttribute
) attribute
)
182 primitiveValue
.setValue(value
);
183 values
.put(key
, primitiveValue
);
185 // no need to add a primitive value if it is not set,
186 // all necessary information is in the spec
188 } else if (attribute
instanceof RefSpecAttribute
) {
189 if (attribute
.getIsConstant()) {
190 values
.put(key
, new RefValue(REF_VALUE_INTERNAL
));
194 buildRefValue((RefSpecAttribute
) attribute
,
195 executionFlow
, key
));
197 throw new FlowConfigurationException(
198 "Unkown spec attribute type " + attribute
.getClass());
203 ExecutionFlowDescriptor efd
= new ExecutionFlowDescriptor(name
, null,
204 values
, executionSpec
);
205 // Takes description from spring
206 BeanFactory bf
= getBeanFactory();
208 BeanDefinition bd
= getBeanFactory().getBeanDefinition(name
);
209 efd
.setDescription(bd
.getDescription());
214 protected RefValue
buildRefValue(RefSpecAttribute rsa
,
215 ExecutionFlow executionFlow
, String key
) {
216 RefValue refValue
= new RefValue();
217 // FIXME: UI should be able to deal with other types
218 refValue
.setType(REF_VALUE_TYPE_BEAN_NAME
);
219 Class
<?
> targetClass
= rsa
.getTargetClass();
220 String primitiveType
= PrimitiveUtils
.classAsType(targetClass
);
221 if (primitiveType
!= null) {
222 if (executionFlow
.isSetAsParameter(key
)) {
223 Object value
= executionFlow
.getParameter(key
);
224 refValue
.setRef(value
.toString());
226 refValue
.setType(primitiveType
);
230 if (executionFlow
.isSetAsParameter(key
)) {
232 Object value
= executionFlow
.getParameter(key
);
233 if (applicationContext
== null) {
234 log
.warn("No application context declared, cannot scan ref value.");
235 ref
= value
.toString();
238 // look for a ref to the value
239 Map
<String
, ?
> beans
= getBeanFactory()
240 .getBeansOfType(targetClass
, false, false);
241 // TODO: also check scoped beans
242 beans
: for (String beanName
: beans
.keySet()) {
243 Object obj
= beans
.get(beanName
);
244 if (value
instanceof ScopedObject
) {
245 // don't call methods of the target of the scope
246 if (obj
instanceof ScopedObject
)
252 if (obj
.equals(value
)) {
260 if (log
.isTraceEnabled())
261 log
.trace("Cannot define reference for ref spec attribute "
268 + " If it is an inner bean consider put it frozen.");
269 ref
= REF_VALUE_INTERNAL
;
271 if (log
.isTraceEnabled())
273 + " is the reference for ref spec attribute "
274 + key
+ " in " + executionFlow
+ " (" + rsa
277 refValue
.setRef(ref
);
283 /** @return can be null */
284 private ConfigurableListableBeanFactory
getBeanFactory() {
285 if (applicationContext
== null)
287 return ((ConfigurableApplicationContext
) applicationContext
)
291 /** Must be use within the execution application context */
292 public void setApplicationContext(ApplicationContext applicationContext
)
293 throws BeansException
{
294 this.applicationContext
= applicationContext
;
297 private static class ExecutionFlowDescriptorComparator
implements
298 Comparator
<ExecutionFlowDescriptor
> {
299 @SuppressWarnings("deprecation")
300 public int compare(ExecutionFlowDescriptor o1
,
301 ExecutionFlowDescriptor o2
) {
302 // TODO: write unit tests for this
304 String name1
= o1
.getName();
305 String name2
= o2
.getName();
307 String path1
= o1
.getPath();
308 String path2
= o2
.getPath();
310 // Check whether name include path
311 int lastIndex1
= name1
.lastIndexOf('/');
312 // log.debug(name1+", "+lastIndex1);
313 if (!StringUtils
.hasText(path1
) && lastIndex1
>= 0) {
314 path1
= name1
.substring(0, lastIndex1
);
315 name1
= name1
.substring(lastIndex1
+ 1);
318 int lastIndex2
= name2
.lastIndexOf('/');
319 if (!StringUtils
.hasText(path2
) && lastIndex2
>= 0) {
320 path2
= name2
.substring(0, lastIndex2
);
321 name2
= name2
.substring(lastIndex2
+ 1);
324 // Perform the actual comparison
325 if (StringUtils
.hasText(path1
) && StringUtils
.hasText(path2
)) {
326 if (path1
.equals(path2
))
327 return name1
.compareTo(name2
);
328 else if (path1
.startsWith(path2
))
330 else if (path2
.startsWith(path1
))
333 return path1
.compareTo(path2
);
334 } else if (!StringUtils
.hasText(path1
)
335 && StringUtils
.hasText(path2
)) {
337 } else if (StringUtils
.hasText(path1
)
338 && !StringUtils
.hasText(path2
)) {
340 } else if (!StringUtils
.hasText(path1
)
341 && !StringUtils
.hasText(path2
)) {
342 return name1
.compareTo(name2
);