/**
* Copyright [2011] [Datasalt Systems S.L.]
*
* 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 com.datasalt.utils.commons.flow;
import java.util.HashMap;
import java.util.Set;
import com.google.common.collect.Maps;
/**
* A class for creating flows of tasks. The current implementation
* allows for splits (forks of executions) and barriers (all tasks must
* reach the barrier before the flow continues).
* <br/>
* For creating a Task, you must implement the {@link Executable}. Some
* configuration can be given to the Executables.
* <br/>
* The current implementation does not execute splits nor barriers in
* pararell. It just execute them sequentially, but assures the correct
* execution order.
* <br/>
*
* An example of use:
* <br/>
* <code>
* [...]
*
* Flow<String> flowRoot = Flow.newFlow();
* Flow<String> sf1 = new Flow<String>().chain("c1", "c1des", exA).chain("c2", "c2des", exB);
* Flow<String> sf2 = new Flow<String>().chain("c3", "c3des", exC);
* flowRoot.split(sf1, sf2);
*
* flowRoot.execute("Config String");
*
* </code>
* <br/>
* Another example with barriers:
* <br/>
* <code>
*
* flowRoot = Flow.newFlow();
* flowRoot.barrier("Barrier", "b", sf1, sf2).chain("c4", "c4des", exA);
*
* flowRoot.execute("Config String");
* </code>
* <br/>
* For that case, the task c4 is executed only when sf1 and sf2 are fully executed.
*
* @author ivan
*
* @param <ConfigData>
*/
public class Flow<ConfigData> {
public static final String ROOT_FLOW_NAME = "Flow Root";
ConfigData config;
ChainableExecutable<ConfigData> root = null;
ChainableExecutable<ConfigData> current = null;
HashMap<String, ChainableExecutable<ConfigData>> taskByName = Maps.newHashMap();
public Flow() {
}
public Flow<ConfigData> chain(String name, String description, Executable<ConfigData> ex){
current = new ChainableExecutable<ConfigData>(name, description, current, ex);
if (root == null) {
root = current;
}
taskByName.put(name, current);
return this;
}
public Flow<ConfigData> chain(Flow<ConfigData> flow) {
if (flow.root == null) {
return this;
}
if (root == null) {
root = flow.root;
} else {
current.registerChild(flow.root);
flow.current.parent = current;
}
taskByName.putAll(flow.taskByName);
current = flow.current;
return this;
}
public Flow<ConfigData> barrier(String name, String description, Flow<ConfigData> ... flows) {
BarrierExecutable<ConfigData> barrier = new BarrierExecutable<ConfigData>(name, description, current);
current = barrier;
if (root == null) {
root = current;
}
taskByName.put(name, current);
for (Flow<ConfigData> flow: flows) {
if (flow.root != null) {
barrier.addBarriedChild(flow.root);
flow.root.parent = current;
taskByName.putAll(flow.taskByName);
}
}
return this;
}
public Flow<ConfigData> split(Flow<ConfigData> ...flows) {
if (root == null) {
root = new ChainableExecutable<ConfigData>(ROOT_FLOW_NAME, "Root split", null, null);
current = root;
taskByName.put(ROOT_FLOW_NAME, current);
}
for (Flow<ConfigData> flow : flows) {
if (flow.root != null) {
current.registerChild(flow.root);
flow.root.parent = current;
taskByName.putAll(flow.taskByName);
}
}
return this;
}
public void execute(ConfigData config) throws Exception {
if (root != null) {
root.execute(config);
}
}
public void executeFrom(String name, ConfigData config) throws Exception {
ChainableExecutable<ConfigData> node = taskByName.get(name);
if (node != null) {
node.execute(config);
}
}
public Set<String> getNames() {
return taskByName.keySet();
}
public String getDescription(String name) {
ChainableExecutable<ConfigData> node = taskByName.get(name);
if (node != null) {
return node.getDescription();
}
return "";
}
public String getHelp() {
StringBuilder sb = new StringBuilder();
for(String name: taskByName.keySet()) {
sb.append("-------\n");
ChainableExecutable<ConfigData> c = taskByName.get(name);
sb.append("Name: " + name + "\n");
ChainableExecutable<ConfigData> parent = c.getParent();
if (parent != null) {
sb.append("Parent: " + parent.getName() + "\n");
}
if (c instanceof BarrierExecutable<?>) {
for (Executable<ConfigData> bChild: ((BarrierExecutable<ConfigData>) c).barrierFlows ) {
if (bChild instanceof ChainableExecutable<?>) {
ChainableExecutable<ConfigData> ex = (ChainableExecutable<ConfigData>) bChild;
sb.append("BarrierChild: " + ex.getName() + "\n");
}
}
}
if (c instanceof BarrierExecutable<?>) {
for (Executable<ConfigData> child: ((BarrierExecutable<ConfigData>) c).childs ) {
if (child instanceof ChainableExecutable<?>) {
ChainableExecutable<ConfigData> ex = (ChainableExecutable<ConfigData>) child;
sb.append("Child: " + ex.getName() + "\n");
}
}
}
sb.append("Description: " + c.getDescription() + "\n");
}
sb.append("-------\n");
return sb.toString();
}
public String toGraphviz() {
if (root == null) {
return "";
}
GvizNode n = root.genGviz();
StringBuilder sb = new StringBuilder();
sb.append("digraph Flow {\n");
for(String edge: n.edges) {
sb.append(edge + ";\n");
}
sb.append("}\n");
return sb.toString();
}
public static<T> Flow<T> newFlow() {
return new Flow<T>();
}
public static void main(String args []) throws Exception {
}
}