blob: 7557db7fde129e7b7e0fad6dd45f4738f03cde51 [file] [log] [blame]
package org.purl.wf4ever.wfdesc.scufl2;
import static uk.org.taverna.scufl2.api.common.Scufl2Tools.NESTED_WORKFLOW;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.openrdf.OpenRDFException;
import org.openrdf.concepts.rdfs.Resource;
import org.openrdf.elmo.ElmoModule;
import org.openrdf.elmo.sesame.SesameManager;
import org.openrdf.elmo.sesame.SesameManagerFactory;
import org.openrdf.query.parser.QueryParserRegistry;
import org.openrdf.query.parser.sparql.SPARQLParserFactory;
import org.openrdf.repository.Repository;
import org.openrdf.repository.contextaware.ContextAwareConnection;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.helpers.OrganizedRDFWriter;
import org.purl.wf4ever.roterms.RotermsResource;
import org.purl.wf4ever.wf4ever.BeanshellScript;
import org.purl.wf4ever.wf4ever.CommandLineTool;
import org.purl.wf4ever.wf4ever.RESTService;
import org.purl.wf4ever.wf4ever.RScript;
import org.purl.wf4ever.wf4ever.SOAPService;
import org.purl.wf4ever.wfdesc.Input;
import org.purl.wf4ever.wfdesc.Output;
import org.purl.wf4ever.wfdesc.Process;
import org.w3.prov.Entity;
import uk.org.taverna.scufl2.api.activity.Activity;
import uk.org.taverna.scufl2.api.annotation.Annotation;
import uk.org.taverna.scufl2.api.common.Child;
import uk.org.taverna.scufl2.api.common.Named;
import uk.org.taverna.scufl2.api.common.Scufl2Tools;
import uk.org.taverna.scufl2.api.common.URITools;
import uk.org.taverna.scufl2.api.common.Visitor.VisitorWithPath;
import uk.org.taverna.scufl2.api.common.WorkflowBean;
import uk.org.taverna.scufl2.api.configurations.Configuration;
import uk.org.taverna.scufl2.api.container.WorkflowBundle;
import uk.org.taverna.scufl2.api.core.DataLink;
import uk.org.taverna.scufl2.api.core.Processor;
import uk.org.taverna.scufl2.api.core.Workflow;
import uk.org.taverna.scufl2.api.io.WriterException;
import uk.org.taverna.scufl2.api.port.InputPort;
import uk.org.taverna.scufl2.api.port.OutputPort;
import uk.org.taverna.scufl2.api.port.WorkflowPort;
import uk.org.taverna.scufl2.api.profiles.ProcessorBinding;
import uk.org.taverna.scufl2.api.profiles.ProcessorPortBinding;
import uk.org.taverna.scufl2.api.profiles.Profile;
import com.fasterxml.jackson.databind.JsonNode;
public class WfdescSerialiser {
private static Logger logger = Logger.getLogger(WfdescSerialiser.class.getCanonicalName());
public static URI REST = URI
.create("http://ns.taverna.org.uk/2010/activity/rest");
public static URI WSDL = URI
.create("http://ns.taverna.org.uk/2010/activity/wsdl");
public static URI SECURITY = WSDL.resolve("wsdl/security");
public static URI OPERATION = WSDL.resolve("wsdl/operation");
public static URI BEANSHELL = URI
.create("http://ns.taverna.org.uk/2010/activity/beanshell");
public static URI RSHELL = URI
.create("http://ns.taverna.org.uk/2010/activity/rshell");
public static URI TOOL = URI
.create("http://ns.taverna.org.uk/2010/activity/tool");
private Scufl2Tools scufl2Tools = new Scufl2Tools();
private SesameManager sesameManager;
private URITools uriTools = new URITools();
private WorkflowBundle wfBundle;
public Repository getRepository() {
return getSesameManager().getConnection().getRepository();
}
public Scufl2Tools getScufl2Tools() {
return scufl2Tools;
}
public SesameManager getSesameManager() {
if (sesameManager == null) {
// Raven workaround - register SPARQLParserFactory
QueryParserRegistry.getInstance().add(new SPARQLParserFactory());
ElmoModule module = new ElmoModule();
module.addConcept(Labelled.class);
SesameManagerFactory factory = new SesameManagerFactory(module);
factory.setInferencingEnabled(true);
sesameManager = factory.createElmoManager();
}
return sesameManager;
}
public URITools getUriTools() {
return uriTools;
}
private QName qnameForBean(WorkflowBean bean) {
URI uri = uriTools.uriForBean(bean);
org.openrdf.model.URI sesameUri = getRepository().getValueFactory()
.createURI(uri.toASCIIString());
return new QName(sesameUri.getNamespace(), sesameUri.getLocalName());
}
protected void save(final WorkflowBundle bundle) {
bundle.accept(new VisitorWithPath() {
Scufl2Tools scufl2Tools = new Scufl2Tools();
public boolean visit() {
WorkflowBean node = getCurrentNode();
// System.out.println(node);
if (node instanceof WorkflowBundle) {
return true;
}
// @SuppressWarnings("rawtypes")
if (node instanceof uk.org.taverna.scufl2.api.core.Workflow) {
entityForBean(node, org.purl.wf4ever.wfdesc.Workflow.class);
} else if (node instanceof Processor) {
Processor processor = (Processor)node;
Process process = entityForBean(processor, Process.class);
entityForBean(processor.getParent(), org.purl.wf4ever.wfdesc.Workflow.class).getWfHasSubProcess().add(process);
} else if (node instanceof InputPort) {
WorkflowBean parent = ((Child) node).getParent();
Input input = entityForBean(node, Input.class);
Process process = entityForBean(parent, Process.class);
process.getWfHasInput().add(input);
} else if (node instanceof OutputPort) {
WorkflowBean parent = ((Child) node).getParent();
Output output = entityForBean(node, Output.class);
Process process = entityForBean(parent, Process.class);
process.getWfHasOutput().add(output);
} else if (node instanceof DataLink) {
WorkflowBean parent = ((Child) node).getParent();
DataLink link = (DataLink) node;
org.purl.wf4ever.wfdesc.DataLink dl = entityForBean(link,
org.purl.wf4ever.wfdesc.DataLink.class);
Output source = entityForBean(link.getReceivesFrom(),
Output.class);
dl.getWfHasSource().add(source);
Input sink = entityForBean(link.getSendsTo(), Input.class);
dl.getWfHasSink().add(sink);
entityForBean(parent,
org.purl.wf4ever.wfdesc.Workflow.class)
.getWfHasDataLink().add(dl);
} else if (node instanceof Profile) {
// So that we can get at the ProcessorBinding - buy only if it is the main Profile
return node == bundle.getMainProfile();
} else if (node instanceof ProcessorBinding) {
ProcessorBinding b = (ProcessorBinding) node;
Activity a = b.getBoundActivity();
Processor boundProcessor = b.getBoundProcessor();
Process process = entityForBean(boundProcessor, Process.class);
// Note: We don't describe the activity and processor binding in wfdesc. Instead we
// assign additional types and attributes to the parent processor
try {
URI type = a.getType();
Configuration c = scufl2Tools.configurationFor(a, b.getParent());
JsonNode json = c.getJson();
if (type.equals(BEANSHELL)) {
BeanshellScript script = getSesameManager().designateEntity(process, BeanshellScript.class);
String s = json.get("script").asText();
script.getWfScript().add(s);
JsonNode localDep = json.get("localDependency");
if (localDep != null && localDep.isArray()) {
for (int i=0; i<localDep.size(); i++) {
String depStr = localDep.get(i).asText();
RotermsResource res = getSesameManager().designateEntity(script, RotermsResource.class);
RotermsResource dep = getSesameManager().create(RotermsResource.class);
dep.getWfLabel().add(depStr);
dep.getWfComment().add("JAR dependency");
res.getWfRequiresSoftware().add(dep);
// Somehow this gets the whole thing to fall out of the graph!
// QName depQ = new QName("http://google.com/", ""+ UUID.randomUUID());
// sesameManager.rename(dep, depQ);
}
}
}
if (type.equals(RSHELL)) {
RScript script = getSesameManager().designateEntity(process, RScript.class);
String s = json.get("script").asText();
script.getWfScript().add(s);
}
if (type.equals(WSDL)) {
SOAPService soap = getSesameManager().designateEntity(process, SOAPService.class);
JsonNode operation = json.get("operation");
URI wsdl = URI.create(operation.get("wsdl").asText());
soap.getWfWsdlURI().add(wsdl);
soap.getWfWsdlOperationName().add(operation.get("name").asText());
soap.getWfRootURI().add(wsdl.resolve("/"));
}
if (type.equals(REST)) {
RESTService rest = getSesameManager().designateEntity(process, RESTService.class);
// System.out.println(json);
JsonNode request = json.get("request");
String absoluteURITemplate = request.get("absoluteURITemplate").asText();
String uriTemplate = absoluteURITemplate.replace("{", "");
uriTemplate = uriTemplate.replace("}", "");
// TODO: Detect {}
try {
URI root = new URI(uriTemplate).resolve("/");
rest.getWfRootURI().add(root);
} catch (URISyntaxException e) {
logger.warning("Potentially invalid URI template: " + absoluteURITemplate);
// Uncomment to temporarily break TestInvalidURITemplate:
// rest.getWfRootURI().add(URI.create("http://example.com/FRED"));
}
}
if (type.equals(TOOL)) {
CommandLineTool cmd = getSesameManager().designateEntity(process, CommandLineTool.class);
JsonNode desc = json.get("toolDescription");
//System.out.println(json);
JsonNode command = desc.get("command");
if (command != null) {
cmd.getWfCommand().add(command.asText());
}
}
if (type.equals(NESTED_WORKFLOW)) {
Workflow nestedWf = scufl2Tools.nestedWorkflowForProcessor(boundProcessor, b.getParent());
// The parent process is a specialization of the nested workflow
// (because the nested workflow could exist as several processors)
specializationOf(boundProcessor, nestedWf);
getSesameManager().designateEntity(process, org.purl.wf4ever.wfdesc.Workflow.class);
// Just like the Processor specializes the nested workflow, the
// ProcessorPorts specialize the WorkflowPort
for (ProcessorPortBinding portBinding : b.getInputPortBindings()) {
// Map from activity port (not in wfdesc) to WorkflowPort
WorkflowPort wfPort = nestedWf.getInputPorts().getByName(portBinding.getBoundActivityPort().getName());
if (wfPort == null) {
continue;
}
specializationOf(portBinding.getBoundProcessorPort(), wfPort);
}
for (ProcessorPortBinding portBinding : b.getOutputPortBindings()) {
WorkflowPort wfPort = nestedWf.getOutputPorts().getByName(portBinding.getBoundActivityPort().getName());
if (wfPort == null) {
continue;
}
specializationOf(portBinding.getBoundProcessorPort(), wfPort);
}
}
} catch (IndexOutOfBoundsException ex) {
}
return false;
} else {
// System.out.println("--NO!");
return false;
}
for (Annotation ann : scufl2Tools.annotationsFor(node, bundle)) {
String annotationBody = ann.getBody().toASCIIString();
String baseURI = bundle.getGlobalBaseURI().resolve(ann.getBody()).toASCIIString();
InputStream annotationStream;
try {
annotationStream = bundle.getResources().getResourceAsInputStream(
annotationBody);
RDFFormat dataFormat = RDFFormat.TURTLE;
try {
getSesameManager().getConnection().add(annotationStream, baseURI, dataFormat);
} catch (OpenRDFException e) {
logger.log(Level.WARNING, "Can't parse RDF Turtle from " + annotationBody, e);
} finally {
annotationStream.close();
}
} catch (IOException e) {
logger.log(Level.WARNING, "Can't read " + annotationBody, e);
}
}
if (node instanceof Named) {
Named named = (Named) node;
Labelled labelled = entityForBean(node, Labelled.class);
labelled.getLabel().add(named.getName());
}
return true;
}
private void specializationOf(WorkflowBean special, WorkflowBean general) {
Entity specialEnt = entityForBean(special, Entity.class);
Entity generalEnt = entityForBean(general, Entity.class);
specialEnt.getWfSpecializationOf().add(generalEnt);
}
private <T> T entityForBean(WorkflowBean bean, Class<T> type) {
return getSesameManager().create(qnameForBean(bean), type);
}
// @Override
// public boolean visitEnter(WorkflowBean node) {
// if (node instanceof Processor
// || node instanceof uk.org.taverna.scufl2.api.core.Workflow
// || node instanceof Port || node instanceof DataLink) {
// visit(node);
// return true;
// }
// // The other node types (e.g. dispatch stack, configuration) are
// // not (directly) represented in wfdesc
//// System.out.println("Skipping " + node);
// return false;
// };
});
}
public void save(WorkflowBundle wfBundle, OutputStream output)
throws WriterException {
synchronized (this) {
if (this.wfBundle != null) {
throw new IllegalStateException(
"This serializer is not thread-safe and can only save one WorkflowBundle at a time");
}
this.wfBundle = wfBundle;
}
try {
final URI baseURI;
if (wfBundle.getMainWorkflow() != null) {
Workflow mainWorkflow = wfBundle.getMainWorkflow();
baseURI = uriTools.uriForBean(mainWorkflow);
save(wfBundle);
} else {
throw new WriterException(
"wfdesc format requires a main workflow");
}
ContextAwareConnection connection = getSesameManager().getConnection();
try {
connection.setNamespace("rdfs",
"http://www.w3.org/2000/01/rdf-schema#");
connection.setNamespace("xsd", "http://www.w3.org/2001/XMLSchema#");
connection.setNamespace("owl", "http://www.w3.org/2002/07/owl#");
connection.setNamespace("prov", "http://www.w3.org/ns/prov#");
connection.setNamespace("wfdesc",
"http://purl.org/wf4ever/wfdesc#");
connection.setNamespace("wf4ever",
"http://purl.org/wf4ever/wf4ever#");
connection.setNamespace("roterms",
"http://purl.org/wf4ever/roterms#");
connection.setNamespace("dc", "http://purl.org/dc/elements/1.1/");
connection.setNamespace("dcterms", "http://purl.org/dc/terms/");
connection.setNamespace("comp", "http://purl.org/DP/components#");
connection.setNamespace("dep", "http://scape.keep.pt/vocab/dependencies#");
connection.setNamespace("biocat", "http://biocatalogue.org/attribute/");
connection.setNamespace("", "#");
connection.export(new OrganizedRDFWriter(
new TurtleWriterWithBase(output, baseURI)));
} catch (OpenRDFException e) {
throw new WriterException("Can't write to output", e);
}
} finally {
synchronized (this) {
this.wfBundle = null;
}
}
}
public void setScufl2Tools(Scufl2Tools scufl2Tools) {
this.scufl2Tools = scufl2Tools;
}
public void setSesameManager(SesameManager sesameManager) {
this.sesameManager = sesameManager;
}
public void setUriTools(URITools uriTools) {
this.uriTools = uriTools;
}
}