]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java
Improve versioning driver
[gpl/argeo-slc.git] / runtime / org.argeo.slc.core / src / main / java / org / argeo / slc / core / execution / DefaultExecutionFlowDescriptorConverter.java
1 /*
2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 package org.argeo.slc.core.execution;
18
19 import java.util.Comparator;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.SortedSet;
23 import java.util.TreeMap;
24 import java.util.TreeSet;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.argeo.slc.SlcException;
29 import org.argeo.slc.UnsupportedException;
30 import org.argeo.slc.execution.ExecutionFlow;
31 import org.argeo.slc.execution.ExecutionFlowDescriptor;
32 import org.argeo.slc.execution.ExecutionFlowDescriptorConverter;
33 import org.argeo.slc.execution.ExecutionModuleDescriptor;
34 import org.argeo.slc.execution.ExecutionSpec;
35 import org.argeo.slc.execution.ExecutionSpecAttribute;
36 import org.springframework.aop.scope.ScopedObject;
37 import org.springframework.beans.BeansException;
38 import org.springframework.beans.factory.BeanFactory;
39 import org.springframework.beans.factory.config.BeanDefinition;
40 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
41 import org.springframework.context.ApplicationContext;
42 import org.springframework.context.ApplicationContextAware;
43 import org.springframework.context.ConfigurableApplicationContext;
44 import org.springframework.util.StringUtils;
45
46 /**
47 * Performs conversion in both direction between data exchanged with the agent
48 * and the data in the application context.
49 */
50 public class DefaultExecutionFlowDescriptorConverter implements
51 ExecutionFlowDescriptorConverter, ApplicationContextAware {
52 public final static String REF_VALUE_TYPE_BEAN_NAME = "beanName";
53
54 /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */
55 private final static String REF_VALUE_INTERNAL = "[internal]";
56
57 private final static Log log = LogFactory
58 .getLog(DefaultExecutionFlowDescriptorConverter.class);
59
60 private ApplicationContext applicationContext;
61
62 @SuppressWarnings("unused")
63 public Map<String, Object> convertValues(
64 ExecutionFlowDescriptor executionFlowDescriptor) {
65 Map<String, Object> values = executionFlowDescriptor.getValues();
66 Map<String, Object> convertedValues = new HashMap<String, Object>();
67 ExecutionSpec executionSpec = executionFlowDescriptor
68 .getExecutionSpec();
69
70 if (executionSpec == null && log.isTraceEnabled())
71 log.warn("Execution spec is null for " + executionFlowDescriptor);
72
73 if (values != null && executionSpec != null) {
74 values: for (String key : values.keySet()) {
75 ExecutionSpecAttribute attribute = executionSpec
76 .getAttributes().get(key);
77
78 if (attribute == null)
79 throw new SlcException("No spec attribute defined for '"
80 + key + "'");
81
82 if (attribute.getIsConstant())
83 continue values;
84
85 Object value = values.get(key);
86 if (value instanceof PrimitiveValue) {
87 PrimitiveValue primitiveValue = (PrimitiveValue) value;
88 // TODO: check class <=> type
89 convertedValues.put(key, primitiveValue.getValue());
90 } else if (value instanceof RefValue) {
91 RefValue refValue = (RefValue) value;
92 String type = refValue.getType();
93 if (REF_VALUE_TYPE_BEAN_NAME.equals(type)) {
94 // FIXME: UI should send all information about spec
95 // - targetClass
96 // - name
97 // String executionSpecName = executionSpec.getName();
98 // ExecutionSpec localSpec = (ExecutionSpec)
99 // applicationContext
100 // .getBean(executionSpecName);
101 // RefSpecAttribute localAttr = (RefSpecAttribute)
102 // localSpec
103 // .getAttributes().get(key);
104 // Class<?> targetClass = localAttr.getTargetClass();
105 //
106 // String primitiveType = PrimitiveUtils
107 // .classAsType(targetClass);
108 String primitiveType = null;
109 if (primitiveType != null) {
110 // not active
111 String ref = refValue.getRef();
112 Object obj = PrimitiveUtils.convert(primitiveType,
113 ref);
114 convertedValues.put(key, obj);
115 } else {
116 String ref = refValue.getRef();
117 if (ref != null && !ref.equals(REF_VALUE_INTERNAL)) {
118 Object obj = null;
119 if (applicationContext.containsBean(ref)) {
120 obj = applicationContext.getBean(ref);
121 } else {
122 // FIXME: hack in order to pass primitive
123 obj = ref;
124 }
125 convertedValues.put(key, obj);
126 } else {
127 log.warn("Cannot interpret " + refValue);
128 }
129 }
130 } else if (PrimitiveUtils.typeAsClass(type) != null) {
131 String ref = refValue.getRef();
132 Object obj = PrimitiveUtils.convert(type, ref);
133 convertedValues.put(key, obj);
134 } else {
135 throw new UnsupportedException("Ref value type",
136 refValue.getType());
137 }
138 } else {
139 // default is to take the value as is
140 convertedValues.put(key, value);
141 }
142 }
143 }
144 return convertedValues;
145 }
146
147 public void addFlowsToDescriptor(ExecutionModuleDescriptor md,
148 Map<String, ExecutionFlow> executionFlows) {
149 SortedSet<ExecutionFlowDescriptor> set = new TreeSet<ExecutionFlowDescriptor>(
150 new ExecutionFlowDescriptorComparator());
151 for (String name : executionFlows.keySet()) {
152 ExecutionFlow executionFlow = executionFlows.get(name);
153
154 ExecutionFlowDescriptor efd = getExecutionFlowDescriptor(executionFlow);
155 ExecutionSpec executionSpec = efd.getExecutionSpec();
156
157 // Add execution spec if necessary
158 if (!md.getExecutionSpecs().contains(executionSpec))
159 md.getExecutionSpecs().add(executionSpec);
160
161 // Add execution flow
162 set.add(efd);
163 // md.getExecutionFlows().add(efd);
164 }
165 md.getExecutionFlows().addAll(set);
166 }
167
168 @SuppressWarnings("deprecation")
169 public ExecutionFlowDescriptor getExecutionFlowDescriptor(
170 ExecutionFlow executionFlow) {
171 if (executionFlow.getName() == null)
172 throw new SlcException("Flow name is null: " + executionFlow);
173 String name = executionFlow.getName();
174
175 ExecutionSpec executionSpec = executionFlow.getExecutionSpec();
176 if (executionSpec == null)
177 throw new SlcException("Execution spec is null: " + executionFlow);
178 if (executionSpec.getName() == null)
179 throw new SlcException("Execution spec name is null: "
180 + executionSpec);
181
182 Map<String, Object> values = new TreeMap<String, Object>();
183 for (String key : executionSpec.getAttributes().keySet()) {
184 ExecutionSpecAttribute attribute = executionSpec.getAttributes()
185 .get(key);
186
187 if (attribute instanceof PrimitiveSpecAttribute) {
188 if (executionFlow.isSetAsParameter(key)) {
189 Object value = executionFlow.getParameter(key);
190 PrimitiveValue primitiveValue = new PrimitiveValue();
191 primitiveValue.setType(((PrimitiveSpecAttribute) attribute)
192 .getType());
193 primitiveValue.setValue(value);
194 values.put(key, primitiveValue);
195 } else {
196 // no need to add a primitive value if it is not set,
197 // all necessary information is in the spec
198 }
199 } else if (attribute instanceof RefSpecAttribute) {
200 if (attribute.getIsConstant()) {
201 values.put(key, new RefValue(REF_VALUE_INTERNAL));
202 } else
203 values.put(
204 key,
205 buildRefValue((RefSpecAttribute) attribute,
206 executionFlow, key));
207 } else {
208 throw new SlcException("Unkown spec attribute type "
209 + attribute.getClass());
210 }
211
212 }
213
214 ExecutionFlowDescriptor efd = new ExecutionFlowDescriptor(name, values,
215 executionSpec);
216 if (executionFlow.getPath() != null)
217 efd.setPath(executionFlow.getPath());
218 else
219 efd.setPath("");
220
221 // Takes description from spring
222 BeanFactory bf = getBeanFactory();
223 if (bf != null) {
224 BeanDefinition bd = getBeanFactory().getBeanDefinition(name);
225 efd.setDescription(bd.getDescription());
226 }
227 return efd;
228 }
229
230 @SuppressWarnings(value = { "unchecked" })
231 protected RefValue buildRefValue(RefSpecAttribute rsa,
232 ExecutionFlow executionFlow, String key) {
233 RefValue refValue = new RefValue();
234 // FIXME: UI should be able to deal with other types
235 refValue.setType(REF_VALUE_TYPE_BEAN_NAME);
236 Class<?> targetClass = rsa.getTargetClass();
237 String primitiveType = PrimitiveUtils.classAsType(targetClass);
238 if (primitiveType != null) {
239 if (executionFlow.isSetAsParameter(key)) {
240 Object value = executionFlow.getParameter(key);
241 refValue.setRef(value.toString());
242 }
243 refValue.setType(primitiveType);
244 return refValue;
245 } else {
246
247 if (executionFlow.isSetAsParameter(key)) {
248 String ref = null;
249 Object value = executionFlow.getParameter(key);
250 if (applicationContext == null) {
251 log.warn("No application context declared, cannot scan ref value.");
252 ref = value.toString();
253 } else {
254
255 // look for a ref to the value
256 Map<String, Object> beans = getBeanFactory()
257 .getBeansOfType(targetClass, false, false);
258 // TODO: also check scoped beans
259 beans: for (String beanName : beans.keySet()) {
260 Object obj = beans.get(beanName);
261 if (value instanceof ScopedObject) {
262 // don't call methods of the target of the scope
263 if (obj instanceof ScopedObject)
264 if (value == obj) {
265 ref = beanName;
266 break beans;
267 }
268 } else {
269 if (obj.equals(value)) {
270 ref = beanName;
271 break beans;
272 }
273 }
274 }
275 }
276 if (ref == null) {
277 if (log.isTraceEnabled())
278 log.trace("Cannot define reference for ref spec attribute "
279 + key
280 + " in "
281 + executionFlow
282 + " ("
283 + rsa
284 + ")."
285 + " If it is an inner bean consider put it frozen.");
286 ref = REF_VALUE_INTERNAL;
287 } else {
288 if (log.isTraceEnabled())
289 log.trace(ref
290 + " is the reference for ref spec attribute "
291 + key + " in " + executionFlow + " (" + rsa
292 + ")");
293 }
294 refValue.setRef(ref);
295 }
296 return refValue;
297 }
298 }
299
300 /** @return can be null */
301 private ConfigurableListableBeanFactory getBeanFactory() {
302 if (applicationContext == null)
303 return null;
304 return ((ConfigurableApplicationContext) applicationContext)
305 .getBeanFactory();
306 }
307
308 /** Must be use within the execution application context */
309 public void setApplicationContext(ApplicationContext applicationContext)
310 throws BeansException {
311 this.applicationContext = applicationContext;
312 }
313
314 private static class ExecutionFlowDescriptorComparator implements
315 Comparator<ExecutionFlowDescriptor> {
316 @SuppressWarnings("deprecation")
317 public int compare(ExecutionFlowDescriptor o1,
318 ExecutionFlowDescriptor o2) {
319 // TODO: write unit tests for this
320
321 String name1 = o1.getName();
322 String name2 = o2.getName();
323
324 String path1 = o1.getPath();
325 String path2 = o2.getPath();
326
327 // Check whether name include path
328 int lastIndex1 = name1.lastIndexOf('/');
329 // log.debug(name1+", "+lastIndex1);
330 if (!StringUtils.hasText(path1) && lastIndex1 >= 0) {
331 path1 = name1.substring(0, lastIndex1);
332 name1 = name1.substring(lastIndex1 + 1);
333 }
334
335 int lastIndex2 = name2.lastIndexOf('/');
336 if (!StringUtils.hasText(path2) && lastIndex2 >= 0) {
337 path2 = name2.substring(0, lastIndex2);
338 name2 = name2.substring(lastIndex2 + 1);
339 }
340
341 // Perform the actual comparison
342 if (StringUtils.hasText(path1) && StringUtils.hasText(path2)) {
343 if (path1.equals(path2))
344 return name1.compareTo(name2);
345 else if (path1.startsWith(path2))
346 return -1;
347 else if (path2.startsWith(path1))
348 return 1;
349 else
350 return path1.compareTo(path2);
351 } else if (!StringUtils.hasText(path1)
352 && StringUtils.hasText(path2)) {
353 return 1;
354 } else if (StringUtils.hasText(path1)
355 && !StringUtils.hasText(path2)) {
356 return -1;
357 } else if (!StringUtils.hasText(path1)
358 && !StringUtils.hasText(path2)) {
359 return name1.compareTo(name2);
360 } else {
361 return 0;
362 }
363 }
364
365 }
366 }