]> git.argeo.org Git - gpl/argeo-slc.git/blob - org.argeo.slc.spring/src/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java
Re-activate Spring runtime unit tests.
[gpl/argeo-slc.git] / org.argeo.slc.spring / src / org / argeo / slc / core / execution / DefaultExecutionFlowDescriptorConverter.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
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 package org.argeo.slc.core.execution;
17
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.SortedSet;
22 import java.util.TreeMap;
23 import java.util.TreeSet;
24
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;
48
49 /**
50 * Performs conversion in both direction between data exchanged with the agent
51 * and the data in the application context.
52 */
53 public class DefaultExecutionFlowDescriptorConverter implements
54 ExecutionFlowDescriptorConverter, ApplicationContextAware {
55 public final static String REF_VALUE_TYPE_BEAN_NAME = "beanName";
56
57 /** Workaround for https://www.spartadn.com/bugzilla/show_bug.cgi?id=206 */
58 private final static String REF_VALUE_INTERNAL = "[internal]";
59
60 private final static Log log = LogFactory
61 .getLog(DefaultExecutionFlowDescriptorConverter.class);
62
63 private ApplicationContext applicationContext;
64
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
71 .getExecutionSpec();
72
73 if (executionSpec == null && log.isTraceEnabled())
74 log.warn("Execution spec is null for " + executionFlowDescriptor);
75
76 if (values != null && executionSpec != null) {
77 values: for (String key : values.keySet()) {
78 ExecutionSpecAttribute attribute = executionSpec
79 .getAttributes().get(key);
80
81 if (attribute == null)
82 throw new FlowConfigurationException(
83 "No spec attribute defined for '" + key + "'");
84
85 if (attribute.getIsConstant())
86 continue values;
87
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
98 // - targetClass
99 // - name
100 // String executionSpecName = executionSpec.getName();
101 // ExecutionSpec localSpec = (ExecutionSpec)
102 // applicationContext
103 // .getBean(executionSpecName);
104 // RefSpecAttribute localAttr = (RefSpecAttribute)
105 // localSpec
106 // .getAttributes().get(key);
107 // Class<?> targetClass = localAttr.getTargetClass();
108 //
109 // String primitiveType = PrimitiveUtils
110 // .classAsType(targetClass);
111 String primitiveType = null;
112 if (primitiveType != null) {
113 // not active
114 String ref = refValue.getRef();
115 Object obj = PrimitiveUtils.convert(primitiveType,
116 ref);
117 convertedValues.put(key, obj);
118 } else {
119 String ref = refValue.getRef();
120 if (ref != null && !ref.equals(REF_VALUE_INTERNAL)) {
121 Object obj = null;
122 if (applicationContext.containsBean(ref)) {
123 obj = applicationContext.getBean(ref);
124 } else {
125 // FIXME: hack in order to pass primitive
126 obj = ref;
127 }
128 convertedValues.put(key, obj);
129 } else {
130 log.warn("Cannot interpret " + refValue);
131 }
132 }
133 } else if (PrimitiveUtils.typeAsClass(type) != null) {
134 String ref = refValue.getRef();
135 Object obj = PrimitiveUtils.convert(type, ref);
136 convertedValues.put(key, obj);
137 } else {
138 throw new FlowConfigurationException(
139 "Ref value type not supported: "
140 + refValue.getType());
141 }
142 } else {
143 // default is to take the value as is
144 convertedValues.put(key, value);
145 }
146 }
147 }
148 return convertedValues;
149 }
150
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);
157
158 ExecutionFlowDescriptor efd = getExecutionFlowDescriptor(executionFlow);
159 ExecutionSpec executionSpec = efd.getExecutionSpec();
160
161 // Add execution spec if necessary
162 if (!md.getExecutionSpecs().contains(executionSpec))
163 md.getExecutionSpecs().add(executionSpec);
164
165 // Add execution flow
166 set.add(efd);
167 // md.getExecutionFlows().add(efd);
168 }
169 md.getExecutionFlows().addAll(set);
170 }
171
172 public ExecutionFlowDescriptor getExecutionFlowDescriptor(
173 ExecutionFlow executionFlow) {
174 if (executionFlow.getName() == null)
175 throw new FlowConfigurationException("Flow name is null: "
176 + executionFlow);
177 String name = executionFlow.getName();
178
179 ExecutionSpec executionSpec = executionFlow.getExecutionSpec();
180 if (executionSpec == null)
181 throw new FlowConfigurationException("Execution spec is null: "
182 + executionFlow);
183 if (executionSpec.getName() == null)
184 throw new FlowConfigurationException(
185 "Execution spec name is null: " + executionSpec);
186
187 Map<String, Object> values = new TreeMap<String, Object>();
188 for (String key : executionSpec.getAttributes().keySet()) {
189 ExecutionSpecAttribute attribute = executionSpec.getAttributes()
190 .get(key);
191
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)
197 .getType());
198 primitiveValue.setValue(value);
199 values.put(key, primitiveValue);
200 } else {
201 // no need to add a primitive value if it is not set,
202 // all necessary information is in the spec
203 }
204 } else if (attribute instanceof RefSpecAttribute) {
205 if (attribute.getIsConstant()) {
206 values.put(key, new RefValue(REF_VALUE_INTERNAL));
207 } else
208 values.put(
209 key,
210 buildRefValue((RefSpecAttribute) attribute,
211 executionFlow, key));
212 } else {
213 throw new FlowConfigurationException(
214 "Unkown spec attribute type " + attribute.getClass());
215 }
216
217 }
218
219 ExecutionFlowDescriptor efd = new ExecutionFlowDescriptor(name, null,
220 values, executionSpec);
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 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());
241 }
242 refValue.setType(primitiveType);
243 return refValue;
244 } else {
245
246 if (executionFlow.isSetAsParameter(key)) {
247 String ref = null;
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();
252 } else {
253
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)
263 if (value == obj) {
264 ref = beanName;
265 break beans;
266 }
267 } else {
268 if (obj.equals(value)) {
269 ref = beanName;
270 break beans;
271 }
272 }
273 }
274 }
275 if (ref == null) {
276 if (log.isTraceEnabled())
277 log.trace("Cannot define reference for ref spec attribute "
278 + key
279 + " in "
280 + executionFlow
281 + " ("
282 + rsa
283 + ")."
284 + " If it is an inner bean consider put it frozen.");
285 ref = REF_VALUE_INTERNAL;
286 } else {
287 if (log.isTraceEnabled())
288 log.trace(ref
289 + " is the reference for ref spec attribute "
290 + key + " in " + executionFlow + " (" + rsa
291 + ")");
292 }
293 refValue.setRef(ref);
294 }
295 return refValue;
296 }
297 }
298
299 /** @return can be null */
300 private ConfigurableListableBeanFactory getBeanFactory() {
301 if (applicationContext == null)
302 return null;
303 return ((ConfigurableApplicationContext) applicationContext)
304 .getBeanFactory();
305 }
306
307 /** Must be use within the execution application context */
308 public void setApplicationContext(ApplicationContext applicationContext)
309 throws BeansException {
310 this.applicationContext = applicationContext;
311 }
312
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
319
320 String name1 = o1.getName();
321 String name2 = o2.getName();
322
323 String path1 = o1.getPath();
324 String path2 = o2.getPath();
325
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);
332 }
333
334 int lastIndex2 = name2.lastIndexOf('/');
335 if (!StringUtils.hasText(path2) && lastIndex2 >= 0) {
336 path2 = name2.substring(0, lastIndex2);
337 name2 = name2.substring(lastIndex2 + 1);
338 }
339
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))
345 return -1;
346 else if (path2.startsWith(path1))
347 return 1;
348 else
349 return path1.compareTo(path2);
350 } else if (!StringUtils.hasText(path1)
351 && StringUtils.hasText(path2)) {
352 return 1;
353 } else if (StringUtils.hasText(path1)
354 && !StringUtils.hasText(path2)) {
355 return -1;
356 } else if (!StringUtils.hasText(path1)
357 && !StringUtils.hasText(path2)) {
358 return name1.compareTo(name2);
359 } else {
360 return 0;
361 }
362 }
363
364 }
365 }