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