]> git.argeo.org Git - gpl/argeo-slc.git/blob - legacy/org.argeo.slc.spring/src/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java
Merge remote-tracking branch 'origin/master' into testing
[gpl/argeo-slc.git] / legacy / org.argeo.slc.spring / src / org / argeo / slc / core / execution / DefaultExecutionFlowDescriptorConverter.java
1 package org.argeo.slc.core.execution;
2
3 import java.util.Comparator;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.SortedSet;
7 import java.util.TreeMap;
8 import java.util.TreeSet;
9
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;
33
34 /**
35 * Performs conversion in both direction between data exchanged with the agent
36 * and the data in the application context.
37 */
38 public class DefaultExecutionFlowDescriptorConverter implements
39 ExecutionFlowDescriptorConverter, ApplicationContextAware {
40 public final static String REF_VALUE_TYPE_BEAN_NAME = "beanName";
41
42 /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */
43 private final static String REF_VALUE_INTERNAL = "[internal]";
44
45 private final static Log log = LogFactory
46 .getLog(DefaultExecutionFlowDescriptorConverter.class);
47
48 private ApplicationContext applicationContext;
49
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
56 .getExecutionSpec();
57
58 if (executionSpec == null && log.isTraceEnabled())
59 log.warn("Execution spec is null for " + executionFlowDescriptor);
60
61 if (values != null && executionSpec != null) {
62 values: for (String key : values.keySet()) {
63 ExecutionSpecAttribute attribute = executionSpec
64 .getAttributes().get(key);
65
66 if (attribute == null)
67 throw new FlowConfigurationException(
68 "No spec attribute defined for '" + key + "'");
69
70 if (attribute.getIsConstant())
71 continue values;
72
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
83 // - targetClass
84 // - name
85 // String executionSpecName = executionSpec.getName();
86 // ExecutionSpec localSpec = (ExecutionSpec)
87 // applicationContext
88 // .getBean(executionSpecName);
89 // RefSpecAttribute localAttr = (RefSpecAttribute)
90 // localSpec
91 // .getAttributes().get(key);
92 // Class<?> targetClass = localAttr.getTargetClass();
93 //
94 // String primitiveType = PrimitiveUtils
95 // .classAsType(targetClass);
96 String primitiveType = null;
97 if (primitiveType != null) {
98 // not active
99 String ref = refValue.getRef();
100 Object obj = PrimitiveUtils.convert(primitiveType,
101 ref);
102 convertedValues.put(key, obj);
103 } else {
104 String ref = refValue.getRef();
105 if (ref != null && !ref.equals(REF_VALUE_INTERNAL)) {
106 Object obj = null;
107 if (applicationContext.containsBean(ref)) {
108 obj = applicationContext.getBean(ref);
109 } else {
110 // FIXME: hack in order to pass primitive
111 obj = ref;
112 }
113 convertedValues.put(key, obj);
114 } else {
115 log.warn("Cannot interpret " + refValue);
116 }
117 }
118 } else if (PrimitiveUtils.typeAsClass(type) != null) {
119 String ref = refValue.getRef();
120 Object obj = PrimitiveUtils.convert(type, ref);
121 convertedValues.put(key, obj);
122 } else {
123 throw new FlowConfigurationException(
124 "Ref value type not supported: "
125 + refValue.getType());
126 }
127 } else {
128 // default is to take the value as is
129 convertedValues.put(key, value);
130 }
131 }
132 }
133 return convertedValues;
134 }
135
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);
142
143 ExecutionFlowDescriptor efd = getExecutionFlowDescriptor(executionFlow);
144 ExecutionSpec executionSpec = efd.getExecutionSpec();
145
146 // Add execution spec if necessary
147 if (!md.getExecutionSpecs().contains(executionSpec))
148 md.getExecutionSpecs().add(executionSpec);
149
150 // Add execution flow
151 set.add(efd);
152 // md.getExecutionFlows().add(efd);
153 }
154 md.getExecutionFlows().addAll(set);
155 }
156
157 public ExecutionFlowDescriptor getExecutionFlowDescriptor(
158 ExecutionFlow executionFlow) {
159 if (executionFlow.getName() == null)
160 throw new FlowConfigurationException("Flow name is null: "
161 + executionFlow);
162 String name = executionFlow.getName();
163
164 ExecutionSpec executionSpec = executionFlow.getExecutionSpec();
165 if (executionSpec == null)
166 throw new FlowConfigurationException("Execution spec is null: "
167 + executionFlow);
168 if (executionSpec.getName() == null)
169 throw new FlowConfigurationException(
170 "Execution spec name is null: " + executionSpec);
171
172 Map<String, Object> values = new TreeMap<String, Object>();
173 for (String key : executionSpec.getAttributes().keySet()) {
174 ExecutionSpecAttribute attribute = executionSpec.getAttributes()
175 .get(key);
176
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)
182 .getType());
183 primitiveValue.setValue(value);
184 values.put(key, primitiveValue);
185 } else {
186 // no need to add a primitive value if it is not set,
187 // all necessary information is in the spec
188 }
189 } else if (attribute instanceof RefSpecAttribute) {
190 if (attribute.getIsConstant()) {
191 values.put(key, new RefValue(REF_VALUE_INTERNAL));
192 } else
193 values.put(
194 key,
195 buildRefValue((RefSpecAttribute) attribute,
196 executionFlow, key));
197 } else {
198 throw new FlowConfigurationException(
199 "Unkown spec attribute type " + attribute.getClass());
200 }
201
202 }
203
204 ExecutionFlowDescriptor efd = new ExecutionFlowDescriptor(name, null,
205 values, executionSpec);
206 // Takes description from spring
207 BeanFactory bf = getBeanFactory();
208 if (bf != null) {
209 BeanDefinition bd = getBeanFactory().getBeanDefinition(name);
210 efd.setDescription(bd.getDescription());
211 }
212 return efd;
213 }
214
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());
226 }
227 refValue.setType(primitiveType);
228 return refValue;
229 } else {
230
231 if (executionFlow.isSetAsParameter(key)) {
232 String ref = null;
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();
237 } else {
238
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)
248 if (value == obj) {
249 ref = beanName;
250 break beans;
251 }
252 } else {
253 if (obj.equals(value)) {
254 ref = beanName;
255 break beans;
256 }
257 }
258 }
259 }
260 if (ref == null) {
261 if (log.isTraceEnabled())
262 log.trace("Cannot define reference for ref spec attribute "
263 + key
264 + " in "
265 + executionFlow
266 + " ("
267 + rsa
268 + ")."
269 + " If it is an inner bean consider put it frozen.");
270 ref = REF_VALUE_INTERNAL;
271 } else {
272 if (log.isTraceEnabled())
273 log.trace(ref
274 + " is the reference for ref spec attribute "
275 + key + " in " + executionFlow + " (" + rsa
276 + ")");
277 }
278 refValue.setRef(ref);
279 }
280 return refValue;
281 }
282 }
283
284 /** @return can be null */
285 private ConfigurableListableBeanFactory getBeanFactory() {
286 if (applicationContext == null)
287 return null;
288 return ((ConfigurableApplicationContext) applicationContext)
289 .getBeanFactory();
290 }
291
292 /** Must be use within the execution application context */
293 public void setApplicationContext(ApplicationContext applicationContext)
294 throws BeansException {
295 this.applicationContext = applicationContext;
296 }
297
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
304
305 String name1 = o1.getName();
306 String name2 = o2.getName();
307
308 String path1 = o1.getPath();
309 String path2 = o2.getPath();
310
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);
317 }
318
319 int lastIndex2 = name2.lastIndexOf('/');
320 if (!StringUtils.hasText(path2) && lastIndex2 >= 0) {
321 path2 = name2.substring(0, lastIndex2);
322 name2 = name2.substring(lastIndex2 + 1);
323 }
324
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))
330 return -1;
331 else if (path2.startsWith(path1))
332 return 1;
333 else
334 return path1.compareTo(path2);
335 } else if (!StringUtils.hasText(path1)
336 && StringUtils.hasText(path2)) {
337 return 1;
338 } else if (StringUtils.hasText(path1)
339 && !StringUtils.hasText(path2)) {
340 return -1;
341 } else if (!StringUtils.hasText(path1)
342 && !StringUtils.hasText(path2)) {
343 return name1.compareTo(name2);
344 } else {
345 return 0;
346 }
347 }
348
349 }
350 }