]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/DefaultExecutionFlow.java
afb6ddbd2b8c72f4e955ded044fb0726dbc9f22e
[gpl/argeo-slc.git] / runtime / org.argeo.slc.core / src / main / java / org / argeo / slc / core / execution / DefaultExecutionFlow.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.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.argeo.slc.SlcException;
27 import org.argeo.slc.core.structure.tree.TreeSPath;
28 import org.argeo.slc.core.structure.tree.TreeSRegistry;
29 import org.argeo.slc.execution.ExecutionFlow;
30 import org.argeo.slc.execution.ExecutionSpec;
31 import org.argeo.slc.execution.ExecutionSpecAttribute;
32 import org.argeo.slc.structure.StructureAware;
33 import org.argeo.slc.structure.StructureRegistry;
34 import org.springframework.aop.scope.ScopedObject;
35 import org.springframework.beans.factory.BeanNameAware;
36 import org.springframework.beans.factory.InitializingBean;
37 import org.springframework.validation.MapBindingResult;
38
39 /** Default implementation of an execution flow. */
40 public class DefaultExecutionFlow implements ExecutionFlow, InitializingBean,
41 BeanNameAware {
42 private final static Log log = LogFactory
43 .getLog(DefaultExecutionFlow.class);
44
45 private final ExecutionSpec executionSpec;
46 private String name = null;
47 private Map<String, Object> parameters = new HashMap<String, Object>();
48 private List<Runnable> executables = new ArrayList<Runnable>();
49
50 private String path;
51 private StructureRegistry<TreeSPath> registry = new TreeSRegistry();
52
53 private Boolean failOnError = true;
54
55 public DefaultExecutionFlow() {
56 this.executionSpec = new DefaultExecutionSpec();
57 }
58
59 public DefaultExecutionFlow(ExecutionSpec executionSpec) {
60 this.executionSpec = executionSpec;
61 }
62
63 public DefaultExecutionFlow(ExecutionSpec executionSpec,
64 Map<String, Object> parameters) {
65 // be sure to have an execution spec
66 this.executionSpec = (executionSpec == null) ? new DefaultExecutionSpec()
67 : executionSpec;
68
69 // only parameters contained in the executionSpec can be set
70 for (String parameter : parameters.keySet()) {
71 if (!executionSpec.getAttributes().containsKey(parameter)) {
72 throw new SlcException("Parameter " + parameter
73 + " is not defined in the ExecutionSpec");
74 }
75 }
76
77 // set the parameters
78 this.parameters.putAll(parameters);
79
80 // check that all the required parameters are defined
81 MapBindingResult errors = new MapBindingResult(parameters, "execution#"
82 + getName());
83 for (String key : executionSpec.getAttributes().keySet()) {
84 ExecutionSpecAttribute attr = executionSpec.getAttributes()
85 .get(key);
86
87 if (attr.getIsImmutable() && !isSetAsParameter(key)) {
88 errors.rejectValue(key, "Immutable but not set");
89 break;
90 }
91
92 if (attr.getIsConstant() && !isSetAsParameter(key)) {
93 errors.rejectValue(key, "Constant but not set as parameter");
94 break;
95 }
96
97 if (attr.getIsHidden() && !isSetAsParameter(key)) {
98 errors.rejectValue(key, "Hidden but not set as parameter");
99 break;
100 }
101 }
102
103 if (errors.hasErrors())
104 throw new SlcException("Could not prepare execution flow: "
105 + errors.toString());
106
107 }
108
109 public void run() {
110 try {
111 for (Runnable executable : executables) {
112 if (Thread.interrupted()) {
113 log.error("Flow '" + getName() + "' killed before '"
114 + executable + "'");
115 Thread.currentThread().interrupt();
116 return;
117 // throw new ThreadDeath();
118 }
119 this.doExecuteRunnable(executable);
120 }
121 } catch (RuntimeException e) {
122 if (Thread.interrupted()) {
123 log.error("Flow '" + getName()
124 + "' killed while receiving an unrelated exception", e);
125 Thread.currentThread().interrupt();
126 return;
127 // throw new ThreadDeath();
128 }
129 if (failOnError)
130 throw e;
131 else {
132 log.error("Execution flow failed,"
133 + " but process did not fail"
134 + " because failOnError property"
135 + " is set to false: " + e);
136 if (log.isTraceEnabled())
137 e.printStackTrace();
138 }
139 }
140 }
141
142 public void doExecuteRunnable(Runnable runnable) {
143 runnable.run();
144 }
145
146 @SuppressWarnings(value = { "unchecked" })
147 public void afterPropertiesSet() throws Exception {
148 if (path == null) {
149 if (name.charAt(0) == '/') {
150 path = name.substring(0, name.lastIndexOf('/'));
151 }
152 }
153
154 if (path != null) {
155 for (Runnable executable : executables) {
156 if (executable instanceof StructureAware
157 && !(executable instanceof ScopedObject)) {
158 ((StructureAware<TreeSPath>) executable).notifyCurrentPath(
159 registry, new TreeSPath(path));
160 } else if (executable instanceof DefaultExecutionFlow) {
161 // so we don't need to have DefaultExecutionFlow
162 // implementing StructureAware
163 // FIXME: probably has side effects
164 DefaultExecutionFlow flow = (DefaultExecutionFlow) executable;
165 String newPath = path + '/' + flow.getName();
166 flow.setPath(newPath);
167 log.warn(newPath + " was forcibly set on " + flow);
168 }
169 }
170 }
171 }
172
173 public void setBeanName(String name) {
174 this.name = name;
175 }
176
177 public void setExecutables(List<Runnable> executables) {
178 this.executables = executables;
179 }
180
181 public void setParameters(Map<String, Object> attributes) {
182 this.parameters = attributes;
183 }
184
185 public String getName() {
186 return name;
187 }
188
189 public ExecutionSpec getExecutionSpec() {
190 return executionSpec;
191 }
192
193 public Object getParameter(String parameterName) {
194 // Verify that there is a spec attribute
195 ExecutionSpecAttribute specAttr = null;
196 if (executionSpec.getAttributes().containsKey(parameterName)) {
197 specAttr = executionSpec.getAttributes().get(parameterName);
198 } else {
199 throw new SlcException("Key " + parameterName
200 + " is not defined in the specifications of " + toString());
201 }
202
203 if (parameters.containsKey(parameterName)) {
204 Object paramValue = parameters.get(parameterName);
205 return paramValue;
206 } else {
207 if (specAttr.getValue() != null) {
208 return specAttr.getValue();
209 }
210 }
211 throw new SlcException("Key " + parameterName
212 + " is not set as parameter in " + toString());
213 }
214
215 public Boolean isSetAsParameter(String key) {
216 return parameters.containsKey(key)
217 || (executionSpec.getAttributes().containsKey(key) && executionSpec
218 .getAttributes().get(key).getValue() != null);
219 }
220
221 @Override
222 public String toString() {
223 return new StringBuffer("Execution flow ").append(name).toString();
224 }
225
226 @Override
227 public boolean equals(Object obj) {
228 return ((ExecutionFlow) obj).getName().equals(name);
229 }
230
231 @Override
232 public int hashCode() {
233 return name.hashCode();
234 }
235
236 public String getPath() {
237 return path;
238 }
239
240 public void setPath(String path) {
241 this.path = path;
242 }
243
244 public void setRegistry(StructureRegistry<TreeSPath> registry) {
245 this.registry = registry;
246 }
247
248 public Boolean getFailOnError() {
249 return failOnError;
250 }
251
252 public void setFailOnError(Boolean failOnError) {
253 this.failOnError = failOnError;
254 }
255
256 }