2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.slc
.core
.execution
;
18 import java
.util
.Comparator
;
19 import java
.util
.HashMap
;
21 import java
.util
.SortedSet
;
22 import java
.util
.TreeMap
;
23 import java
.util
.TreeSet
;
25 import org
.apache
.commons
.logging
.Log
;
26 import org
.apache
.commons
.logging
.LogFactory
;
27 import org
.argeo
.slc
.execution
.ExecutionFlow
;
28 import org
.argeo
.slc
.execution
.ExecutionFlowDescriptor
;
29 import org
.argeo
.slc
.execution
.ExecutionFlowDescriptorConverter
;
30 import org
.argeo
.slc
.execution
.ExecutionModuleDescriptor
;
31 import org
.argeo
.slc
.execution
.ExecutionSpec
;
32 import org
.argeo
.slc
.execution
.ExecutionSpecAttribute
;
33 import org
.argeo
.slc
.execution
.FlowConfigurationException
;
34 import org
.argeo
.slc
.execution
.RefSpecAttribute
;
35 import org
.argeo
.slc
.execution
.RefValue
;
36 import org
.argeo
.slc
.primitive
.PrimitiveSpecAttribute
;
37 import org
.argeo
.slc
.primitive
.PrimitiveUtils
;
38 import org
.argeo
.slc
.primitive
.PrimitiveValue
;
39 import org
.springframework
.aop
.scope
.ScopedObject
;
40 import org
.springframework
.beans
.BeansException
;
41 import org
.springframework
.beans
.factory
.BeanFactory
;
42 import org
.springframework
.beans
.factory
.config
.BeanDefinition
;
43 import org
.springframework
.beans
.factory
.config
.ConfigurableListableBeanFactory
;
44 import org
.springframework
.context
.ApplicationContext
;
45 import org
.springframework
.context
.ApplicationContextAware
;
46 import org
.springframework
.context
.ConfigurableApplicationContext
;
47 import org
.springframework
.util
.StringUtils
;
50 * Performs conversion in both direction between data exchanged with the agent
51 * and the data in the application context.
53 public class DefaultExecutionFlowDescriptorConverter
implements
54 ExecutionFlowDescriptorConverter
, ApplicationContextAware
{
55 public final static String REF_VALUE_TYPE_BEAN_NAME
= "beanName";
57 /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */
58 private final static String REF_VALUE_INTERNAL
= "[internal]";
60 private final static Log log
= LogFactory
61 .getLog(DefaultExecutionFlowDescriptorConverter
.class);
63 private ApplicationContext applicationContext
;
65 @SuppressWarnings("unused")
66 public Map
<String
, Object
> convertValues(
67 ExecutionFlowDescriptor executionFlowDescriptor
) {
68 Map
<String
, Object
> values
= executionFlowDescriptor
.getValues();
69 Map
<String
, Object
> convertedValues
= new HashMap
<String
, Object
>();
70 ExecutionSpec executionSpec
= executionFlowDescriptor
73 if (executionSpec
== null && log
.isTraceEnabled())
74 log
.warn("Execution spec is null for " + executionFlowDescriptor
);
76 if (values
!= null && executionSpec
!= null) {
77 values
: for (String key
: values
.keySet()) {
78 ExecutionSpecAttribute attribute
= executionSpec
79 .getAttributes().get(key
);
81 if (attribute
== null)
82 throw new FlowConfigurationException(
83 "No spec attribute defined for '" + key
+ "'");
85 if (attribute
.getIsConstant())
88 Object value
= values
.get(key
);
89 if (value
instanceof PrimitiveValue
) {
90 PrimitiveValue primitiveValue
= (PrimitiveValue
) value
;
91 // TODO: check class <=> type
92 convertedValues
.put(key
, primitiveValue
.getValue());
93 } else if (value
instanceof RefValue
) {
94 RefValue refValue
= (RefValue
) value
;
95 String type
= refValue
.getType();
96 if (REF_VALUE_TYPE_BEAN_NAME
.equals(type
)) {
97 // FIXME: UI should send all information about spec
100 // String executionSpecName = executionSpec.getName();
101 // ExecutionSpec localSpec = (ExecutionSpec)
102 // applicationContext
103 // .getBean(executionSpecName);
104 // RefSpecAttribute localAttr = (RefSpecAttribute)
106 // .getAttributes().get(key);
107 // Class<?> targetClass = localAttr.getTargetClass();
109 // String primitiveType = PrimitiveUtils
110 // .classAsType(targetClass);
111 String primitiveType
= null;
112 if (primitiveType
!= null) {
114 String ref
= refValue
.getRef();
115 Object obj
= PrimitiveUtils
.convert(primitiveType
,
117 convertedValues
.put(key
, obj
);
119 String ref
= refValue
.getRef();
120 if (ref
!= null && !ref
.equals(REF_VALUE_INTERNAL
)) {
122 if (applicationContext
.containsBean(ref
)) {
123 obj
= applicationContext
.getBean(ref
);
125 // FIXME: hack in order to pass primitive
128 convertedValues
.put(key
, obj
);
130 log
.warn("Cannot interpret " + refValue
);
133 } else if (PrimitiveUtils
.typeAsClass(type
) != null) {
134 String ref
= refValue
.getRef();
135 Object obj
= PrimitiveUtils
.convert(type
, ref
);
136 convertedValues
.put(key
, obj
);
138 throw new FlowConfigurationException(
139 "Ref value type not supported: "
140 + refValue
.getType());
143 // default is to take the value as is
144 convertedValues
.put(key
, value
);
148 return convertedValues
;
151 public void addFlowsToDescriptor(ExecutionModuleDescriptor md
,
152 Map
<String
, ExecutionFlow
> executionFlows
) {
153 SortedSet
<ExecutionFlowDescriptor
> set
= new TreeSet
<ExecutionFlowDescriptor
>(
154 new ExecutionFlowDescriptorComparator());
155 for (String name
: executionFlows
.keySet()) {
156 ExecutionFlow executionFlow
= executionFlows
.get(name
);
158 ExecutionFlowDescriptor efd
= getExecutionFlowDescriptor(executionFlow
);
159 ExecutionSpec executionSpec
= efd
.getExecutionSpec();
161 // Add execution spec if necessary
162 if (!md
.getExecutionSpecs().contains(executionSpec
))
163 md
.getExecutionSpecs().add(executionSpec
);
165 // Add execution flow
167 // md.getExecutionFlows().add(efd);
169 md
.getExecutionFlows().addAll(set
);
172 public ExecutionFlowDescriptor
getExecutionFlowDescriptor(
173 ExecutionFlow executionFlow
) {
174 if (executionFlow
.getName() == null)
175 throw new FlowConfigurationException("Flow name is null: "
177 String name
= executionFlow
.getName();
179 ExecutionSpec executionSpec
= executionFlow
.getExecutionSpec();
180 if (executionSpec
== null)
181 throw new FlowConfigurationException("Execution spec is null: "
183 if (executionSpec
.getName() == null)
184 throw new FlowConfigurationException(
185 "Execution spec name is null: " + executionSpec
);
187 Map
<String
, Object
> values
= new TreeMap
<String
, Object
>();
188 for (String key
: executionSpec
.getAttributes().keySet()) {
189 ExecutionSpecAttribute attribute
= executionSpec
.getAttributes()
192 if (attribute
instanceof PrimitiveSpecAttribute
) {
193 if (executionFlow
.isSetAsParameter(key
)) {
194 Object value
= executionFlow
.getParameter(key
);
195 PrimitiveValue primitiveValue
= new PrimitiveValue();
196 primitiveValue
.setType(((PrimitiveSpecAttribute
) attribute
)
198 primitiveValue
.setValue(value
);
199 values
.put(key
, primitiveValue
);
201 // no need to add a primitive value if it is not set,
202 // all necessary information is in the spec
204 } else if (attribute
instanceof RefSpecAttribute
) {
205 if (attribute
.getIsConstant()) {
206 values
.put(key
, new RefValue(REF_VALUE_INTERNAL
));
210 buildRefValue((RefSpecAttribute
) attribute
,
211 executionFlow
, key
));
213 throw new FlowConfigurationException(
214 "Unkown spec attribute type " + attribute
.getClass());
219 ExecutionFlowDescriptor efd
= new ExecutionFlowDescriptor(name
, null,
220 values
, executionSpec
);
221 // Takes description from spring
222 BeanFactory bf
= getBeanFactory();
224 BeanDefinition bd
= getBeanFactory().getBeanDefinition(name
);
225 efd
.setDescription(bd
.getDescription());
230 protected RefValue
buildRefValue(RefSpecAttribute rsa
,
231 ExecutionFlow executionFlow
, String key
) {
232 RefValue refValue
= new RefValue();
233 // FIXME: UI should be able to deal with other types
234 refValue
.setType(REF_VALUE_TYPE_BEAN_NAME
);
235 Class
<?
> targetClass
= rsa
.getTargetClass();
236 String primitiveType
= PrimitiveUtils
.classAsType(targetClass
);
237 if (primitiveType
!= null) {
238 if (executionFlow
.isSetAsParameter(key
)) {
239 Object value
= executionFlow
.getParameter(key
);
240 refValue
.setRef(value
.toString());
242 refValue
.setType(primitiveType
);
246 if (executionFlow
.isSetAsParameter(key
)) {
248 Object value
= executionFlow
.getParameter(key
);
249 if (applicationContext
== null) {
250 log
.warn("No application context declared, cannot scan ref value.");
251 ref
= value
.toString();
254 // look for a ref to the value
255 Map
<String
, ?
> beans
= getBeanFactory()
256 .getBeansOfType(targetClass
, false, false);
257 // TODO: also check scoped beans
258 beans
: for (String beanName
: beans
.keySet()) {
259 Object obj
= beans
.get(beanName
);
260 if (value
instanceof ScopedObject
) {
261 // don't call methods of the target of the scope
262 if (obj
instanceof ScopedObject
)
268 if (obj
.equals(value
)) {
276 if (log
.isTraceEnabled())
277 log
.trace("Cannot define reference for ref spec attribute "
284 + " If it is an inner bean consider put it frozen.");
285 ref
= REF_VALUE_INTERNAL
;
287 if (log
.isTraceEnabled())
289 + " is the reference for ref spec attribute "
290 + key
+ " in " + executionFlow
+ " (" + rsa
293 refValue
.setRef(ref
);
299 /** @return can be null */
300 private ConfigurableListableBeanFactory
getBeanFactory() {
301 if (applicationContext
== null)
303 return ((ConfigurableApplicationContext
) applicationContext
)
307 /** Must be use within the execution application context */
308 public void setApplicationContext(ApplicationContext applicationContext
)
309 throws BeansException
{
310 this.applicationContext
= applicationContext
;
313 private static class ExecutionFlowDescriptorComparator
implements
314 Comparator
<ExecutionFlowDescriptor
> {
315 @SuppressWarnings("deprecation")
316 public int compare(ExecutionFlowDescriptor o1
,
317 ExecutionFlowDescriptor o2
) {
318 // TODO: write unit tests for this
320 String name1
= o1
.getName();
321 String name2
= o2
.getName();
323 String path1
= o1
.getPath();
324 String path2
= o2
.getPath();
326 // Check whether name include path
327 int lastIndex1
= name1
.lastIndexOf('/');
328 // log.debug(name1+", "+lastIndex1);
329 if (!StringUtils
.hasText(path1
) && lastIndex1
>= 0) {
330 path1
= name1
.substring(0, lastIndex1
);
331 name1
= name1
.substring(lastIndex1
+ 1);
334 int lastIndex2
= name2
.lastIndexOf('/');
335 if (!StringUtils
.hasText(path2
) && lastIndex2
>= 0) {
336 path2
= name2
.substring(0, lastIndex2
);
337 name2
= name2
.substring(lastIndex2
+ 1);
340 // Perform the actual comparison
341 if (StringUtils
.hasText(path1
) && StringUtils
.hasText(path2
)) {
342 if (path1
.equals(path2
))
343 return name1
.compareTo(name2
);
344 else if (path1
.startsWith(path2
))
346 else if (path2
.startsWith(path1
))
349 return path1
.compareTo(path2
);
350 } else if (!StringUtils
.hasText(path1
)
351 && StringUtils
.hasText(path2
)) {
353 } else if (StringUtils
.hasText(path1
)
354 && !StringUtils
.hasText(path2
)) {
356 } else if (!StringUtils
.hasText(path1
)
357 && !StringUtils
.hasText(path2
)) {
358 return name1
.compareTo(name2
);