package net.sourceforge.seqware.pipeline.plugins; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import net.sourceforge.seqware.common.module.ReturnValue; import net.sourceforge.seqware.common.util.filetools.FileTools; import net.sourceforge.seqware.pipeline.plugin.Plugin; import net.sourceforge.seqware.pipeline.plugin.PluginInterface; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import org.openide.util.lookup.ServiceProvider; @ServiceProvider(service = PluginInterface.class) public class OozieXML2Dot extends Plugin { private static final Namespace NAMESPACE = Namespace.getNamespace("uri:oozie:workflow:0.2"); private String input; private String output; private final ReturnValue ret = new ReturnValue(ReturnValue.SUCCESS); public OozieXML2Dot() { super(); parser.accepts("input").withRequiredArg().ofType(String.class).describedAs("input XML file"); parser.accepts("output").withRequiredArg().ofType(String.class).describedAs("output dot file"); } @Override public ReturnValue init() { this.input = (String) options.valueOf("input"); this.output = (String) options.valueOf("output"); if (!(FileTools.fileExistsAndNotEmpty(new File(this.input)).getExitStatus() == ReturnValue.SUCCESS)) ret.setReturnValue(ReturnValue.INVALIDFILE); return ret; } @Override public ReturnValue do_test() { return ret; } @Override public ReturnValue do_run() { try { this.parseOozie(input, output); } catch (JDOMException e) { e.printStackTrace(); ret.setReturnValue(ReturnValue.FAILURE); } catch (IOException e) { e.printStackTrace(); ret.setReturnValue(ReturnValue.FAILURE); } return ret; } @Override public ReturnValue clean_up() { return ret; } private void parseOozie(String input, String output) throws JDOMException, IOException { SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(input); Element root = doc.getRootElement(); // start Element start = root.getChild("start", NAMESPACE); DotNode rootNode = new DotNode(start.getAttributeValue("to")); this.addSubNode(rootNode, root); // construct dot file try (FileWriter fw = new FileWriter(output)) { fw.write("digraph dag {\n"); // avoid duplicated Set<String> allEdge = new HashSet<>(); this.visitNode(rootNode, fw, allEdge); fw.write("}\n"); } } private Element findElementByName(String name, Element root) { List<Element> actionelements = root.getChildren("action", NAMESPACE); for (Element element : actionelements) { if (element.getAttributeValue("name").equals(name)) return element; } List<Element> forkElements = root.getChildren("fork", NAMESPACE); for (Element element : forkElements) { if (element.getAttributeValue("name").equals(name)) return element; } List<Element> joinElements = root.getChildren("join", NAMESPACE); for (Element element : joinElements) { if (element.getAttributeValue("name").equals(name)) return element; } if (name.equals("end")) return root.getChild("end", NAMESPACE); return null; } private void addSubNode(DotNode parent, Element root) { Element element = this.findElementByName(parent.getName(), root); switch (element.getName()) { case "action": { Element child = element.getChild("ok", NAMESPACE); String okTo = child.getAttributeValue("to"); DotNode childNode = new DotNode(okTo); parent.addChild(childNode); this.addSubNode(childNode, root); break; } case "fork": List<Element> forks = element.getChildren(); for (Element fork : forks) { DotNode childNode = new DotNode(fork.getAttributeValue("start")); parent.addChild(childNode); this.addSubNode(childNode, root); } break; case "join": { DotNode childNode = new DotNode(element.getAttributeValue("to")); parent.addChild(childNode); this.addSubNode(childNode, root); break; } } } @Override public String get_description() { return "This take an input file of oozie workflow xml, and translate the relation of all actions into dot format"; } private void visitNode(DotNode node, FileWriter fw, Collection<String> all) throws IOException { for (DotNode child : node.getChildren()) { String w = node.toString() + " -> " + child.toString(); if (all.contains(w)) continue; all.add(w); fw.write(w + "\n"); this.visitNode(child, fw, all); } } class DotNode { private List<DotNode> children; private String name; public DotNode(String pid) { this.children = new ArrayList<>(); this.name = pid; } public void addChild(DotNode node) { if (!this.children.contains(node)) this.children.add(node); } @Override public String toString() { return this.name; } public List<DotNode> getChildren() { return this.children; } @Override public boolean equals(Object obj) { if (obj instanceof DotNode == false) return false; if (obj == this) return true; DotNode rhs = (DotNode) obj; return new EqualsBuilder().appendSuper(super.equals(obj)).append(this.name, rhs.name).isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37).append(this.name).toHashCode(); } public String getName() { return this.toString(); } } }