]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/DefaultExecutionFlowDescriptorConverter.java
Document and improve execution model
[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.config.BeanDefinition;
39 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
40 import org.springframework.context.ApplicationContext;
41 import org.springframework.context.ApplicationContextAware;
42 import org.springframework.context.ConfigurableApplicationContext;
43 import org.springframework.util.Assert;
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 public Map<String, Object> convertValues(
63 ExecutionFlowDescriptor executionFlowDescriptor) {
64 Map<String, Object> values = executionFlowDescriptor.getValues();
65 Map<String, Object> convertedValues = new HashMap<String, Object>();
66 ExecutionSpec executionSpec = executionFlowDescriptor
67 .getExecutionSpec();
68
69 if (executionSpec == null && log.isTraceEnabled())
70 log.warn("Execution spec is null for " + executionFlowDescriptor);
71
72 if (values != null && executionSpec != null) {
73 values: for (String key : values.keySet()) {
74 ExecutionSpecAttribute attribute = executionSpec
75 .getAttributes().get(key);
76
77 if (attribute.getIsFrozen())
78 continue values;
79
80 Object value = values.get(key);
81 if (value instanceof PrimitiveValue) {
82 PrimitiveValue primitiveValue = (PrimitiveValue) value;
83 // TODO: check class <=> type
84 convertedValues.put(key, primitiveValue.getValue());
85 } else if (value instanceof RefValue) {
86 RefValue refValue = (RefValue) value;
87 String type = refValue.getType();
88 if (REF_VALUE_TYPE_BEAN_NAME.equals(type)) {
89 // FIXME: UI should send all information about spec
90 // - targetClass
91 // - name
92 // String executionSpecName = executionSpec.getName();
93 // ExecutionSpec localSpec = (ExecutionSpec)
94 // applicationContext
95 // .getBean(executionSpecName);
96 // RefSpecAttribute localAttr = (RefSpecAttribute)
97 // localSpec
98 // .getAttributes().get(key);
99 // Class<?> targetClass = localAttr.getTargetClass();
100 //
101 // String primitiveType = PrimitiveUtils
102 // .classAsType(targetClass);
103 String primitiveType = null;
104 if (primitiveType != null) {
105 // not active
106 String ref = refValue.getRef();
107 Object obj = PrimitiveUtils.convert(primitiveType,
108 ref);
109 convertedValues.put(key, obj);
110 } else {
111 String ref = refValue.getRef();
112 if (ref != null && !ref.equals(REF_VALUE_INTERNAL)) {
113 Object obj = null;
114 if (applicationContext.containsBean(ref)) {
115 obj = applicationContext.getBean(ref);
116 } else {
117 // FIXME: hack in order to pass primitive
118 obj = ref;
119 }
120 convertedValues.put(key, obj);
121 } else {
122 log.warn("Cannot interpret " + refValue);
123 }
124 }
125 } else if (PrimitiveUtils.typeAsClass(type) != null) {
126 String ref = refValue.getRef();
127 Object obj = PrimitiveUtils.convert(type, ref);
128 convertedValues.put(key, obj);
129 } else {
130 throw new UnsupportedException("Ref value type",
131 refValue.getType());
132 }
133 }
134 }
135 }
136 return convertedValues;
137 }
138
139 public void addFlowsToDescriptor(ExecutionModuleDescriptor md,
140 Map<String, ExecutionFlow> executionFlows) {
141 SortedSet<ExecutionFlowDescriptor> set = new TreeSet<ExecutionFlowDescriptor>(
142 new ExecutionFlowDescriptorComparator());
143 for (String name : executionFlows.keySet()) {
144 ExecutionFlow executionFlow = executionFlows.get(name);
145
146 ExecutionFlowDescriptor efd = getExecutionFlowDescriptor(executionFlow);
147 ExecutionSpec executionSpec = efd.getExecutionSpec();
148
149 // Add execution spec if necessary
150 if (!md.getExecutionSpecs().contains(executionSpec))
151 md.getExecutionSpecs().add(executionSpec);
152
153 // Add execution flow
154 set.add(efd);
155 // md.getExecutionFlows().add(efd);
156 }
157 md.getExecutionFlows().addAll(set);
158 }
159
160 public ExecutionFlowDescriptor getExecutionFlowDescriptor(
161 ExecutionFlow executionFlow) {
162 Assert.notNull(executionFlow.getName());
163 String name = executionFlow.getName();
164
165 ExecutionSpec executionSpec = executionFlow.getExecutionSpec();
166 Assert.notNull(executionSpec);
167 Assert.notNull(executionSpec.getName());
168
169 Map<String, Object> values = new TreeMap<String, Object>();
170 for (String key : executionSpec.getAttributes().keySet()) {
171 ExecutionSpecAttribute attribute = executionSpec.getAttributes()
172 .get(key);
173
174 if (attribute instanceof PrimitiveSpecAttribute) {
175 if (executionFlow.isSetAsParameter(key)) {
176 Object value = executionFlow.getParameter(key);
177 PrimitiveValue primitiveValue = new PrimitiveValue();
178 primitiveValue.setType(((PrimitiveSpecAttribute) attribute)
179 .getType());
180 primitiveValue.setValue(value);
181 values.put(key, primitiveValue);
182 } else {
183 // no need to add a primitive value if it is not set,
184 // all necessary information is in the spec
185 }
186 } else if (attribute instanceof RefSpecAttribute) {
187 if (attribute.getIsFrozen()) {
188 values.put(key, new RefValue(REF_VALUE_INTERNAL));
189 } else
190 values.put(
191 key,
192 buildRefValue((RefSpecAttribute) attribute,
193 executionFlow, key));
194 } else {
195 throw new SlcException("Unkown spec attribute type "
196 + attribute.getClass());
197 }
198
199 }
200
201 ExecutionFlowDescriptor efd = new ExecutionFlowDescriptor(name, values,
202 executionSpec);
203 if (executionFlow.getPath() != null)
204 efd.setPath(executionFlow.getPath());
205 else
206 efd.setPath("");
207
208 // Takes description from spring
209 BeanDefinition bd = getBeanFactory().getBeanDefinition(name);
210 efd.setDescription(bd.getDescription());
211 return efd;
212 }
213
214 @SuppressWarnings(value = { "unchecked" })
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, Object> 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 log.warn("Cannot define reference for ref spec attribute "
262 + key + " in " + executionFlow + " (" + rsa + ")."
263 + " If it is an inner bean consider put it frozen.");
264 ref = REF_VALUE_INTERNAL;
265 } else {
266 if (log.isTraceEnabled())
267 log.trace(ref
268 + " is the reference for ref spec attribute "
269 + key + " in " + executionFlow + " (" + rsa
270 + ")");
271 }
272 refValue.setRef(ref);
273 }
274 return refValue;
275 }
276 }
277
278 private ConfigurableListableBeanFactory getBeanFactory() {
279 return ((ConfigurableApplicationContext) applicationContext)
280 .getBeanFactory();
281 }
282
283 /** Must be use within the execution application context */
284 public void setApplicationContext(ApplicationContext applicationContext)
285 throws BeansException {
286 this.applicationContext = applicationContext;
287 }
288
289 private static class ExecutionFlowDescriptorComparator implements
290 Comparator<ExecutionFlowDescriptor> {
291 public int compare(ExecutionFlowDescriptor o1,
292 ExecutionFlowDescriptor o2) {
293 // TODO: write unit tests for this
294
295 String name1 = o1.getName();
296 String name2 = o2.getName();
297
298 String path1 = o1.getPath();
299 String path2 = o2.getPath();
300
301 // Check whether name include path
302 int lastIndex1 = name1.lastIndexOf('/');
303 // log.debug(name1+", "+lastIndex1);
304 if (!StringUtils.hasText(path1) && lastIndex1 >= 0) {
305 path1 = name1.substring(0, lastIndex1);
306 name1 = name1.substring(lastIndex1 + 1);
307 }
308
309 int lastIndex2 = name2.lastIndexOf('/');
310 if (!StringUtils.hasText(path2) && lastIndex2 >= 0) {
311 path2 = name2.substring(0, lastIndex2);
312 name2 = name2.substring(lastIndex2 + 1);
313 }
314
315 // Perform the actual comparison
316 if (StringUtils.hasText(path1) && StringUtils.hasText(path2)) {
317 if (path1.equals(path2))
318 return name1.compareTo(name2);
319 else if (path1.startsWith(path2))
320 return -1;
321 else if (path2.startsWith(path1))
322 return 1;
323 else
324 return path1.compareTo(path2);
325 } else if (!StringUtils.hasText(path1)
326 && StringUtils.hasText(path2)) {
327 return 1;
328 } else if (StringUtils.hasText(path1)
329 && !StringUtils.hasText(path2)) {
330 return -1;
331 } else if (!StringUtils.hasText(path1)
332 && !StringUtils.hasText(path2)) {
333 return name1.compareTo(name2);
334 } else {
335 return 0;
336 }
337 }
338
339 }
340 }