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