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