]> git.argeo.org Git - gpl/argeo-slc.git/blobdiff - 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
diff --git a/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java b/org.argeo.slc.runtime/src/org/argeo/slc/runtime/DefaultExecutionFlow.java
new file mode 100644 (file)
index 0000000..289f660
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.argeo.slc.runtime;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.slc.SlcException;
+import org.argeo.slc.execution.ExecutionContext;
+import org.argeo.slc.execution.ExecutionFlow;
+import org.argeo.slc.execution.ExecutionSpec;
+import org.argeo.slc.execution.ExecutionSpecAttribute;
+
+/** Default implementation of an execution flow. */
+public class DefaultExecutionFlow implements ExecutionFlow {
+       private final static Log log = LogFactory.getLog(DefaultExecutionFlow.class);
+
+       private final ExecutionSpec executionSpec;
+       private String name = null;
+       private Map<String, Object> parameters = new HashMap<String, Object>();
+       private List<Runnable> executables = new ArrayList<Runnable>();
+
+       private String path;
+
+       private Boolean failOnError = true;
+
+       // Only needed if stacked execution flows are used
+       private ExecutionContext executionContext = null;
+
+       public DefaultExecutionFlow() {
+               this.executionSpec = new DefaultExecutionSpec();
+       }
+
+       public DefaultExecutionFlow(ExecutionSpec executionSpec) {
+               this.executionSpec = executionSpec;
+       }
+
+       public DefaultExecutionFlow(ExecutionSpec executionSpec, Map<String, Object> parameters) {
+               // be sure to have an execution spec
+               this.executionSpec = (executionSpec == null) ? new DefaultExecutionSpec() : executionSpec;
+
+               // only parameters contained in the executionSpec can be set
+               for (String parameter : parameters.keySet()) {
+                       if (!executionSpec.getAttributes().containsKey(parameter)) {
+                               throw new SlcException("Parameter " + parameter + " is not defined in the ExecutionSpec");
+                       }
+               }
+
+               // set the parameters
+               this.parameters.putAll(parameters);
+
+               // check that all the required parameters are defined
+//             MapBindingResult errors = new MapBindingResult(parameters, "execution#"
+//                             + getName());
+               Map<String, String> errors = new HashMap<>();
+               for (String key : executionSpec.getAttributes().keySet()) {
+                       ExecutionSpecAttribute attr = executionSpec.getAttributes().get(key);
+
+                       if (attr.getIsImmutable() && !isSetAsParameter(key)) {
+                               errors.put(key, "Immutable but not set");
+                               break;
+                       }
+
+                       if (attr.getIsConstant() && !isSetAsParameter(key)) {
+                               errors.put(key, "Constant but not set as parameter");
+                               break;
+                       }
+
+                       if (attr.getIsHidden() && !isSetAsParameter(key)) {
+                               errors.put(key, "Hidden but not set as parameter");
+                               break;
+                       }
+               }
+
+               if (!errors.isEmpty())
+                       throw new SlcException("Could not prepare execution flow: " + errors.toString());
+
+       }
+
+       public void run() {
+               try {
+                       for (Runnable executable : executables) {
+                               if (Thread.interrupted()) {
+                                       log.error("Flow '" + getName() + "' killed before '" + executable + "'");
+                                       Thread.currentThread().interrupt();
+                                       return;
+                                       // throw new ThreadDeath();
+                               }
+                               this.doExecuteRunnable(executable);
+                       }
+               } catch (RuntimeException e) {
+                       if (Thread.interrupted()) {
+                               log.error("Flow '" + getName() + "' killed while receiving an unrelated exception", e);
+                               Thread.currentThread().interrupt();
+                               return;
+                               // throw new ThreadDeath();
+                       }
+                       if (failOnError)
+                               throw e;
+                       else {
+                               log.error("Execution flow failed," + " but process did not fail" + " because failOnError property"
+                                               + " is set to false: " + e);
+                               if (log.isTraceEnabled())
+                                       e.printStackTrace();
+                       }
+               }
+       }
+
+       /**
+        * List sub-runnables that would be executed if run() method would be called.
+        */
+       public Iterator<Runnable> runnables() {
+               return executables.iterator();
+       }
+
+       /**
+        * If there is one and only one runnable wrapped return it, throw an exeception
+        * otherwise.
+        */
+       public Runnable getRunnable() {
+               if (executables.size() == 1)
+                       return executables.get(0);
+               else
+                       throw new SlcException("There are " + executables.size() + " runnables in flow " + getName());
+       }
+
+       public void doExecuteRunnable(Runnable runnable) {
+               try {
+                       if (executionContext != null)
+                               if (runnable instanceof ExecutionFlow)
+                                       executionContext.beforeFlow((ExecutionFlow) runnable);
+                       runnable.run();
+               } finally {
+                       if (executionContext != null)
+                               if (runnable instanceof ExecutionFlow)
+                                       executionContext.afterFlow((ExecutionFlow) runnable);
+               }
+       }
+
+       public void init() throws Exception {
+               if (path == null) {
+                       if (name.charAt(0) == '/') {
+                               path = name.substring(0, name.lastIndexOf('/'));
+                       }
+               }
+
+               if (path != null) {
+                       for (Runnable executable : executables) {
+                               if (executable instanceof DefaultExecutionFlow) {
+                                       // so we don't need to have DefaultExecutionFlow
+                                       // implementing StructureAware
+                                       // FIXME: probably has side effects
+                                       DefaultExecutionFlow flow = (DefaultExecutionFlow) executable;
+                                       String newPath = path + '/' + flow.getName();
+                                       flow.setPath(newPath);
+                                       log.warn(newPath + " was forcibly set on " + flow);
+                               }
+                       }
+               }
+       }
+
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       public void setExecutables(List<Runnable> executables) {
+               this.executables = executables;
+       }
+
+       public void setParameters(Map<String, Object> attributes) {
+               this.parameters = attributes;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public ExecutionSpec getExecutionSpec() {
+               return executionSpec;
+       }
+
+       public Object getParameter(String parameterName) {
+               // Verify that there is a spec attribute
+               ExecutionSpecAttribute specAttr = null;
+               if (executionSpec.getAttributes().containsKey(parameterName)) {
+                       specAttr = executionSpec.getAttributes().get(parameterName);
+               } else {
+                       throw new SlcException("Key " + parameterName + " is not defined in the specifications of " + toString());
+               }
+
+               if (parameters.containsKey(parameterName)) {
+                       Object paramValue = parameters.get(parameterName);
+                       return paramValue;
+               } else {
+                       if (specAttr.getValue() != null) {
+                               return specAttr.getValue();
+                       }
+               }
+               throw new SlcException("Key " + parameterName + " is not set as parameter in " + toString());
+       }
+
+       public Boolean isSetAsParameter(String key) {
+               return parameters.containsKey(key) || (executionSpec.getAttributes().containsKey(key)
+                               && executionSpec.getAttributes().get(key).getValue() != null);
+       }
+
+       @Override
+       public String toString() {
+               return new StringBuffer("Execution flow ").append(name).toString();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               return ((ExecutionFlow) obj).getName().equals(name);
+       }
+
+       @Override
+       public int hashCode() {
+               return name.hashCode();
+       }
+
+       public String getPath() {
+               return path;
+       }
+
+       public void setPath(String path) {
+               this.path = path;
+       }
+
+       public Boolean getFailOnError() {
+               return failOnError;
+       }
+
+       public void setFailOnError(Boolean failOnError) {
+               this.failOnError = failOnError;
+       }
+
+       public void setExecutionContext(ExecutionContext executionContext) {
+               this.executionContext = executionContext;
+       }
+
+}