package LONI.visitor;
import java.util.ArrayList;
import java.util.UUID;
import java.util.List;
import org.apache.commons.lang3.StringEscapeUtils;
import Core.Pair;
import LONI.tree.*;
import LONI.tree.GraphObject.DataModule;
import LONI.tree.GraphObject.GraphObject;
import LONI.tree.GraphObject.Module;
import LONI.tree.GraphObject.ModuleGroup;
import LONI.tree.module.Output;
import LONI.tree.module.Parameter;
import LONI.tree.module.Value;
import LONI.tree.module.Values;
import LONI.tree.workflow.Connection;
import LONI.tree.workflow.Connections;
import LONI.tree.workflow.Pipeline;
import Taverna.Tree.Annotation.*;
import Taverna.Tree.Annotation.Annotations;
import Taverna.Tree.DataFlowImpl.AnnotatedGranularDepthInputPorts;
import Taverna.Tree.DataFlowImpl.AnnotatedGranularDepthOutputPorts;
import Taverna.Tree.DataFlowImpl.AnnotatedGranularDepthPort;
import Taverna.Tree.DataFlowImpl.AnnotatedGranularDepthInputPorts;
import Taverna.Tree.DataFlowImpl.AnnotatedGranularDepthOutputPorts;
import Taverna.Tree.DataFlowImpl.AnnotatedPort;
import Taverna.Tree.DataFlowImpl.AnnotatedPorts;
import Taverna.Tree.DataFlowImpl.DataLink;
import Taverna.Tree.DataFlowImpl.Dataflow;
import Taverna.Tree.DataFlowImpl.Datalinks;
import Taverna.Tree.DataFlowImpl.DepthPort;
import Taverna.Tree.DataFlowImpl.GranularDepthPort;
import Taverna.Tree.DataFlowImpl.Link;
import Taverna.Tree.DataFlowImpl.PortProduct;
import Taverna.Tree.Processor.Activities;
import Taverna.Tree.Processor.Activity;
import Taverna.Tree.Processor.ActivityInputPortDefinitionBean;
import Taverna.Tree.Processor.ActivityOutputPortDefinitionBean;
import Taverna.Tree.Processor.ConfigBean;
import Taverna.Tree.Processor.InputPorts;
import Taverna.Tree.Processor.LocalWorkerActivityConfigurationBean;
import Taverna.Tree.Processor.Mapping;
import Taverna.Tree.Processor.MimeTypes;
import Taverna.Tree.Processor.Null;
import Taverna.Tree.Processor.ParallelizeConfig;
import Taverna.Tree.Processor.Processor;
import Taverna.Tree.Processor.Processors;
import Taverna.Tree.Processor.RetryConfig;
import Taverna.Tree.Processor.lWACBInputs;
import Taverna.Tree.Processor.lWACBOutputs;
import Taverna.Tree.Conditions;
import Taverna.Tree.Cross;
import Taverna.Tree.DispatchLayer;
import Taverna.Tree.DispatchStack;
import Taverna.Tree.Iteration;
import Taverna.Tree.IterationNode;
import Taverna.Tree.IterationStrategyStack;
import Taverna.Tree.Raven;
import Taverna.Tree.TavMap;
import Taverna.Tree.Workflow;
public class LoniToTavernaConverter extends DFSVisitor {
/**
* Converts a Loni Pipeline Workflow into corresponding Taverna Workflow object.
*/
public Object visit(Pipeline pipeline){
this.pipeline = pipeline;
Workflow workflow;
String producedBy="taverna-2.3.0";
String version = "1"; //Taverna wouldn't open the file with the version less than 1
String xmlns="http://taverna.sf.net/2008/xml/t2flow";
Dataflow dataflow = (Dataflow) visit(pipeline.getPipelineModuleGroup());
workflow = new Workflow( dataflow, producedBy, version, xmlns);
this.workflow = workflow;
return workflow;
}
/**
* Visits Loni Pipeline ModuleGroup and converts into corresponding Taverna Dataflow object.
*/
public Object visit(ModuleGroup moduleGroup){
this.moduleGroup = moduleGroup;
Dataflow dataflow;
// Initialize parameters to construct Dataflow object
String name = moduleGroup.getName();
AnnotatedGranularDepthInputPorts inputPorts = new AnnotatedGranularDepthInputPorts();
AnnotatedGranularDepthOutputPorts outputPorts = new AnnotatedGranularDepthOutputPorts();
Processors processors = new Processors();
Conditions conditions = new Conditions();
Datalinks datalinks = (Datalinks) visit(pipeline.getConnections());
Annotations annotations = new Annotations();
String id = UUID.randomUUID().toString();
String role = "top";
dataflow = new Dataflow(name, inputPorts, outputPorts, processors, conditions, datalinks, annotations, id, role);
this.dataflow = dataflow; // set environment variable for visitor
// Add Title and Description
annotations.addAnnotationChain("DescriptiveTitle", moduleGroup.getName(), null);
annotations.addAnnotationChain("FreeTextDescription", moduleGroup.getDescription(), null);
// Add Authors
List<Author> authors = moduleGroup.getAuthors().getAuthors();
for(Author author : authors)
{
String authorText = author.getFullName() + " Email: " + author.getEmail() + " Website: " + author.getWebsite();
annotations.addAnnotationChain("Author", authorText, null);
}
// Add Modules/Graph Objects
for(GraphObject module : moduleGroup.getModules())
{
moduleVisitor.visit(module, null);
}
return dataflow;
}
/**
* Visits Connections in Loni Pipeline xml file to create corresponding Datalinks in Taverna.
*/
public Object visit(Connections connections){
Datalinks datalinks = new Datalinks();
// For each connection in between modules, create a corresponding Datalink for Taverna workflow.
for (Connection c : connections.getConnections())
{
Link src = null;
Link snk = null;
String source[] = c.getSource().split("\\.");
for(GraphObject module : moduleGroup.getModules())
{
if(source[0].equals(module.getId())){
src = module.getSource(source[1]);
}
}
String sink[] = c.getSink().split("\\.");
for(GraphObject module : moduleGroup.getModules())
{
if(sink[0].equals(module.getId())){
snk = module.getSink(sink[1]);
}
}
DataLink datalink = new DataLink(snk, src);
datalinks.addDataLink(datalink);
}
return datalinks;
}
/*
* Visitor for different modules in Loni Pipeline. Currently only DataModules and
* Modules (executable modules) are supported. In order to extend the converter to
* handle more types of modules, add a new method to ModuleVisitor as such:
* public Pair<Object, Object> visit(<moduleType> module, Object o);
*/
{
moduleVisitor = new ModuleVisitor(){
// Visit DataModule to create data sources/sinks
public Pair<Object, Object> visit(DataModule dataModule, Object o){
String name = dataModule.getId();
String description = dataModule.getDescription();
String value = " Input/Output file(s) can be found at: ";
Values values = dataModule.getValues();
if(values != null){
for(Value v : values.getValues())
value += v.getValue();
}
description += value;
Annotations annotations = new Annotations();
annotations.addAnnotationChain("FreeTextDescription", description, null);
// DataModule is data source
if(dataModule.getInputs() == null)
{
AnnotatedGranularDepthPort port = new AnnotatedGranularDepthPort(name, 0, 0);
port.setAnnotations(annotations);
dataflow.getInputPorts().addPorts(port);
}
// DataModule is data sink
else if(dataModule.getOutputs() == null)
{
AnnotatedGranularDepthPort port = new AnnotatedGranularDepthPort(name, 0,0);
port.setAnnotations(annotations);
dataflow.getOutputPorts().addPorts(port);
}
return null;
}
// Visit [executable] Module to create Processors
public Pair<Object, Object> visit(Module module, Object o){
String name = module.getId();
String location = module.getLocation();
String description = "Location of executable: " + location + ". When prompted to input data values, enter this path for the command input.";
Processor processor = new Processor(null, null, null, name);
// Create annotations
Annotations annotations = new Annotations();
annotations.addAnnotationChain("FreeTextDescription", description, null);
// Add inputs/outputs
int depth = 0;
for(Parameter p : module.getInputs())
{
depth++;
}
processor.getInputPorts().addPorts(new AnnotatedGranularDepthPort("args", depth-1, 0));
processor.getOutputPorts().addPorts(new AnnotatedGranularDepthPort("result", 0, 0));
/*
* Next few steps are procedural steps to create a predefined
* type of module in Taverna that allows the user to run an
* executable command line app on data, effectively emulating
* executable modules in Loni Pipeline.
*/
// Add activities
Raven raven = new Raven("net.sf.taverna.t2.activities", "localworker-activity", "1.3");
Mapping input1 = new Mapping("args", "args");
Mapping input2 = new Mapping("command", "command");
TavMap inputMap = new TavMap();
inputMap.addMapping(input1);
inputMap.addMapping(input2);
Mapping output = new Mapping("result", "result");
TavMap outputMap = new TavMap();
outputMap.addMapping(output);
lWACBInputs lWACBIn = new lWACBInputs();
MimeTypes m1 = new MimeTypes("'text/plain'");
lWACBIn.addInput(new ActivityInputPortDefinitionBean("command", "0", m1, "", "java.lang.String", "true", null));
MimeTypes m2 = new MimeTypes("l('text/plain')");
lWACBIn.addInput(new ActivityInputPortDefinitionBean("args", "1", m2, "", "java.lang.String", "true", null));
lWACBOutputs lWACBOut = new lWACBOutputs();
MimeTypes m3 = new MimeTypes("'text/plain'");
lWACBOut.addOutput(new ActivityOutputPortDefinitionBean("result", "0", m3, null, null, null, "0"));
String script = StringEscapeUtils.unescapeXml("if (command == void || command.equals(\"\")) {\r\n\tthrow new RuntimeException(\"The 'command' port cannot be null.\");\r\n}\r\nProcess proc = null;\r\nRuntime rt = Runtime.getRuntime();\r\n\r\nString osName = System.getProperty(\"os.name\");\r\nString[] cmdArray = null;\r\nif (osName.equals(\"Windows NT\") || osName.equals(\"Windows XP\")) {\r\n\tcmdArray = new String[] { \"cmd.exe\", \"/c\", command };\r\n} else if (osName.equals(\"Windows 95\")) {\r\n\tcmdArray = new String[] { \"command.exe\", \"/c\", command };\r\n} else {// TODO: investigate if this will work in Linux and OSX\r\n\tcmdArray = new String[] { command };\r\n}\r\n\r\n// concatenate the arrays\r\nif ((args == void) || (args == null)) {\r\n\targs = new ArrayList();\r\n}\r\n\r\nint argSize = cmdArray.length + args.size();\r\nArrayList appArray = new ArrayList(argSize);\r\nfor (int i = 0; i < cmdArray.length; i++) {\r\n\tappArray.add(cmdArray[i]);\r\n}\r\n\r\nfor (int i = 0; i < args.size(); i++) {\r\n\tappArray.add(args.get(i));\r\n}\r\n\r\nString[] applist = new String[argSize];\r\nappArray.toArray(applist);\r\nproc = rt.exec(applist);\r\n\r\n// Get the input stream and read from it\r\nInputStream in = proc.getInputStream();\r\n\r\nint c;\r\nStringBuffer sb = new StringBuffer();\r\nwhile ((c = in.read()) != -1) {\r\n\tsb.append((char) c);\r\n}\r\nin.close();\r\nresult = sb.toString();\r\n");
LocalWorkerActivityConfigurationBean lwacb = new LocalWorkerActivityConfigurationBean("", lWACBIn, lWACBOut, "workflow", null, null, script, null, "net.sourceforge.taverna.scuflworkers.io.LocalCommand");
ConfigBean configBean = new ConfigBean("xstream", lwacb, null, null, null);
Activity activity = new Activity(raven, "net.sf.taverna.t2.activities.localworker.LocalworkerActivity", inputMap, outputMap, configBean);
Activities activities = new Activities();
activities.addActivity(activity);
processor.setActivities(activities);
// Add Dispatch Stack
DispatchStack ds = new DispatchStack();
Raven dispatchRaven = new Raven("net.sf.taverna.t2.core", "workflowmodel-impl", "1.3");
DispatchLayer layer1 = new DispatchLayer(dispatchRaven, "net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Parallelize", new ConfigBean("xstream", null, new ParallelizeConfig("", "1"), null, null));
DispatchLayer layer2 = new DispatchLayer(dispatchRaven, "net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.ErrorBounce", new ConfigBean("xstream", null, null, new Null(""), null));
DispatchLayer layer3 = new DispatchLayer(dispatchRaven, "net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Failover", new ConfigBean("xstream", null, null, new Null(""), null));
DispatchLayer layer4 = new DispatchLayer(dispatchRaven, "net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Retry", new ConfigBean("xstream", null, null, null, new RetryConfig("", "1.0", "1000", "5000", "0")));
DispatchLayer layer5 = new DispatchLayer(dispatchRaven, "net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke", new ConfigBean("xstream", null, null, new Null(""), null));
ds.addDispatchLayer(layer1);
ds.addDispatchLayer(layer2);
ds.addDispatchLayer(layer3);
ds.addDispatchLayer(layer4);
ds.addDispatchLayer(layer5);
processor.setDispatchStack(ds);
// Add Iteration Strategy Stack
IterationStrategyStack iss = new IterationStrategyStack();
IterationNode in = new IterationNode();
in.setCross(new Cross());
in.getCross().addPort(new PortProduct("args", 1));
in.getCross().addPort(new PortProduct("command", 0));
Iteration i = new Iteration();
i.addStrategy(in);
iss.addIteration(i);
processor.setIterationStrategyStack(iss);
// Add processor to Processors
dataflow.getProcessors().addProcessor(processor);
// Create Data Input with command-line app
AnnotatedGranularDepthPort command = new AnnotatedGranularDepthPort("Command: " + processor.getProcessorName(), 0, 0);
command.setAnnotations(annotations);
dataflow.getInputPorts().addPorts(command);
processor.getInputPorts().addPorts(new AnnotatedGranularDepthPort("command", 0, 0));
// Create connection from command-line app to processor
Link snk = new Link("processor", name, "command");
Link src = new Link("dataflow", null, "Command: " + processor.getProcessorName());
DataLink datalink = new DataLink(snk, src);
dataflow.getDatalinks().addDataLink(datalink);
return null;
}
};
}
// Environment variables
Workflow workflow;
Dataflow dataflow;
Pipeline pipeline;
ModuleGroup moduleGroup;
}