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