Merge branch '2.5' into taverna-3
diff --git a/pom.xml b/pom.xml
index 10df511..fc7b0c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
<groupId>uk.org.taverna.server</groupId>
<artifactId>server</artifactId>
<packaging>pom</packaging>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
<name>Taverna Server</name>
<description>Taverna Server is a service that provides execution of Taverna Workflows, provided they do not access the user interface while executing.</description>
<url>http://www.taverna.org.uk/</url>
@@ -21,8 +21,8 @@
<!-- Having to edit anything below here is probably indicative of a bug. -->
<parent>
<groupId>net.sf.taverna</groupId>
- <artifactId>parent</artifactId>
- <version>0.2.2014-03-19</version>
+ <artifactId>taverna-parent</artifactId>
+ <version>3.0.1-SNAPSHOT</version>
</parent>
<prerequisites>
<maven>2.2</maven>
@@ -141,13 +141,22 @@
<repository>
<id>mygrid-repository</id>
<name>myGrid Respository</name>
- <url>http://www.mygrid.org.uk/maven/repository</url>
+ <url>http://build.mygrid.org.uk/maven/repository</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases />
</repository>
<repository>
+ <id>mygrid-snapshot-repository</id>
+ <name>myGrid Snapshot Repository</name>
+ <url>http://build.mygrid.org.uk/maven/snapshot-repository</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases />
+ </repository>
+ <repository>
<id>mygrid-snapshots</id>
<name>myGrid Snapshot Respository</name>
<url>http://www.mygrid.org.uk/maven/snapshot-repository</url>
@@ -543,6 +552,7 @@
<module>server-unix-forker</module>
<module>server-usagerecord</module>
<module>server-port-description</module>
+ <module>server-execution-delegate</module>
<module>server-rmidaemon</module>
<module>server-client</module>
<module>server-distribution</module>
diff --git a/server-client/pom.xml b/server-client/pom.xml
index 4cc0492..e62978a 100644
--- a/server-client/pom.xml
+++ b/server-client/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>uk.org.taverna.server</groupId>
<artifactId>server</artifactId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
</parent>
<artifactId>server-client</artifactId>
<packaging>bundle</packaging>
diff --git a/server-distribution/pom.xml b/server-distribution/pom.xml
index 6f5a146..9eab5d3 100644
--- a/server-distribution/pom.xml
+++ b/server-distribution/pom.xml
@@ -4,7 +4,7 @@
<parent>
<artifactId>server</artifactId>
<groupId>uk.org.taverna.server</groupId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>server-distribution</artifactId>
diff --git a/server-execution-delegate/pom.xml b/server-execution-delegate/pom.xml
new file mode 100644
index 0000000..6acd601
--- /dev/null
+++ b/server-execution-delegate/pom.xml
@@ -0,0 +1,49 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>uk.org.taverna</groupId>
+ <artifactId>platform</artifactId>
+ <version>0.1.3-SNAPSHOT</version>
+ </parent>
+ <groupId>uk.org.taverna.server</groupId>
+ <artifactId>server-execution-delegate</artifactId>
+ <version>3.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+ <repositories>
+ <repository>
+ <releases />
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ <id>mygrid-repository</id>
+ <name>myGrid Repository</name>
+ <url>http://www.mygrid.org.uk/maven/repository
+ </url>
+ </repository>
+ <repository>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots />
+ <id>mygrid-snapshot-repository</id>
+ <name>myGrid Snapshot Repository</name>
+ <url>http://www.mygrid.org.uk/maven/snapshot-repository</url>
+ </repository>
+ </repositories>
+ <dependencies>
+ <dependency>
+ <groupId>uk.org.taverna.platform</groupId>
+ <artifactId>taverna-execution-api</artifactId>
+ <version>0.1.3-SNAPSHOT</version>
+ <type>bundle</type>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <name>Reporting Probe</name>
+ <inceptionYear>2012</inceptionYear>
+</project>
\ No newline at end of file
diff --git a/server-execution-delegate/src/main/java/org/taverna/server/execution_delegate/ExecutionDelegate.java b/server-execution-delegate/src/main/java/org/taverna/server/execution_delegate/ExecutionDelegate.java
new file mode 100644
index 0000000..7ea9998
--- /dev/null
+++ b/server-execution-delegate/src/main/java/org/taverna/server/execution_delegate/ExecutionDelegate.java
@@ -0,0 +1,172 @@
+package org.taverna.server.execution_delegate;
+
+import java.net.URI;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Map;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import org.taverna.server.execution_delegate.RemoteExecution.ProcessorReportDocument.Property;
+
+import uk.org.taverna.platform.data.api.Data;
+import uk.org.taverna.platform.data.api.DataLocation;
+import uk.org.taverna.platform.execution.api.Execution;
+import uk.org.taverna.platform.report.ActivityReport;
+import uk.org.taverna.platform.report.ProcessorReport;
+import uk.org.taverna.platform.report.StatusReport;
+import uk.org.taverna.platform.report.WorkflowReport;
+import uk.org.taverna.scufl2.api.common.AbstractNamed;
+
+public class ExecutionDelegate extends UnicastRemoteObject implements
+ RemoteExecution {
+ private Execution delegated;
+ private DatatypeFactory dtf;
+
+ public ExecutionDelegate(Execution execution) throws RemoteException,
+ DatatypeConfigurationException {
+ super();
+ delegated = execution;
+ dtf = DatatypeFactory.newInstance();
+ }
+
+ @Override
+ public String getID() {
+ return delegated.getID();
+ }
+
+ @Override
+ public void delete() {
+ delegated.delete();
+ }
+
+ @Override
+ public void start() {
+ delegated.start();
+ }
+
+ @Override
+ public void pause() {
+ delegated.pause();
+ }
+
+ @Override
+ public void resume() {
+ delegated.resume();
+ }
+
+ @Override
+ public void cancel() {
+ delegated.cancel();
+ }
+
+ private XMLGregorianCalendar date(Date d) {
+ if (d == null)
+ return null;
+ GregorianCalendar c = new GregorianCalendar();
+ c.setTime(d);
+ return dtf.newXMLGregorianCalendar(c);
+ }
+
+ private <R extends ReportDocument, T extends AbstractNamed> R init(
+ R snapshot, StatusReport<T, ?, ?> report) {
+ snapshot.created = date(report.getCreatedDate());
+ snapshot.completed = date(report.getCompletedDate());
+ snapshot.cancelled = date(report.getCancelledDate());
+ snapshot.failed = date(report.getFailedDate());
+ snapshot.started = date(report.getStartedDate());
+ for (Date d : report.getPausedDates())
+ snapshot.paused.add(date(d));
+ for (Date d : report.getResumedDates())
+ snapshot.resumed.add(date(d));
+ snapshot.subject = report.getSubject().getName();
+ snapshot.state = report.getState().toString();
+ return snapshot;
+ }
+
+ private WorkflowReportDocument getReport(WorkflowReport report) {
+ WorkflowReportDocument snapshot = init(new WorkflowReportDocument(),
+ report);
+ for (ProcessorReport pr : report.getChildReports())
+ snapshot.processor.add(getReport(pr));
+ initMap(snapshot.inputs, report.getInputs());
+ initMap(snapshot.outputs, report.getOutputs());
+ return snapshot;
+ }
+
+ private void initMap(ArrayList<PortMapping> snapshot, Map<String, ?> report) {
+ for (String port : report.keySet())
+ snapshot.add(data(port, report.get(port)));
+ }
+
+ private static final String DEFAULT_PROPERTY_STRING = "";
+
+ private PortMapping data(String name, Object o) {
+ String loc;
+ if (o instanceof DataLocation) {
+ DataLocation d = (DataLocation) o;
+ loc = d.getDataServiceURI() + "#" + d.getDataID();
+ } else {
+ Data d = (Data) o;
+ loc = null;
+ if (d.hasReferences()) {
+ try {
+ loc = d.getReferences().iterator().next().getURI()
+ .toString();
+ } catch (Exception e) {
+ }
+ }
+ if (loc == null) {
+ DataLocation dl = d.getDataService().getDataLocation(d);
+ loc = dl.getDataServiceURI() + "#" + dl.getDataID();
+ }
+ }
+ return new PortMapping(name, URI.create(loc));
+ }
+
+ private ProcessorReportDocument getReport(ProcessorReport report) {
+ ProcessorReportDocument snapshot = init(new ProcessorReportDocument(),
+ report);
+ for (ActivityReport pr : report.getChildReports())
+ snapshot.activity.add(getReport(pr));
+ snapshot.jobsQueued = report.getJobsStarted();
+ snapshot.jobsStarted = report.getJobsStarted();
+ snapshot.jobsCompleted = report.getJobsCompleted();
+ snapshot.jobsErrored = report.getJobsCompletedWithErrors();
+ for (String key : report.getPropertyKeys()) {
+ Object value = report.getProperty(key);
+ if (value instanceof String || value instanceof Number)
+ snapshot.properties.add(new Property(key, value.toString()));
+ else
+ snapshot.properties.add(new Property(key,
+ DEFAULT_PROPERTY_STRING));
+ }
+ return snapshot;
+ }
+
+ private ActivityReportDocument getReport(ActivityReport report) {
+ ActivityReportDocument snapshot = init(new ActivityReportDocument(),
+ report);
+ for (WorkflowReport pr : report.getChildReports())
+ snapshot.workflow.add(getReport(pr));
+ initMap(snapshot.inputs, report.getInputs());
+ initMap(snapshot.outputs, report.getOutputs());
+ return snapshot;
+ }
+
+ @Override
+ public WorkflowReportDocument getWorkflowReport() {
+ return getReport(delegated.getWorkflowReport());
+ }
+}
+
+// ExecutionDelegate.java:[96,2]
+// initMap(java.util.ArrayList<org.taverna.server.execution_delegate.RemoteExecution.PortMapping>,java.util.Map<java.lang.String,uk.org.taverna.platform.data.api.Data>)
+// in org.taverna.server.execution_delegate.ExecutionDelegate cannot be applied
+// to
+// (java.util.ArrayList<org.taverna.server.execution_delegate.RemoteExecution.PortMapping>,java.util.Map<java.lang.String,uk.org.taverna.platform.data.api.DataLocation>)
diff --git a/server-execution-delegate/src/main/java/org/taverna/server/execution_delegate/RemoteExecution.java b/server-execution-delegate/src/main/java/org/taverna/server/execution_delegate/RemoteExecution.java
new file mode 100644
index 0000000..b8510df
--- /dev/null
+++ b/server-execution-delegate/src/main/java/org/taverna/server/execution_delegate/RemoteExecution.java
@@ -0,0 +1,188 @@
+package org.taverna.server.execution_delegate;
+
+import static javax.xml.bind.annotation.XmlAccessType.NONE;
+
+import java.net.URI;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+/**
+ * Interface for a single execution of a Taverna workflow in a remote process.
+ *
+ * @author David Withers
+ */
+public interface RemoteExecution extends Remote {
+
+ /**
+ * Returns the identifier for this execution.
+ *
+ * @return the identifier for this execution
+ */
+ String getID() throws RemoteException;
+
+ /**
+ * Returns the <code>WorkflowReport</code> for the execution.
+ *
+ * @return the <code>WorkflowReport</code> for the execution
+ */
+ WorkflowReportDocument getWorkflowReport() throws RemoteException;
+
+ /**
+ * Deletes the execution.
+ */
+ void delete() throws RemoteException;
+
+ /**
+ * Starts the execution.
+ */
+ void start() throws RemoteException;
+
+ /**
+ * Pauses the execution.
+ */
+ void pause() throws RemoteException;
+
+ /**
+ * Resumes a paused execution.
+ */
+ void resume() throws RemoteException;
+
+ /**
+ * Cancels the execution.
+ */
+ void cancel() throws RemoteException;
+
+ @XmlType(name = "Report", propOrder = { "state", "created", "started",
+ "completed", "failed", "cancelled", "paused", "resumed" })
+ @XmlSeeAlso({ WorkflowReportDocument.class, ProcessorReportDocument.class,
+ ActivityReportDocument.class })
+ @XmlAccessorType(NONE)
+ public static abstract class ReportDocument {
+ @XmlAttribute
+ public String subject;
+ @XmlElement
+ public String state;
+ @XmlElement(required = true)
+ @XmlSchemaType(name = "dateTime")
+ public XMLGregorianCalendar created;
+ @XmlElement
+ @XmlSchemaType(name = "dateTime")
+ public XMLGregorianCalendar started;
+ @XmlElement
+ @XmlSchemaType(name = "dateTime")
+ public XMLGregorianCalendar completed;
+ @XmlElement
+ @XmlSchemaType(name = "dateTime")
+ public XMLGregorianCalendar failed;
+ @XmlElement
+ @XmlSchemaType(name = "dateTime")
+ public XMLGregorianCalendar cancelled;
+ @XmlElement
+ @XmlSchemaType(name = "dateTime")
+ public List<XMLGregorianCalendar> paused;
+ @XmlElement
+ @XmlSchemaType(name = "dateTime")
+ public List<XMLGregorianCalendar> resumed;
+ }
+
+ @XmlType(name = "WorkflowReport", propOrder = { "processor", "inputs",
+ "outputs" })
+ @XmlRootElement(name = "workflowReport")
+ @XmlAccessorType(NONE)
+ public static class WorkflowReportDocument extends ReportDocument {
+ @XmlElement(name = "processor")
+ public ArrayList<ProcessorReportDocument> processor = new ArrayList<ProcessorReportDocument>();
+ @XmlElement(name = "input")
+ public ArrayList<PortMapping> inputs = new ArrayList<PortMapping>();
+ @XmlElement(name = "output")
+ public ArrayList<PortMapping> outputs = new ArrayList<PortMapping>();
+ }
+
+ @XmlType(name = "ProcessorReport", propOrder = { "jobsQueued",
+ "jobsStarted", "jobsCompleted", "jobsErrored", "properties",
+ "activity" })
+ @XmlAccessorType(NONE)
+ public static class ProcessorReportDocument extends ReportDocument {
+ @XmlElement(name = "jobsQueued", required = true)
+ public int jobsQueued;
+ @XmlElement(name = "jobsStarted", required = true)
+ public int jobsStarted;
+ @XmlElement(name = "jobsCompleted", required = true)
+ public int jobsCompleted;
+ @XmlElement(name = "jobsErrored", required = true)
+ public int jobsErrored;
+ @XmlElement(name = "property")
+ @XmlElementWrapper(name = "properties")
+ public ArrayList<Property> properties = new ArrayList<Property>();
+ @XmlElement(name = "activity")
+ public ArrayList<ActivityReportDocument> activity = new ArrayList<ActivityReportDocument>();
+
+ @XmlType(name = "ProcessorProperty")
+ @XmlAccessorType(NONE)
+ public static class Property {
+ @XmlAttribute(name = "key", required = true)
+ String key;
+ @XmlValue
+ String value;
+
+ public Property() {
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ Property(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+ }
+
+ @XmlType(name = "ActivityReport", propOrder = { "workflow", "inputs",
+ "outputs" })
+ @XmlAccessorType(NONE)
+ public static class ActivityReportDocument extends ReportDocument {
+ @XmlElement(name = "workflow")
+ public ArrayList<WorkflowReportDocument> workflow = new ArrayList<WorkflowReportDocument>();
+ @XmlElement(name = "input")
+ public ArrayList<PortMapping> inputs = new ArrayList<PortMapping>();
+ @XmlElement(name = "output")
+ public ArrayList<PortMapping> outputs = new ArrayList<PortMapping>();
+ }
+
+ @XmlType(name = "PortMapping")
+ @XmlAccessorType(NONE)
+ public static class PortMapping {
+ public PortMapping() {
+ }
+
+ PortMapping(String port, URI data) {
+ this.name = port;
+ this.reference = data;
+ }
+
+ @XmlAttribute(name = "name")
+ public String name;
+ @XmlAttribute(name = "ref")
+ @XmlSchemaType(name = "anyURI")
+ public URI reference;
+ }
+}
diff --git a/server-execution-delegate/src/test/java/SerializationTest.java b/server-execution-delegate/src/test/java/SerializationTest.java
new file mode 100644
index 0000000..c4bf0b9
--- /dev/null
+++ b/server-execution-delegate/src/test/java/SerializationTest.java
@@ -0,0 +1,54 @@
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.SchemaOutputResolver;
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.taverna.server.execution_delegate.RemoteExecution.WorkflowReportDocument;
+
+public class SerializationTest {
+ private static final boolean PRINT = true;
+ SchemaOutputResolver sink;
+ StringWriter schema;
+
+ String schema() {
+ return schema.toString();
+ }
+
+ @Before
+ public void init() {
+ schema = new StringWriter();
+ sink = new SchemaOutputResolver() {
+ @Override
+ public Result createOutput(String namespaceUri,
+ String suggestedFileName) throws IOException {
+ StreamResult sr = new StreamResult(schema);
+ sr.setSystemId("/dev/null");
+ return sr;
+ }
+ };
+ assertEquals("", schema());
+ }
+
+ @Test
+ public void testSchemaGeneration() throws JAXBException, IOException {
+ JAXBContext.newInstance(WorkflowReportDocument.class).generateSchema(
+ sink);
+ assertFalse("generated schema must be non-empty", schema().isEmpty());
+ assertTrue(
+ "generated schema must define workflowReport element",
+ schema().contains(
+ "<xs:element name=\"workflowReport\" type=\"WorkflowReport\"/>\n"));
+ if (PRINT)
+ System.out.print(schema());
+ }
+}
diff --git a/server-port-description/pom.xml b/server-port-description/pom.xml
index 2382504..44a9d50 100644
--- a/server-port-description/pom.xml
+++ b/server-port-description/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>server</artifactId>
<groupId>uk.org.taverna.server</groupId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
</parent>
<artifactId>server-port-description</artifactId>
<name>Workflow Port Descriptor Types</name>
diff --git a/server-rmidaemon/pom.xml b/server-rmidaemon/pom.xml
index 0e76a90..c2282b0 100644
--- a/server-rmidaemon/pom.xml
+++ b/server-rmidaemon/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>uk.org.taverna.server</groupId>
<artifactId>server</artifactId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
</parent>
<artifactId>server-rmidaemon</artifactId>
<name>RMI registry daemon</name>
diff --git a/server-runinterface/pom.xml b/server-runinterface/pom.xml
index d828299..696487a 100644
--- a/server-runinterface/pom.xml
+++ b/server-runinterface/pom.xml
@@ -6,7 +6,7 @@
<parent>
<groupId>uk.org.taverna.server</groupId>
<artifactId>server</artifactId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<scm>
diff --git a/server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java b/server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java
index fb3af6d..eec4ab5 100644
--- a/server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java
+++ b/server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java
@@ -38,7 +38,7 @@
* If anything goes wrong with the communication.
*/
@Nonnull
- RemoteSingleRun make(@Nonnull String workflow, @Nonnull String creator,
+ RemoteSingleRun make(@Nonnull byte[] workflow, @Nonnull String creator,
@Nullable UsageRecordReceiver usageRecordReceiver,
@Nullable UUID masterID) throws RemoteException;
diff --git a/server-unix-forker/pom.xml b/server-unix-forker/pom.xml
index bf228a2..cdbce7b 100644
--- a/server-unix-forker/pom.xml
+++ b/server-unix-forker/pom.xml
@@ -4,7 +4,7 @@
<parent>
<artifactId>server</artifactId>
<groupId>uk.org.taverna.server</groupId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
</parent>
<artifactId>server-unix-forker</artifactId>
<scm>
diff --git a/server-usagerecord/pom.xml b/server-usagerecord/pom.xml
index e654df7..5352779 100644
--- a/server-usagerecord/pom.xml
+++ b/server-usagerecord/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>server</artifactId>
<groupId>uk.org.taverna.server</groupId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>server-usagerecord</artifactId>
diff --git a/server-webapp/pom.xml b/server-webapp/pom.xml
index 719da86..15f1eb4 100644
--- a/server-webapp/pom.xml
+++ b/server-webapp/pom.xml
@@ -7,7 +7,7 @@
<parent>
<groupId>uk.org.taverna.server</groupId>
<artifactId>server</artifactId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<scm>
@@ -20,11 +20,13 @@
<version.spring-security>3.1.4.RELEASE</version.spring-security>
<version.asm>3.3.1</version.asm>
<version.smack>3.2.1</version.smack>
- <version.commandline>2.5.0</version.commandline>
+ <!--<version.commandline>3.0.1-SNAPSHOT</version.commandline>-->
+ <version.commandline>3.0.0</version.commandline>
<edition.commandline>enterprise</edition.commandline>
<version.jdoapi>3.0.1</version.jdoapi>
<forker.module>server-unix-forker</forker.module>
<util.dir>${project.build.directory}/${project.build.finalName}/WEB-INF/classes/util</util.dir>
+ <scufl2.version>0.9.2</scufl2.version>
<cmdline.dir>${util.dir}/taverna-commandline-${edition.commandline}-${version.commandline}</cmdline.dir>
</properties>
@@ -289,6 +291,28 @@
<version>1.3.4</version>
<scope>runtime</scope>
</dependency>
+ <dependency>
+ <groupId>uk.org.taverna.scufl2</groupId>
+ <artifactId>scufl2-api</artifactId>
+ <version>${scufl2.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>uk.org.taverna.scufl2</groupId>
+ <artifactId>scufl2-t2flow</artifactId>
+ <version>${scufl2.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>uk.org.taverna.scufl2</groupId>
+ <artifactId>scufl2-rdfxml</artifactId>
+ <version>${scufl2.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.4.1</version>
+ </dependency>
</dependencies>
<dependencyManagement>
@@ -573,6 +597,7 @@
<groupId>net.sf.taverna.t2.taverna-commandline</groupId>
<artifactId>taverna-commandline-${edition.commandline}</artifactId>
<version>${version.commandline}</version>
+ <classifier>bin</classifier>
<type>zip</type>
<classifier>bin</classifier>
<outputDirectory>${util.dir}</outputDirectory>
diff --git a/server-webapp/src/build/resources/datanucleus-log.properties b/server-webapp/src/build/resources/datanucleus-log.properties
deleted file mode 100644
index 3249455..0000000
--- a/server-webapp/src/build/resources/datanucleus-log.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-handlers=java.util.logging.ConsoleHandler
-DataNucleus.General.level=fine
-DataNucleus.Enhancer.level=WARN
-java.util.logging.ConsoleHandler.level=WARN
-java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
diff --git a/server-webapp/src/main/java/org/taverna/server/master/ContentsDescriptorBuilder.java b/server-webapp/src/main/java/org/taverna/server/master/ContentsDescriptorBuilder.java
index 8b4b947..f5259bd 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/ContentsDescriptorBuilder.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/ContentsDescriptorBuilder.java
@@ -8,29 +8,21 @@
import static eu.medsea.util.MimeUtil.getMimeType;
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
import static javax.ws.rs.core.UriBuilder.fromUri;
-import static javax.xml.xpath.XPathConstants.NODE;
-import static javax.xml.xpath.XPathConstants.NODESET;
import static org.apache.commons.logging.LogFactory.getLog;
import static org.taverna.server.master.common.Uri.secure;
import java.io.ByteArrayInputStream;
-import java.util.ArrayList;
+import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathExpression;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
import org.apache.commons.logging.Log;
import org.springframework.beans.factory.annotation.Required;
-import org.springframework.util.xml.SimpleNamespaceContext;
import org.taverna.server.master.exceptions.FilesystemAccessException;
import org.taverna.server.master.exceptions.NoDirectoryEntryException;
import org.taverna.server.master.interfaces.Directory;
@@ -49,8 +41,11 @@
import org.taverna.server.port_description.ListValue;
import org.taverna.server.port_description.OutputDescription;
import org.taverna.server.port_description.OutputDescription.OutputPort;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
+
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.core.Workflow;
+import uk.org.taverna.scufl2.api.port.InputWorkflowPort;
+import uk.org.taverna.scufl2.api.port.OutputWorkflowPort;
/**
* A class that is used to build descriptions of the contents of a workflow
@@ -60,67 +55,8 @@
*/
public class ContentsDescriptorBuilder {
private Log log = getLog("Taverna.Server.Webapp");
- /** Namespace for use when pulling apart a .t2flow document. */
- private static final String T2FLOW_NS = "http://taverna.sf.net/2008/xml/t2flow";
- /** Prefix for t2flow namespace. */
- private static final String T2FLOW_PFX = "t2";
-
private FilenameUtils fileUtils;
private UriBuilderFactory uriBuilderFactory;
- private XPathExpression inputPorts;
- private XPathExpression outputPorts;
- private XPathExpression portName;
- private XPathExpression portDepth;
- private XPathExpression dataflow;
-
- public ContentsDescriptorBuilder() throws XPathExpressionException {
- XPath xp = XPathFactory.newInstance().newXPath();
- SimpleNamespaceContext ctxt = new SimpleNamespaceContext();
- ctxt.bindNamespaceUri(T2FLOW_PFX, T2FLOW_NS);
- xp.setNamespaceContext(ctxt);
-
- dataflow = xp.compile("//t2:dataflow[1]");
- inputPorts = xp.compile("./t2:inputPorts/t2:port");
- outputPorts = xp.compile("./t2:outputPorts/t2:port");
- portName = xp.compile("./t2:name/text()");
- portDepth = xp.compile("./t2:depth/text()");
- }
-
- private Element dataflow(Element root) throws XPathExpressionException {
- return (Element) dataflow.evaluate(root, NODE);
- }
-
- private List<Element> inputPorts(Element dataflow)
- throws XPathExpressionException {
- List<Element> result = new ArrayList<>();
- if (dataflow == null)
- return result;
- NodeList nl = (NodeList) inputPorts.evaluate(dataflow, NODESET);
- // Wrap as a list so we can iterate over it <sigh>
- for (int i = 0; i < nl.getLength(); i++)
- result.add((Element) nl.item(i));
- return result;
- }
-
- private List<Element> outputPorts(Element dataflow)
- throws XPathExpressionException {
- List<Element> result = new ArrayList<>();
- if (dataflow == null)
- return result;
- NodeList nl = (NodeList) outputPorts.evaluate(dataflow, NODESET);
- // Wrap as a list so we can iterate over it <sigh>
- for (int i = 0; i < nl.getLength(); i++)
- result.add((Element) nl.item(i));
- return result;
- }
-
- private String portName(Element port) throws XPathExpressionException {
- return portName.evaluate(port);
- }
-
- private String portDepth(Element port) throws XPathExpressionException {
- return portDepth.evaluate(port);
- }
@Required
public void setUriBuilderFactory(UriBuilderFactory uriBuilderFactory) {
@@ -134,50 +70,13 @@
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- private Element fillInFromWorkflow(TavernaRun run, UriBuilder ub,
- AbstractPortDescription portDesc) throws XPathExpressionException {
- Element elem = run.getWorkflow().getWorkflowRoot();
- portDesc.fillInBaseData(elem.getAttribute("id"), run.getId(),
- ub.build());
- return dataflow(elem);
- }
-
- /**
- * Build the contents description.
- *
- * @param run
- * The workflow run this is talking about.
- * @param dataflow
- * The dataflow element of the T2flow document.
- * @param ub
- * How to build URIs.
- * @param descriptor
- * The descriptor to modify.
- * @param expected
- * The list of outputs that are <i>expected</i> to be produced;
- * they might not actually produce anything though.
- * @throws NoDirectoryEntryException
- * @throws FilesystemAccessException
- * @throws XPathExpressionException
- */
- private void constructPorts(TavernaRun run, Element dataflow,
- UriBuilder ub, OutputDescription descriptor)
- throws FilesystemAccessException, NoDirectoryEntryException,
- XPathExpressionException {
- Collection<DirectoryEntry> outs = null;
- try {
- outs = fileUtils.getDirectory(run, "out").getContents();
- } catch (FilesystemAccessException | NoDirectoryEntryException e) {
- log.warn("unexpected failure in construction of output descriptor",
- e);
- }
- for (Element output : outputPorts(dataflow)) {
- OutputPort p = descriptor.addPort(portName(output));
- if (outs != null) {
- p.output = constructValue(outs, ub, p.name);
- p.depth = computeDepth(p.output);
- }
- }
+ private Workflow fillInFromWorkflow(TavernaRun run, UriBuilder ub,
+ AbstractPortDescription portDesc) throws IOException {
+ WorkflowBundle bundle = run.getWorkflow().getScufl2Workflow();
+ bundle.getMainWorkflow().getInputPorts();
+ portDesc.fillInBaseData(bundle.getMainWorkflow()
+ .getWorkflowIdentifier().toString(), run.getId(), ub.build());
+ return bundle.getMainWorkflow();
}
/**
@@ -335,12 +234,20 @@
OutputDescription descriptor = new OutputDescription();
try {
UriBuilder ub = getRunUriBuilder(run, ui);
- Element dataflow = fillInFromWorkflow(run, ub, descriptor);
- if (dataflow == null || run.getOutputBaclavaFile() != null)
- return descriptor;
- constructPorts(run, dataflow, ub.path("wd"), descriptor);
- } catch (XPathExpressionException e) {
- log.info("failure in XPath evaluation", e);
+ Workflow dataflow = fillInFromWorkflow(run, ub, descriptor);
+ Collection<DirectoryEntry> outs = null;
+ ub = ub.path("wd/{path}");
+ for (OutputWorkflowPort output : dataflow.getOutputPorts()) {
+ OutputPort p = descriptor.addPort(output.getName());
+ if (run.getOutputBaclavaFile() == null) {
+ if (outs == null)
+ outs = fileUtils.getDirectory(run, "out").getContents();
+ p.output = constructValue(outs, ub, p.name);
+ p.depth = computeDepth(p.output);
+ }
+ }
+ } catch (IOException e) {
+ log.info("failure in conversion to .scufl2", e);
}
return descriptor;
}
@@ -366,19 +273,19 @@
InputDescription desc = new InputDescription();
try {
UriBuilder ub = getRunUriBuilder(run, ui);
- Element dataflow = fillInFromWorkflow(run, ub, desc);
+ Workflow workflow = fillInFromWorkflow(run, ub, desc);
ub = ub.path("input/{name}");
- for (Element port : inputPorts(dataflow)) {
- InputPort in = desc.addPort(portName(port));
+ for (InputWorkflowPort port : workflow.getInputPorts()) {
+ InputPort in = desc.addPort(port.getName());
in.href = ub.build(in.name);
try {
- in.depth = Integer.valueOf(portDepth(port));
+ in.depth = port.getDepth();
} catch (NumberFormatException ex) {
in.depth = null;
}
}
- } catch (XPathExpressionException e) {
- log.info("failure in XPath evaluation", e);
+ } catch (IOException e) {
+ log.info("failure in conversion to .scufl2", e);
}
return desc;
}
diff --git a/server-webapp/src/main/java/org/taverna/server/master/DirectoryREST.java b/server-webapp/src/main/java/org/taverna/server/master/DirectoryREST.java
index cc7d08a..48969fa 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/DirectoryREST.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/DirectoryREST.java
@@ -343,6 +343,7 @@
List<URI> referenceList, UriInfo ui)
throws NoDirectoryEntryException, NoUpdateException,
FilesystemAccessException {
+ support.permitUpdate(run);
if (referenceList.isEmpty() || referenceList.size() > 1)
return status(422).entity("URI list must have single URI in it")
.build();
diff --git a/server-webapp/src/main/java/org/taverna/server/master/RunREST.java b/server-webapp/src/main/java/org/taverna/server/master/RunREST.java
index a8cffec..563a822 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/RunREST.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/RunREST.java
@@ -31,6 +31,7 @@
import org.ogf.usage.JobUsageRecord;
import org.springframework.beans.factory.annotation.Required;
import org.taverna.server.master.api.RunBean;
+import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.Status;
import org.taverna.server.master.common.Workflow;
import org.taverna.server.master.exceptions.BadStateChangeException;
@@ -179,6 +180,19 @@
@Override
@CallCounted
+ public String getMainProfileName() {
+ String name = run.getWorkflow().getMainProfileName();
+ return (name == null ? "" : name);
+ }
+
+ @Override
+ @CallCounted
+ public ProfileList getProfiles() {
+ return support.getProfileDescriptor(run.getWorkflow());
+ }
+
+ @Override
+ @CallCounted
@PerfLogged
@RolesAllowed({ USER, SELF })
public DirectoryREST getWorkingDirectory() {
@@ -401,6 +415,12 @@
@Override
@CallCounted
+ public Response profileOptions() {
+ return opt();
+ }
+
+ @Override
+ @CallCounted
public Response expiryOptions() {
return opt("PUT");
}
diff --git a/server-webapp/src/main/java/org/taverna/server/master/TavernaServer.java b/server-webapp/src/main/java/org/taverna/server/master/TavernaServer.java
index 62daf7d..0f98da6 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/TavernaServer.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/TavernaServer.java
@@ -60,8 +60,10 @@
import org.taverna.server.master.api.TavernaServerBean;
import org.taverna.server.master.common.Capability;
import org.taverna.server.master.common.Credential;
+import org.taverna.server.master.common.DirEntryReference;
import org.taverna.server.master.common.InputDescription;
import org.taverna.server.master.common.Permission;
+import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.RunReference;
import org.taverna.server.master.common.Status;
import org.taverna.server.master.common.Trust;
@@ -101,6 +103,7 @@
import org.taverna.server.master.soap.FileContents;
import org.taverna.server.master.soap.PermissionList;
import org.taverna.server.master.soap.TavernaServerSOAP;
+import org.taverna.server.master.soap.WrappedWorkflow;
import org.taverna.server.master.soap.ZippedDirectory;
import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;
import org.taverna.server.master.utils.FilenameUtils;
@@ -380,6 +383,23 @@
@CallCounted
@PerfLogged
@RolesAllowed(USER)
+ public RunReference submitWorkflowMTOM(WrappedWorkflow workflow)
+ throws NoUpdateException {
+ Workflow wf;
+ try {
+ wf = workflow.getWorkflow();
+ } catch (IOException e) {
+ throw new NoCreateException(e.getMessage(), e);
+ }
+ checkCreatePolicy(wf);
+ String name = support.buildWorkflow(wf);
+ return new RunReference(name, getRunUriBuilder());
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed(USER)
public RunReference submitWorkflowByURI(URI workflowURI)
throws NoCreateException {
checkCreatePolicy(workflowURI);
@@ -465,6 +485,27 @@
@CallCounted
@PerfLogged
@RolesAllowed(USER)
+ public WrappedWorkflow getRunWorkflowMTOM(String runName)
+ throws UnknownRunException {
+ WrappedWorkflow ww = new WrappedWorkflow();
+ ww.setWorkflow(support.getRun(runName).getWorkflow());
+ return ww;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed(USER)
+ public ProfileList getRunWorkflowProfiles(String runName)
+ throws UnknownRunException {
+ return support.getProfileDescriptor(support.getRun(runName)
+ .getWorkflow());
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed(USER)
public Date getRunExpiry(String runName) throws UnknownRunException {
return support.getRun(runName).getExpiry();
}
@@ -956,6 +997,25 @@
@CallCounted
@PerfLogged
@RolesAllowed(USER)
+ public void setRunFileContentsFromURI(String runName,
+ DirEntryReference file, URI reference)
+ throws UnknownRunException, NoUpdateException,
+ FilesystemAccessException, NoDirectoryEntryException {
+ TavernaRun run = support.getRun(runName);
+ support.permitUpdate(run);
+ File f = fileUtils.getFile(run, file);
+ try {
+ support.copyDataToFile(reference, f);
+ } catch (IOException e) {
+ throw new FilesystemAccessException(
+ "problem transferring data from URI", e);
+ }
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed(USER)
public void setRunFileContentsMTOM(String runName, FileContents newContents)
throws UnknownRunException, NoUpdateException,
FilesystemAccessException, NoDirectoryEntryException {
diff --git a/server-webapp/src/main/java/org/taverna/server/master/TavernaServerSupport.java b/server-webapp/src/main/java/org/taverna/server/master/TavernaServerSupport.java
index c5c219b..ce36bd3 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/TavernaServerSupport.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/TavernaServerSupport.java
@@ -52,6 +52,7 @@
import org.taverna.server.master.api.TavernaServerBean;
import org.taverna.server.master.common.Capability;
import org.taverna.server.master.common.Permission;
+import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.VersionedElement;
import org.taverna.server.master.common.Workflow;
import org.taverna.server.master.common.version.Version;
@@ -79,6 +80,8 @@
import org.taverna.server.master.utils.InvocationCounter;
import org.taverna.server.master.utils.UsernamePrincipal;
+import uk.org.taverna.scufl2.api.profiles.Profile;
+
/**
* Web application support utilities.
*
@@ -861,6 +864,7 @@
long total = 0;
try {
byte[] buffer = new byte[TRANSFER_SIZE];
+ boolean first = true;
while (true) {
int len = stream.read(buffer);
if (len < 0)
@@ -870,19 +874,47 @@
log.debug("read " + len
+ " bytes from source stream (total: " + total
+ ") bound for " + name);
- if (len == buffer.length)
- file.appendContents(buffer);
- else {
+ if (len == buffer.length) {
+ if (first)
+ file.setContents(buffer);
+ else
+ file.appendContents(buffer);
+ } else {
byte[] newBuf = new byte[len];
System.arraycopy(buffer, 0, newBuf, 0, len);
- file.appendContents(newBuf);
+ if (first)
+ file.setContents(newBuf);
+ else
+ file.appendContents(newBuf);
}
+ first = false;
}
} catch (IOException exn) {
throw new FilesystemAccessException("failed to transfer bytes", exn);
}
}
+ /**
+ * Build a description of the profiles supported by a workflow. Note that we
+ * expect the set of profiles to be fairly small.
+ *
+ * @param workflow
+ * The workflow to describe the profiles of.
+ * @return The descriptor (which might be empty).
+ */
+ public ProfileList getProfileDescriptor(Workflow workflow) {
+ ProfileList result = new ProfileList();
+ String main = workflow.getMainProfileName();
+ for (Profile p : workflow.getProfiles()) {
+ ProfileList.Info i = new ProfileList.Info();
+ i.name = p.getName();
+ if (main != null && main.equals(i.name))
+ i.main = true;
+ result.profile.add(i);
+ }
+ return result;
+ }
+
public boolean getAllowStartWorkflowRuns() {
return runFactory.isAllowingRunsToStart();
}
diff --git a/server-webapp/src/main/java/org/taverna/server/master/common/ProfileList.java b/server-webapp/src/main/java/org/taverna/server/master/common/ProfileList.java
new file mode 100644
index 0000000..69c3622
--- /dev/null
+++ b/server-webapp/src/main/java/org/taverna/server/master/common/ProfileList.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The University of Manchester
+ *
+ * See the file "LICENSE.txt" for license terms.
+ */
+package org.taverna.server.master.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+
+/**
+ * Description of the profiles that can apply to a workflow.
+ *
+ * @author Donal K. Fellows
+ */
+@XmlRootElement(name = "profiles")
+@XmlType(name = "ProfileList")
+public class ProfileList {
+ public List<ProfileList.Info> profile = new ArrayList<ProfileList.Info>();
+
+ /**
+ * Description of a single workflow profile.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "profile")
+ @XmlType(name = "Profile")
+ public static class Info {
+ @XmlValue
+ public String name;
+ /**
+ * Whether this is the main profile.
+ */
+ @XmlAttribute(name = "isMain")
+ public Boolean main;
+ }
+}
\ No newline at end of file
diff --git a/server-webapp/src/main/java/org/taverna/server/master/common/Workflow.java b/server-webapp/src/main/java/org/taverna/server/master/common/Workflow.java
index 9aa3669..ba6a68c 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/common/Workflow.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/common/Workflow.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 The University of Manchester
+ * Copyright (C) 2010-2012 The University of Manchester
*
* See the file "LICENSE" for license terms.
*/
@@ -7,8 +7,12 @@
import static javax.xml.bind.Marshaller.JAXB_ENCODING;
import static javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT;
+import static javax.xml.bind.annotation.XmlAccessType.NONE;
import static org.apache.commons.logging.LogFactory.getLog;
-import static org.taverna.server.master.common.Namespaces.T2FLOW;
+import static org.taverna.server.master.rest.handler.Scufl2DocumentHandler.SCUFL2;
+import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW;
+import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW_NS;
+import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW_ROOTNAME;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -22,39 +26,57 @@
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.InflaterInputStream;
+import java.net.URL;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.taverna.server.master.rest.handler.Scufl2DocumentHandler;
+import org.taverna.server.master.rest.handler.T2FlowDocumentHandler;
+import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import uk.org.taverna.scufl2.api.common.NamedSet;
+import uk.org.taverna.scufl2.api.container.WorkflowBundle;
+import uk.org.taverna.scufl2.api.io.ReaderException;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+import uk.org.taverna.scufl2.api.io.WriterException;
+import uk.org.taverna.scufl2.api.profiles.Profile;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
- * Encapsulation of a T2flow document.
+ * Encapsulation of a T2flow or Scufl2 document.
*
- * @author dkf
+ * @author Donal K. Fellows
*/
@XmlRootElement(name = "workflow")
@XmlType(name = "Workflow")
-public class Workflow implements Serializable,Externalizable {
- /**
- * Literal document.
- */
+@XmlAccessorType(NONE)
+public class Workflow implements Serializable, Externalizable {
+ /** Literal document, if present. */
@XmlAnyElement(lax = true)
- public Element[] content;
+ private Element content;
+ /** SCUFL2 bundle, if present. */
+ @XmlTransient
+ private WorkflowBundle bundle;
+ /** Which came first, the bundle or the t2flow document. */
+ @XmlTransient
+ private boolean isBundleFirst;
private static Marshaller marshaller;
private static Unmarshaller unmarshaller;
- private final static String ENCODING = "UTF-8";
+ private final static String ENCODING = "UTF-8";
+ private final static WorkflowBundleIO io;
static {
try {
JAXBContext context = JAXBContext.newInstance(Workflow.class);
@@ -67,14 +89,160 @@
"failed to build JAXB context for working with "
+ Workflow.class, e);
}
+ io = new WorkflowBundleIO();
}
+ public enum ContentType {
+ T2FLOW(T2FlowDocumentHandler.T2FLOW), SCUFL2(
+ Scufl2DocumentHandler.SCUFL2);
+ private String type;
+
+ ContentType(String type) {
+ this.type = type;
+ }
+
+ public String getContentType() {
+ return type;
+ }
+ }
+
+ public Workflow() {
+ }
+
+ public Workflow(Element element) {
+ this.content = element;
+ this.isBundleFirst = false;
+ }
+
+ public Workflow(WorkflowBundle bundle) {
+ this.bundle = bundle;
+ this.isBundleFirst = true;
+ }
+
+ public Workflow(URL url) throws ReaderException, IOException {
+ this(io.readBundle(url, null));
+ }
+
+ /**
+ * What content type would this workflow "prefer" to be?
+ */
+ public ContentType getPreferredContentType() {
+ if (isBundleFirst)
+ return ContentType.SCUFL2;
+ else
+ return ContentType.T2FLOW;
+ }
+
+ /**
+ * Retrieves the workflow as a SCUFL2 document, converting it if necessary.
+ *
+ * @return The SCUFL2 document.
+ * @throws IOException
+ * If anything goes wrong.
+ */
+ public WorkflowBundle getScufl2Workflow() throws IOException {
+ try {
+ if (bundle == null)
+ bundle = io.readBundle(new ByteArrayInputStream(getAsT2Flow()),
+ T2FLOW);
+ return bundle;
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException("problem when converting to SCUFL2", e);
+ }
+ }
+
+ /**
+ * Get the bytes of the serialized SCUFL2 workflow.
+ *
+ * @return Array of bytes.
+ * @throws IOException
+ * If serialization fails.
+ * @throws WriterException
+ * If conversion fails.
+ */
+ public byte[] getScufl2Bytes() throws IOException, WriterException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ io.writeBundle(getScufl2Workflow(), baos, SCUFL2);
+ return baos.toByteArray();
+ }
+
+ /**
+ * Retrieves the workflow as a T2Flow document, converting it if necessary.
+ *
+ * @return The T2Flow document.
+ * @throws IOException
+ * If anything goes wrong.
+ */
+ public Element getT2flowWorkflow() throws IOException {
+ try {
+ if (content != null)
+ return content;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ io.writeBundle(bundle, baos, T2FLOW);
+ Document doc;
+ try {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory
+ .newInstance();
+ dbf.setNamespaceAware(true);
+ doc = dbf.newDocumentBuilder().parse(
+ new ByteArrayInputStream(baos.toByteArray()));
+ } catch (SAXException e) {
+ throw new IOException("failed to convert to DOM tree", e);
+ }
+ Element e = doc.getDocumentElement();
+ if (e.getNamespaceURI().equals(T2FLOW_NS)
+ && e.getNodeName().equals(T2FLOW_ROOTNAME))
+ return content = e;
+ throw new IOException(
+ "unexpected element when converting to T2Flow: {"
+ + e.getNamespaceURI() + "}" + e.getNodeName());
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IOException("problem when converting to SCUFL2", e);
+ }
+ }
+
+ /**
+ * @return The name of the main workflow profile, or <tt>null</tt> if there
+ * is none.
+ */
+ public String getMainProfileName() {
+ try {
+ return getScufl2Workflow().getMainProfile().getName();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * @return The set of profiles supported over this workflow.
+ */
+ public NamedSet<Profile> getProfiles() {
+ try {
+ return getScufl2Workflow().getProfiles();
+ } catch (IOException e) {
+ return new NamedSet<Profile>();
+ }
+ }
+
+ /**
+ * Convert from marshalled form.
+ *
+ * @throws JAXBException
+ * If the conversion fails.
+ */
public static Workflow unmarshal(String representation)
throws JAXBException {
StringReader sr = new StringReader(representation);
return (Workflow) unmarshaller.unmarshal(sr);
}
+ /**
+ * Convert to marshalled form.
+ */
public String marshal() throws JAXBException {
StringWriter sw = new StringWriter();
marshaller.marshal(this, sw);
@@ -85,35 +253,69 @@
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
try {
- int len = in.readInt();
- byte[] bytes = new byte[len];
- in.readFully(bytes);
- try (Reader r = new InputStreamReader(new InflaterInputStream(
- new ByteArrayInputStream(bytes)), ENCODING)) {
- this.content = ((Workflow) unmarshaller.unmarshal(r)).content;
- }
+ ByteArrayInputStream bytes = readbytes(in);
+ if (bytes != null)
+ try (Reader r = new InputStreamReader(bytes, ENCODING)) {
+ content = ((Workflow) unmarshaller.unmarshal(r)).content;
+ }
+ bytes = readbytes(in);
+ if (bytes != null)
+ bundle = io.readBundle(bytes, SCUFL2);
+ isBundleFirst = in.readBoolean();
return;
} catch (JAXBException e) {
throw new IOException("failed to unmarshal", e);
} catch (ClassCastException e) {
throw new IOException("bizarre result of unmarshalling", e);
+ } catch (ReaderException e) {
+ throw new IOException("failed to unmarshal", e);
}
}
+ private byte[] getAsT2Flow() throws IOException, JAXBException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStreamWriter w = new OutputStreamWriter(baos, ENCODING);
+ marshaller.marshal(this, w);
+ w.close();
+ return baos.toByteArray();
+ }
+
+ private byte[] getAsScufl2() throws IOException, WriterException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ io.writeBundle(bundle, baos, SCUFL2);
+ baos.close();
+ return baos.toByteArray();
+ }
+
@Override
public void writeExternal(ObjectOutput out) throws IOException {
try {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (OutputStreamWriter w = new OutputStreamWriter(
- new DeflaterOutputStream(baos), ENCODING)) {
- marshaller.marshal(this, w);
- }
- byte[] bytes = baos.toByteArray();
- out.writeInt(bytes.length);
- out.write(bytes);
+ writebytes(out, (content != null) ? getAsT2Flow() : null);
} catch (JAXBException e) {
- throw new IOException("failed to marshal", e);
+ throw new IOException("failed to marshal t2flow", e);
}
+ try {
+ writebytes(out, (bundle != null) ? getAsScufl2() : null);
+ } catch (WriterException e) {
+ throw new IOException("failed to marshal scufl2", e);
+ }
+ out.writeBoolean(isBundleFirst);
+ }
+
+ private ByteArrayInputStream readbytes(ObjectInput in) throws IOException {
+ int len = in.readInt();
+ if (len > 0) {
+ byte[] bytes = new byte[len];
+ in.readFully(bytes);
+ return new ByteArrayInputStream(bytes);
+ }
+ return null;
+ }
+
+ private void writebytes(ObjectOutput out, byte[] data) throws IOException {
+ out.writeInt(data == null ? 0 : data.length);
+ if (data != null && data.length > 0)
+ out.write(data);
}
/**
@@ -125,13 +327,7 @@
* @return The looked up element, or <tt>null</tt> if it doesn't exist.
*/
private Element getEl(String... name) {
- Element el = null;
- for (Element e : content)
- if (e.getNamespaceURI().equals(T2FLOW)
- && e.getLocalName().equals(name[0])) {
- el = e;
- break;
- }
+ Element el = content;
boolean skip = true;
for (String n : name) {
if (skip) {
@@ -140,7 +336,7 @@
}
if (el == null)
return null;
- NodeList nl = el.getElementsByTagNameNS(T2FLOW, n);
+ NodeList nl = el.getElementsByTagNameNS(T2FLOW_NS, n);
if (nl.getLength() == 0)
return null;
Node node = nl.item(0);
diff --git a/server-webapp/src/main/java/org/taverna/server/master/localworker/AbstractRemoteRunFactory.java b/server-webapp/src/main/java/org/taverna/server/master/localworker/AbstractRemoteRunFactory.java
index f1dec3c..fc7f881 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/localworker/AbstractRemoteRunFactory.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/localworker/AbstractRemoteRunFactory.java
@@ -59,6 +59,8 @@
import org.taverna.server.master.worker.RemoteRunDelegate;
import org.taverna.server.master.worker.RunFactoryConfiguration;
+import uk.org.taverna.scufl2.api.io.WriterException;
+
/**
* Bridge to remote runs via RMI.
*
@@ -387,8 +389,14 @@
* @throws JAXBException
* If serialization fails.
*/
- protected String serializeWorkflow(Workflow workflow) throws JAXBException {
- return workflow.marshal();
+ protected byte[] serializeWorkflow(Workflow workflow) throws JAXBException {
+ try {
+ return workflow.getScufl2Bytes();
+ } catch (IOException e) {
+ throw new JAXBException("problem converting to scufl2", e);
+ } catch (WriterException e) {
+ throw new JAXBException("problem converting to scufl2", e);
+ }
}
private void acceptUsageRecord(String usageRecord) {
diff --git a/server-webapp/src/main/java/org/taverna/server/master/localworker/ForkRunFactory.java b/server-webapp/src/main/java/org/taverna/server/master/localworker/ForkRunFactory.java
index b8746bc..b67e121 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/localworker/ForkRunFactory.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/localworker/ForkRunFactory.java
@@ -282,7 +282,7 @@
* If anything fails (communications error, etc.)
*/
private RemoteSingleRun getRealRun(@Nonnull UsernamePrincipal creator,
- @Nonnull String wf, UUID id) throws RemoteException {
+ @Nonnull byte[] wf, UUID id) throws RemoteException {
@Nonnull
String globaluser = "Unknown Person";
if (creator != null)
@@ -297,7 +297,7 @@
protected RemoteSingleRun getRealRun(UsernamePrincipal creator,
Workflow workflow, UUID id) throws Exception {
@Nonnull
- String wf = serializeWorkflow(workflow);
+ byte[] wf = serializeWorkflow(workflow);
for (int i = 0; i < 3; i++) {
initFactory();
try {
diff --git a/server-webapp/src/main/java/org/taverna/server/master/localworker/IdAwareForkRunFactory.java b/server-webapp/src/main/java/org/taverna/server/master/localworker/IdAwareForkRunFactory.java
index 6679039..e449373 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/localworker/IdAwareForkRunFactory.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/localworker/IdAwareForkRunFactory.java
@@ -269,7 +269,7 @@
* If anything fails (communications error, etc.)
*/
private RemoteSingleRun getRealRun(@Nonnull UsernamePrincipal creator,
- @Nonnull String username, @Nonnull String wf, UUID id)
+ @Nonnull String username, @Nonnull byte[] wf, UUID id)
throws RemoteException {
String globaluser = "Unknown Person";
if (creator != null)
@@ -283,7 +283,7 @@
@Override
protected RemoteSingleRun getRealRun(UsernamePrincipal creator,
Workflow workflow, UUID id) throws Exception {
- String wf = serializeWorkflow(workflow);
+ byte[] wf = serializeWorkflow(workflow);
String username = mapper == null ? null : mapper
.getUsernameForPrincipal(creator);
if (username == null)
diff --git a/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerREST.java b/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerREST.java
index 1e9f4a2..62158bb 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerREST.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerREST.java
@@ -19,6 +19,7 @@
import static org.taverna.server.master.rest.TavernaServerREST.PathNames.POL_RUN_LIMIT;
import static org.taverna.server.master.rest.TavernaServerREST.PathNames.ROOT;
import static org.taverna.server.master.rest.TavernaServerREST.PathNames.RUNS;
+import static org.taverna.server.master.rest.handler.Scufl2DocumentHandler.SCUFL2;
import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW;
import java.net.URI;
@@ -41,7 +42,9 @@
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
import org.apache.abdera.model.Entry;
import org.apache.abdera.model.Feed;
@@ -59,7 +62,7 @@
import org.taverna.server.master.soap.TavernaServerSOAP;
/**
- * The REST service interface to Taverna 2.5.4 Server.
+ * The REST service interface to Taverna 3 Server.
*
* @author Donal Fellows
* @see TavernaServerSOAP
@@ -117,7 +120,7 @@
*/
@POST
@Path(RUNS)
- @Consumes({ T2FLOW, XML })
+ @Consumes({ T2FLOW, SCUFL2, XML })
@RolesAllowed(USER)
@Description("Accepts (or not) a request to create a new run executing "
+ "the given workflow.")
@@ -129,16 +132,15 @@
* Accepts (or not) a request to create a new run executing the workflow at
* the given location.
*
- * @param referenceList
- * The URI to workflow document to execute.
+ * @param workflowReference
+ * The wrapped URI to workflow document to execute.
* @param ui
* About the URI being POSTed to.
* @return A response to the POST describing what was created.
* @throws NoUpdateException
* If the POST failed.
- * @throws NoCreateException
- * If the workflow couldn't be read into the server or the
- * engine rejects it.
+ * @throw NoCreateException If the workflow couldn't be read into the server
+ * or the engine rejects it.
*/
@POST
@Path(RUNS)
@@ -586,4 +588,17 @@
@Nonnull
Entry getEvent(@Nonnull @PathParam("id") String id);
}
+
+ /**
+ * A reference to a workflow hosted on some public HTTP server.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "workflowurl")
+ @XmlType(name = "WorkflowReference")
+ public static class WorkflowReference {
+ @XmlValue
+ @XmlSchemaType(name = "anyURI")
+ public URI url;
+ }
}
diff --git a/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerRunREST.java b/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerRunREST.java
index 536c455..2b328fd 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerRunREST.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/rest/TavernaServerRunREST.java
@@ -8,6 +8,7 @@
import static javax.ws.rs.core.UriBuilder.fromUri;
import static org.joda.time.format.ISODateTimeFormat.basicDateTime;
import static org.taverna.server.master.common.Roles.USER;
+import static org.taverna.server.master.rest.handler.Scufl2DocumentHandler.SCUFL2;
import static org.taverna.server.master.interaction.InteractionFeedSupport.FEED_URL_DIR;
import static org.taverna.server.master.rest.ContentTypes.JSON;
import static org.taverna.server.master.rest.ContentTypes.ROBUNDLE;
@@ -20,6 +21,7 @@
import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.LOG;
import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.NAME;
import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.OUT;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.PROFILE;
import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.ROOT;
import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.RUNBUNDLE;
import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.SEC;
@@ -62,6 +64,7 @@
import org.apache.cxf.jaxrs.model.wadl.Description;
import org.joda.time.format.DateTimeFormatter;
import org.taverna.server.master.common.Namespaces;
+import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.Status;
import org.taverna.server.master.common.Uri;
import org.taverna.server.master.common.VersionedElement;
@@ -125,7 +128,7 @@
*/
@GET
@Path(WF)
- @Produces({ T2FLOW, XML, JSON })
+ @Produces({ T2FLOW, SCUFL2, XML, JSON })
@Description("Gives the workflow document used to create the workflow run.")
@Nonnull
public Workflow getWorkflow();
@@ -168,6 +171,40 @@
Response nameOptions();
/**
+ * Produces the name of the workflow's main profile.
+ *
+ * @return The main profile name, or the empty string if there is no such
+ * profile.
+ */
+ @GET
+ @Path(PROFILE)
+ @Produces(TEXT)
+ @Description("Gives the name of the workflow's main profile, or the empty string if none is defined.")
+ @Nonnull
+ String getMainProfileName();
+
+ /**
+ * Get a description of the profiles supported by the workflow document used
+ * to create this run.
+ *
+ * @return A description of the supported profiles.
+ */
+ @GET
+ @Path(PROFILE)
+ @Produces({ XML, JSON })
+ @Description("Describes what profiles exist on the workflow.")
+ @Nonnull
+ ProfileList getProfiles();
+
+ /** Produce the workflow profile HTTP operations. */
+ @OPTIONS
+ @Path(PROFILE)
+ @Description("Produces the description of the operations on the run's "
+ + "profile.")
+ @Nonnull
+ Response profileOptions();
+
+ /**
* Returns a resource that represents the workflow run's security
* properties. These may only be accessed by the owner.
*
@@ -571,6 +608,7 @@
public static final String STATUS = "status";
public static final String IN = "input";
public static final String OUT = "output";
+ public static final String PROFILE = "profile";
public static final String LISTEN = "listeners";
public static final String SEC = "security";
public static final String STDOUT = "stdout";
diff --git a/server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java b/server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java
new file mode 100644
index 0000000..edeac63
--- /dev/null
+++ b/server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ *
+ * See the file "LICENSE.txt" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.common.Workflow;
+
+import uk.org.taverna.scufl2.api.io.ReaderException;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+import uk.org.taverna.scufl2.api.io.WriterException;
+
+/**
+ * Handler that allows a .scufl2 document to be read from and written to a REST
+ * message directly.
+ *
+ * @author Donal Fellows
+ */
+@Provider
+public class Scufl2DocumentHandler implements MessageBodyReader<Workflow>,
+ MessageBodyWriter<Workflow> {
+ private static final MediaType SCUFL2_TYPE = new MediaType("application",
+ "vnd.taverna.scufl2.workflow-bundle");
+ public static final String SCUFL2 = "application/vnd.taverna.scufl2.workflow-bundle";
+ private WorkflowBundleIO io = new WorkflowBundleIO();
+
+ @Override
+ public boolean isReadable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ if (type.isAssignableFrom(Workflow.class))
+ return mediaType.isCompatible(SCUFL2_TYPE);
+ return false;
+ }
+
+ @Override
+ public Workflow readFrom(Class<Workflow> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+ throws IOException, WebApplicationException {
+ try {
+ return new Workflow(io.readBundle(entityStream, SCUFL2));
+ } catch (ReaderException e) {
+ throw new WebApplicationException(e, 403);
+ }
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ if (Workflow.class.isAssignableFrom(type))
+ return mediaType.isCompatible(SCUFL2_TYPE);
+ return false;
+ }
+
+ @Override
+ public long getSize(Workflow workflow, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(Workflow workflow, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ try {
+ io.writeBundle(workflow.getScufl2Workflow(), entityStream, SCUFL2);
+ } catch (WriterException e) {
+ throw new WebApplicationException(e);
+ }
+ }
+}
diff --git a/server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java b/server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
index 78ee822..4227d80 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
@@ -28,7 +28,6 @@
import org.taverna.server.master.common.Workflow;
import org.w3c.dom.Document;
-import org.w3c.dom.Element;
import org.xml.sax.SAXException;
/**
@@ -43,8 +42,8 @@
private static final MediaType T2FLOW_TYPE = new MediaType("application",
"vnd.taverna.t2flow+xml");
public static final String T2FLOW = "application/vnd.taverna.t2flow+xml";
- private static final String T2FLOW_ROOTNAME = "workflow";
- private static final String T2FLOW_NS = "http://taverna.sf.net/2008/xml/t2flow";
+ public static final String T2FLOW_ROOTNAME = "workflow";
+ public static final String T2FLOW_NS = "http://taverna.sf.net/2008/xml/t2flow";
private DocumentBuilderFactory db;
private TransformerFactory transformer;
@@ -76,8 +75,7 @@
} catch (ParserConfigurationException e) {
throw new WebApplicationException(e);
}
- Workflow workflow = new Workflow();
- workflow.content = new Element[] { doc.getDocumentElement() };
+ Workflow workflow = new Workflow(doc.getDocumentElement());
if (doc.getDocumentElement().getNamespaceURI().equals(T2FLOW_NS)
&& doc.getDocumentElement().getNodeName()
.equals(T2FLOW_ROOTNAME))
@@ -109,7 +107,7 @@
WebApplicationException {
try {
transformer.newTransformer().transform(
- new DOMSource(workflow.content[0]),
+ new DOMSource(workflow.getT2flowWorkflow()),
new StreamResult(entityStream));
} catch (TransformerException e) {
if (e.getCause() != null && e.getCause() instanceof IOException)
diff --git a/server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java b/server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
index 378eee8..a90a229 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
@@ -118,4 +118,4 @@
}
w.flush();
}
-}
\ No newline at end of file
+}
diff --git a/server-webapp/src/main/java/org/taverna/server/master/soap/TavernaServerSOAP.java b/server-webapp/src/main/java/org/taverna/server/master/soap/TavernaServerSOAP.java
index 72aca5e..e175bf8 100644
--- a/server-webapp/src/main/java/org/taverna/server/master/soap/TavernaServerSOAP.java
+++ b/server-webapp/src/main/java/org/taverna/server/master/soap/TavernaServerSOAP.java
@@ -23,8 +23,10 @@
import org.ogf.usage.JobUsageRecord;
import org.taverna.server.master.common.Capability;
import org.taverna.server.master.common.Credential;
+import org.taverna.server.master.common.DirEntryReference;
import org.taverna.server.master.common.InputDescription;
import org.taverna.server.master.common.Permission;
+import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.RunReference;
import org.taverna.server.master.common.Status;
import org.taverna.server.master.common.Trust;
@@ -45,7 +47,7 @@
import org.taverna.server.port_description.OutputDescription;
/**
- * The SOAP service interface to Taverna 2.5.4 Server.
+ * The SOAP service interface to Taverna 3 Server.
*
* @author Donal Fellows
* @see TavernaServerREST
@@ -68,7 +70,22 @@
@WSDLDocumentation("Make a run for a particular workflow.")
RunReference submitWorkflow(
@WebParam(name = "workflow") @XmlElement(required = true) Workflow workflow)
- throws NoUpdateException, NoCreateException;
+ throws NoUpdateException, NoCreateException;
+
+ /**
+ * Make a run for a particular workflow.
+ *
+ * @param workflow
+ * The workflow to instantiate.
+ * @return Annotated handle for created run.
+ * @throws NoUpdateException
+ * @throws NoCreateException
+ */
+ @WebResult(name = "Run")
+ @WSDLDocumentation("Make a run for a particular workflow.")
+ RunReference submitWorkflowMTOM(
+ @WebParam(name = "workflow") @XmlElement(required = true) WrappedWorkflow workflow)
+ throws NoUpdateException;
/**
* Make a run for a particular workflow, where that workflow will be
@@ -81,7 +98,8 @@
* @throws NoCreateException
*/
@WebResult(name = "Run")
- @WSDLDocumentation("Make a run for a particular workflow where that workflow is given by publicly readable URI.")
+ @WSDLDocumentation("Make a run for a particular workflow where that "
+ + "workflow is given by publicly readable URI.")
RunReference submitWorkflowByURI(
@WebParam(name = "workflowURI") @XmlElement(required = true) URI workflowURI)
throws NoCreateException, NoUpdateException;
@@ -179,6 +197,39 @@
throws UnknownRunException;
/**
+ * Get the workflow document used to create the given run.
+ *
+ * @param runName
+ * The handle of the run.
+ * @return The workflow document.
+ * @throws UnknownRunException
+ * If the server doesn't know about the run or if the user is
+ * not permitted to see it.
+ */
+ @WebResult(name = "CreationWorkflow")
+ @WSDLDocumentation("Get the workflow document used to create the given run.")
+ WrappedWorkflow getRunWorkflowMTOM(
+ @WebParam(name = "runName") String runName)
+ throws UnknownRunException;
+
+ /**
+ * Get a description of the profiles supported by the workflow document used
+ * to create the given run.
+ *
+ * @param runName
+ * The handle of the run.
+ * @return A description of the supported profiles.
+ * @throws UnknownRunException
+ * If the server doesn't know about the run or if the user is
+ * not permitted to see it.
+ */
+ @WebResult(name = "Profiles")
+ @WSDLDocumentation("Get a description of the profiles supported by the workflow document used to create the given run.")
+ ProfileList getRunWorkflowProfiles(
+ @WebParam(name = "runName") String runName)
+ throws UnknownRunException;
+
+ /**
* Get the descriptive name of the workflow run. The descriptive name
* carries no deep information.
*
@@ -1281,6 +1332,37 @@
FilesystemAccessException, NoDirectoryEntryException;
/**
+ * Set the contents of a file under the run's working directory to the
+ * contents of a publicly readable URI. Runs do not share working
+ * directories.
+ *
+ * @param runName
+ * The handle of the run.
+ * @param file
+ * The name of the file to update; the main working directory is
+ * <tt>/</tt> and <tt>..</tt> is always disallowed.
+ * @param reference
+ * The publicly readable URI whose contents are to become the
+ * literal bytes of the file's contents.
+ * @throws UnknownRunException
+ * If the server doesn't know about the run or if the user is
+ * not permitted to see it.
+ * @throws NoUpdateException
+ * If the user is not allowed to make modifications to the run.
+ * @throws FilesystemAccessException
+ * If some assumption is violated (e.g., writing the contents of
+ * a directory).
+ * @throws NoDirectoryEntryException
+ * If the file doesn't exist.
+ */
+ @WSDLDocumentation("Set the contents of a file under the run's working directory from the contents of a publicly readable URI.")
+ void setRunFileContentsFromURI(@WebParam(name = "runName") String runName,
+ @WebParam(name = "fileName") DirEntryReference file,
+ @WebParam(name = "contents") URI reference)
+ throws UnknownRunException, NoUpdateException,
+ FilesystemAccessException, NoDirectoryEntryException;
+
+ /**
* Get the length of any file (in bytes) at/under the run's working
* directory. Runs do not share working directories.
*
diff --git a/server-webapp/src/main/java/org/taverna/server/master/soap/WrappedWorkflow.java b/server-webapp/src/main/java/org/taverna/server/master/soap/WrappedWorkflow.java
new file mode 100644
index 0000000..cd06115
--- /dev/null
+++ b/server-webapp/src/main/java/org/taverna/server/master/soap/WrappedWorkflow.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 The University of Manchester
+ *
+ * See the file "LICENSE.txt" for license terms.
+ */
+package org.taverna.server.master.soap;
+
+import static javax.xml.bind.annotation.XmlAccessType.NONE;
+import static org.apache.commons.io.IOUtils.closeQuietly;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlMimeType;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import org.taverna.server.master.common.Workflow;
+
+import uk.org.taverna.scufl2.api.io.ReaderException;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+import uk.org.taverna.scufl2.api.io.WriterException;
+
+/**
+ * An MTOM-capable description of how to transfer the contents of a file.
+ *
+ * @author Donal Fellows
+ */
+@XmlType(name = "WorkflowReference")
+@XmlAccessorType(NONE)
+public class WrappedWorkflow {
+ @XmlMimeType("application/octet-stream")
+ // JAXB bug: must be this
+ public DataHandler workflowData;
+ Workflow workflow;
+
+ /**
+ * Initialize the contents of this descriptor from the given file and
+ * content type.
+ *
+ * @param workflow
+ * The workflow that is to be reported.
+ */
+ public void setWorkflow(Workflow workflow) {
+ workflowData = new DataHandler(new WorkflowSource(workflow));
+ }
+
+ @XmlTransient
+ public Workflow getWorkflow() throws IOException {
+ if (workflow != null)
+ return workflow;
+ try {
+ return new Workflow(new WorkflowBundleIO().readBundle(
+ workflowData.getInputStream(), null));
+ } catch (ReaderException e) {
+ throw new IOException("problem converting to scufl2 bundle", e);
+ }
+ }
+}
+
+/**
+ * A data source that knows how to deliver a workflow.
+ *
+ * @author Donal Fellows
+ */
+class WorkflowSource implements DataSource {
+ WorkflowSource(Workflow workflow) {
+ this.wf = workflow;
+ this.io = new WorkflowBundleIO();
+ }
+
+ Workflow wf;
+ final WorkflowBundleIO io;
+
+ @Override
+ public String getContentType() {
+ return wf.getPreferredContentType().getContentType();
+ }
+
+ @Override
+ public String getName() {
+ switch (wf.getPreferredContentType()) {
+ case SCUFL2:
+ return "workflow.scufl2";
+ case T2FLOW:
+ return "workflow.t2flow";
+ default:
+ return "workflow";
+ }
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ PipedInputStream is = new PipedInputStream();
+ final OutputStream os = new PipedOutputStream(is);
+ new Worker() {
+ @Override
+ public void doWork() throws WriterException, IOException {
+ io.writeBundle(wf.getScufl2Workflow(), os, wf
+ .getPreferredContentType().getContentType());
+ }
+
+ @Override
+ public void doneWork() {
+ closeQuietly(os);
+ }
+ };
+ return is;
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ final PipedInputStream is = new PipedInputStream();
+ OutputStream os = new PipedOutputStream(is);
+ new Worker() {
+ @Override
+ public void doWork() throws IOException, ReaderException {
+ wf = new Workflow(io.readBundle(is, null));
+ }
+
+ @Override
+ public void doneWork() {
+ closeQuietly(is);
+ }
+ };
+ return os;
+ }
+
+ static abstract class Worker extends Thread {
+ public Worker() {
+ setDaemon(true);
+ start();
+ }
+
+ public abstract void doWork() throws Exception;
+
+ public abstract void doneWork();
+
+ @Override
+ public void run() {
+ try {
+ doWork();
+ } catch (Exception e) {
+ // do nothing.
+ } finally {
+ doneWork();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server-webapp/src/main/java/org/taverna/server/master/utils/WSDLHeadOptionsInterceptor.java b/server-webapp/src/main/java/org/taverna/server/master/utils/WSDLHeadOptionsInterceptor.java
new file mode 100644
index 0000000..96cdc6d
--- /dev/null
+++ b/server-webapp/src/main/java/org/taverna/server/master/utils/WSDLHeadOptionsInterceptor.java
@@ -0,0 +1,65 @@
+package org.taverna.server.master.utils;
+
+import static org.apache.commons.logging.LogFactory.getLog;
+import static org.apache.cxf.common.util.UrlUtils.parseQueryString;
+import static org.apache.cxf.message.Message.HTTP_REQUEST_METHOD;
+import static org.apache.cxf.message.Message.QUERY_STRING;
+import static org.apache.cxf.message.Message.REQUEST_URL;
+import static org.apache.cxf.phase.Phase.READ;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.cxf.binding.soap.interceptor.EndpointSelectionInterceptor;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+
+
+/**
+ * Thunk for TAVSERV-293.
+ *
+ * @author Donal Fellows (based on work by Daniel Hagen)
+ */
+public class WSDLHeadOptionsInterceptor extends
+ AbstractPhaseInterceptor<Message> {
+ public static final Log log = getLog("Taverna.Server.Utils");
+
+ public WSDLHeadOptionsInterceptor() {
+ super(READ);
+ getAfter().add(EndpointSelectionInterceptor.class.getName());
+ }
+
+ @Override
+ public void handleMessage(Message message) throws Fault {
+ String method = (String) message.get(HTTP_REQUEST_METHOD);
+ String query = (String) message.get(QUERY_STRING);
+
+ if (("HEAD".equals(method) || "OPTIONS".equals(method))
+ && query != null && !query.trim().isEmpty()
+ && isRecognizedQuery(query)) {
+ log.debug("adjusting message request method " + method + " for "
+ + message.get(REQUEST_URL) + " to GET");
+ message.put(HTTP_REQUEST_METHOD, "GET");
+ }
+ }
+
+ /*
+ * Stolen from http://permalink.gmane.org/gmane.comp.apache.cxf.user/20037
+ * which is itself in turn stolen from
+ * org.apache.cxf.frontend.WSDLGetInterceptor.isRecognizedQuery
+ */
+ /**
+ * Is this a query for WSDL or XSD relating to it?
+ *
+ * @param query
+ * The query string to check
+ * @return If the query is one to handle.
+ * @see org.apache.cxf.frontend.WSDLGetInterceptor#isRecognizedQuery(Map,String,String,org.apache.cxf.service.model.EndpointInfo)
+ * WSDLGetInterceptor
+ */
+ private boolean isRecognizedQuery(String query) {
+ Map<String, String> map = parseQueryString(query);
+ return map.containsKey("wsdl") || map.containsKey("xsd");
+ }
+}
diff --git a/server-webapp/src/main/replacementscripts/executeworkflow.sh b/server-webapp/src/main/replacementscripts/executeworkflow.sh
index 8885cb1..10d73b2 100644
--- a/server-webapp/src/main/replacementscripts/executeworkflow.sh
+++ b/server-webapp/src/main/replacementscripts/executeworkflow.sh
@@ -8,7 +8,7 @@
## Parse the command line to extract the pieces to move around to before or
## after the JAR filename...
-pre=
+pre=-Djava.awt.headless=true
post=
for arg
do
@@ -40,17 +40,17 @@
if test -x "$JAVA_HOME/bin/java"; then
javabin="$JAVA_HOME/bin/java"
fi
-RAVEN_APPHOME_PROP=
-if test x != "x$RAVEN_APPHOME"; then
- RAVEN_APPHOME_PROP="-Draven.launcher.app.home=$RAVEN_APPHOME"
+APPHOME_PROP=
+if test x != "x$TAVERNA_APPHOME"; then
+ APPHOME_PROP="-Dtaverna.app.home=$TAVERNA_APPHOME"
fi
RUNID_PROP=
if test x != "x$TAVERNA_RUN_ID"; then
RUNID_PROP="-Dtaverna.runid=$TAVERNA_RUN_ID"
fi
-INTERACTION_PROPS=
+INTERACTION_PROPS=-Dtaverna.interaction.ignore_requests=true
if test x != "x$INTERACTION_HOST"; then
- INTERACTION_PROPS="-Dtaverna.interaction.host=$INTERACTION_HOST"
+ INTERACTION_PROPS="$INTERACTION_PROPS -Dtaverna.interaction.host=$INTERACTION_HOST"
INTERACTION_PROPS="$INTERACTION_PROPS -Dtaverna.interaction.port=$INTERACTION_PORT"
INTERACTION_PROPS="$INTERACTION_PROPS -Dtaverna.interaction.webdav_path=$INTERACTION_WEBDAV"
INTERACTION_PROPS="$INTERACTION_PROPS -Dtaverna.interaction.feed_path=$INTERACTION_FEED"
@@ -63,14 +63,9 @@
echo "pid:$$"
exec "$javabin" $memlimit $permsize \
- "-Dcom.sun.net.ssl.enableECC=false" \
- "-Djsse.enableSNIExtension=false" \
- "-Draven.profile=file://$taverna_home/conf/current-profile.xml" \
- "-Dtaverna.startup=$taverna_home" $RAVEN_APPHOME_PROP $RUNID_PROP \
- $INTERACTION_PROPS $pre \
- -Djava.system.class.loader=net.sf.taverna.raven.prelauncher.BootstrapClassLoader \
- -Draven.launcher.app.main=$MainClass \
- -Draven.launcher.show_splashscreen=false \
- -Djava.awt.headless=true -Dtaverna.interaction.ignore_requests=true \
- -jar "$taverna_home/lib/"prelauncher-*.jar \
- "$@"
+ "-Dlog4j.configuration=file://$taverna_home/conf/log4j.properties " \
+ "-Djava.util.logging.config.file=$taverna_home/conf/logging.properties " \
+ "-Dtaverna.app.startup=$taverna_home" -Dtaverna.interaction.ignore_requests=true \
+ $APPHOME_PROP $RUNID_PROP $INTERACTION_PROPS -Djava.awt.headless=true $pre \
+ -jar "$taverna_home/lib/taverna-command-line-0.1.1.jar" \
+ ${1+"$@"}
diff --git a/server-webapp/src/main/resources/welcome.html b/server-webapp/src/main/resources/welcome.html
index 81b3b06..f80da4a 100644
--- a/server-webapp/src/main/resources/welcome.html
+++ b/server-webapp/src/main/resources/welcome.html
@@ -6,6 +6,13 @@
</head>
<body>
<h1>Taverna Server %{VERSION}</h1>
+<div style="text-align;center">
+ <p>
+ <i>Note that this is a pre-release version. Significant known
+ issues remain open and it is not guaranteed that the service API
+ will be stable.</i>
+ </p>
+</div>
<p>For a full list of operations, see the <a
href="%{BASEURL}/services">service listing</a> generated by Apache
CXF, which indicates where to access the WSDL and WADL descriptions of
diff --git a/server-webapp/src/main/webapp/WEB-INF/providers.xml b/server-webapp/src/main/webapp/WEB-INF/providers.xml
index 0bb03eb..43531ab 100644
--- a/server-webapp/src/main/webapp/WEB-INF/providers.xml
+++ b/server-webapp/src/main/webapp/WEB-INF/providers.xml
@@ -88,6 +88,7 @@
<bean id="Provider.RuntimeExceptionRemapping" class="org.taverna.server.master.utils.RuntimeExceptionWrapper" />
<bean id="MessagingProvider.ZipStream" class="org.taverna.server.master.rest.handler.ZipStreamHandler" />
+ <bean id="MessagingProvider.URIList" class="org.taverna.server.master.rest.handler.URIListHandler" />
<bean id="Interceptor.FlushThreadLocalCache"
class="org.taverna.server.master.utils.FlushThreadLocalCacheInterceptor"
lazy-init="false">
diff --git a/server-webapp/src/main/webapp/WEB-INF/webappBeans.xml b/server-webapp/src/main/webapp/WEB-INF/webappBeans.xml
index a22664a..22e74b3 100644
--- a/server-webapp/src/main/webapp/WEB-INF/webappBeans.xml
+++ b/server-webapp/src/main/webapp/WEB-INF/webappBeans.xml
@@ -92,7 +92,8 @@
<ref bean="MessagingProvider.InputStream" />
<ref bean="MessagingProvider.T2flow" />
<ref bean="MessagingProvider.Permission" />
- <ref bean="MessagingProvider.ZipStream"/>
+ <ref bean="MessagingProvider.URIList" />
+ <ref bean="MessagingProvider.ZipStream" />
<ref bean="jsonProvider" />
<ref bean="atomEntryHandler" />
<ref bean="atomFeedHandler" />
@@ -161,6 +162,14 @@
<security:authentication-provider ref="authProvider" />
</security:authentication-manager>
+ <bean id="WSDLHeadOptionsInterceptor"
+ class="org.taverna.server.master.utils.WSDLHeadOptionsInterceptor" />
+ <cxf:bus>
+ <cxf:inInterceptors>
+ <ref bean="WSDLHeadOptionsInterceptor" />
+ </cxf:inInterceptors>
+ </cxf:bus>
+
<aop:aspectj-autoproxy proxy-target-class="true" />
<security:global-method-security
jsr250-annotations="enabled" />
diff --git a/server-webapp/src/test/java/org/taverna/server/master/JaxbSanityTest.java b/server-webapp/src/test/java/org/taverna/server/master/JaxbSanityTest.java
index 3b6f75d..79c026b 100644
--- a/server-webapp/src/test/java/org/taverna/server/master/JaxbSanityTest.java
+++ b/server-webapp/src/test/java/org/taverna/server/master/JaxbSanityTest.java
@@ -27,6 +27,7 @@
import org.taverna.server.master.common.DirEntryReference;
import org.taverna.server.master.common.InputDescription;
import org.taverna.server.master.common.Permission;
+import org.taverna.server.master.common.ProfileList;
import org.taverna.server.master.common.RunReference;
import org.taverna.server.master.common.Status;
import org.taverna.server.master.common.Trust;
@@ -245,6 +246,11 @@
}
@Test
+ public void testJAXBForProfileList() throws Exception {
+ testJAXB(ProfileList.class);
+ }
+
+ @Test
public void testJAXBForDirEntry() throws Exception {
testJAXB(DirEntry.class);
}
@@ -276,7 +282,7 @@
TavernaServerSecurityREST.Descriptor.class,
TavernaServerSecurityREST.PermissionDescription.class,
TavernaServerSecurityREST.PermissionsDescription.class,
- Capability.class, CapabilityList.class);
+ ProfileList.class, Capability.class, CapabilityList.class);
}
@Test
@@ -285,7 +291,7 @@
Permission.class, PermissionList.class,
PermissionList.SinglePermissionMapping.class,
RunReference.class, Status.class, Trust.class, Uri.class,
- Workflow.class, Capability.class);
+ ProfileList.class, Workflow.class, Capability.class);
}
@Test
diff --git a/server-webapp/src/test/java/org/taverna/server/master/WorkflowSerializationTest.java b/server-webapp/src/test/java/org/taverna/server/master/WorkflowSerializationTest.java
index 4d49d17..0450317 100644
--- a/server-webapp/src/test/java/org/taverna/server/master/WorkflowSerializationTest.java
+++ b/server-webapp/src/test/java/org/taverna/server/master/WorkflowSerializationTest.java
@@ -1,5 +1,8 @@
package org.taverna.server.master;
+import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW_NS;
+import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW_ROOTNAME;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -13,6 +16,7 @@
import org.junit.Assert;
import org.junit.Test;
import org.taverna.server.master.common.Workflow;
+import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -21,15 +25,16 @@
public void testWorkflowSerialization()
throws ParserConfigurationException, IOException,
ClassNotFoundException {
- Workflow w = new Workflow();
- w.content = new Element[1];
DocumentBuilder db = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
Document doc = db.getDOMImplementation().createDocument(null, null,
null);
- w.content[0] = doc.createElement("foo");
- w.content[0].setTextContent("bar");
- w.content[0].setAttribute("xyz", "abc");
+ Element workflow = doc.createElementNS(T2FLOW_NS, T2FLOW_ROOTNAME);
+ Element foo = doc.createElementNS("urn:foo:bar", "pqr:foo");
+ foo.setTextContent("bar");
+ foo.setAttribute("xyz", "abc");
+ workflow.appendChild(foo);
+ Workflow w = new Workflow(workflow);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
@@ -45,12 +50,19 @@
Assert.assertNotNull(o);
Assert.assertEquals(w.getClass(), o.getClass());
Workflow w2 = (Workflow) o;
- Assert.assertNotNull(w2.content);
- Assert.assertEquals(1, w2.content.length);
- Element e = w2.content[0];
- Assert.assertEquals("foo", e.getTagName());
+ Assert.assertNotNull(w2.getT2flowWorkflow());
+ Element e = w2.getT2flowWorkflow();
+ Assert.assertEquals(T2FLOW_ROOTNAME, e.getLocalName());
+ Assert.assertEquals(T2FLOW_NS, e.getNamespaceURI());
+ e = (Element) e.getFirstChild();
+ Assert.assertEquals("foo", e.getLocalName());
+ Assert.assertEquals("pqr", e.getPrefix());
+ Assert.assertEquals("urn:foo:bar", e.getNamespaceURI());
Assert.assertEquals("bar", e.getTextContent());
Assert.assertEquals(1, e.getChildNodes().getLength());
+ // WARNING: These are dependent on how namespaces are encoded!
+ Assert.assertEquals(2, e.getAttributes().getLength());
+ Assert.assertEquals("xyz", ((Attr) e.getAttributes().item(1)).getLocalName());
Assert.assertEquals("abc", e.getAttribute("xyz"));
}
}
diff --git a/server-worker/pom.xml b/server-worker/pom.xml
index df2ada8..70239a2 100644
--- a/server-worker/pom.xml
+++ b/server-worker/pom.xml
@@ -6,7 +6,7 @@
<parent>
<groupId>uk.org.taverna.server</groupId>
<artifactId>server</artifactId>
- <version>2.5.5-SNAPSHOT</version>
+ <version>3.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<scm>
@@ -15,6 +15,7 @@
<properties>
<workerMainClass>org.taverna.server.localworker.impl.TavernaRunManager</workerMainClass>
+ <scufl2.version>0.9.2</scufl2.version>
</properties>
<dependencies>
@@ -41,6 +42,23 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
+ <dependency>
+ <groupId>uk.org.taverna.scufl2</groupId>
+ <artifactId>scufl2-api</artifactId>
+ <version>${scufl2.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>uk.org.taverna.scufl2</groupId>
+ <artifactId>scufl2-t2flow</artifactId>
+ <version>${scufl2.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>uk.org.taverna.scufl2</groupId>
+ <artifactId>scufl2-rdfxml</artifactId>
+ <version>${scufl2.version}</version>
+ <scope>runtime</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/server-worker/src/main/java/org/taverna/server/localworker/api/Worker.java b/server-worker/src/main/java/org/taverna/server/localworker/api/Worker.java
index 7b9f012..c513ed8 100644
--- a/server-worker/src/main/java/org/taverna/server/localworker/api/Worker.java
+++ b/server-worker/src/main/java/org/taverna/server/localworker/api/Worker.java
@@ -71,7 +71,7 @@
* If any of quite a large number of things goes wrong.
*/
boolean initWorker(LocalWorker local, String executeWorkflowCommand,
- String workflow, File workingDir, File inputBaclavaFile,
+ byte[] workflow, File workingDir, File inputBaclavaFile,
Map<String, File> inputRealFiles, Map<String, String> inputValues,
Map<String, String> inputDelimiters, File outputBaclavaFile,
File contextDirectory, char[] keystorePassword,
diff --git a/server-worker/src/main/java/org/taverna/server/localworker/impl/LocalWorker.java b/server-worker/src/main/java/org/taverna/server/localworker/impl/LocalWorker.java
index 29755c5..adf3ea7 100644
--- a/server-worker/src/main/java/org/taverna/server/localworker/impl/LocalWorker.java
+++ b/server-worker/src/main/java/org/taverna/server/localworker/impl/LocalWorker.java
@@ -97,7 +97,7 @@
/** What to use to run a workflow engine. */
private final String executeWorkflowCommand;
/** What workflow to run. */
- private final String workflow;
+ private final byte[] workflow;
/** The remote access object for the working directory. */
private final DirectoryDelegate baseDir;
/** What inputs to pass as files. */
@@ -196,7 +196,7 @@
* @throws ImplementationException
* If something goes wrong during local setup.
*/
- protected LocalWorker(String executeWorkflowCommand, String workflow,
+ protected LocalWorker(String executeWorkflowCommand, byte[] workflow,
UsageRecordReceiver urReceiver, UUID id,
Map<String, String> seedEnvironment, List<String> javaParams,
WorkerFactory workerFactory) throws RemoteException,
diff --git a/server-worker/src/main/java/org/taverna/server/localworker/impl/TavernaRunManager.java b/server-worker/src/main/java/org/taverna/server/localworker/impl/TavernaRunManager.java
index a4c9a7e..03ee69d 100644
--- a/server-worker/src/main/java/org/taverna/server/localworker/impl/TavernaRunManager.java
+++ b/server-worker/src/main/java/org/taverna/server/localworker/impl/TavernaRunManager.java
@@ -19,8 +19,8 @@
import static org.taverna.server.localworker.api.Constants.SEC_POLICY_PROP;
import static org.taverna.server.localworker.api.Constants.UNSECURE_PROP;
-import java.io.StringReader;
-import java.io.StringWriter;
+import java.io.ByteArrayInputStream;
+import java.net.URI;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
@@ -31,12 +31,6 @@
import java.util.Map;
import java.util.UUID;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.ws.Holder;
-
import org.taverna.server.localworker.api.RunAccounting;
import org.taverna.server.localworker.api.Worker;
import org.taverna.server.localworker.api.WorkerFactory;
@@ -45,9 +39,8 @@
import org.taverna.server.localworker.server.UsageRecordReceiver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
+
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
/**
* The registered factory for runs, this class is responsible for constructing
@@ -57,20 +50,19 @@
* @author Donal Fellows
* @see LocalWorker
*/
-@java.lang.SuppressWarnings("serial")
+@SuppressWarnings("serial")
public class TavernaRunManager extends UnicastRemoteObject implements
RemoteRunFactory, RunAccounting, WorkerFactory {
- DocumentBuilderFactory dbf;
- TransformerFactory tf;
String command;
+ Map<String, String> seedEnvironment = new HashMap<>();
+ List<String> javaInitParams = new ArrayList<>();
+ private WorkflowBundleIO io;
+ private int activeRuns = 0;
// Hacks!
public static String interactionHost;
public static String interactionPort;
public static String interactionWebdavPath;
public static String interactionFeedPath;
- Map<String, String> seedEnvironment = new HashMap<>();
- List<String> javaInitParams = new ArrayList<>();
- private int activeRuns = 0;
/**
* How to get the actual workflow document from the XML document that it is
@@ -97,55 +89,18 @@
*/
public TavernaRunManager(String command) throws RemoteException {
this.command = command;
- this.dbf = DocumentBuilderFactory.newInstance();
- this.dbf.setNamespaceAware(true);
- this.dbf.setCoalescing(true);
- this.tf = TransformerFactory.newInstance();
- }
-
- /**
- * Do the unwrapping of a workflow to extract the contents of the file to
- * feed into the Taverna core.
- *
- * @param workflow
- * The string containing the workflow to extract.
- * @param wfid
- * A place to store the extracted workflow ID.
- * @return The extracted workflow description.
- * @throws RemoteException
- * If anything goes wrong.
- */
- private String unwrapWorkflow(String workflow, Holder<String> wfid)
- throws RemoteException {
- StringReader sr = new StringReader(workflow);
- StringWriter sw = new StringWriter();
- try {
- Document doc = dbf.newDocumentBuilder().parse(new InputSource(sr));
- // Try to extract the t2flow's ID.
- NodeList nl = doc.getElementsByTagNameNS(
- "http://taverna.sf.net/2008/xml/t2flow", "dataflow");
- if (nl.getLength() > 0) {
- Node n = nl.item(0).getAttributes().getNamedItem("id");
- if (n != null)
- wfid.value = n.getTextContent();
- }
- tf.newTransformer().transform(new DOMSource(unwrapWorkflow(doc)),
- new StreamResult(sw));
- return sw.toString();
- } catch (Exception e) {
- throw new RemoteException("failed to extract contained workflow", e);
- }
+ this.io = new WorkflowBundleIO();
}
@Override
- public RemoteSingleRun make(String workflow, String creator,
+ public RemoteSingleRun make(byte[] workflow, String creator,
UsageRecordReceiver urReceiver, UUID id) throws RemoteException {
if (creator == null)
throw new RemoteException("no creator");
try {
- Holder<String> wfid = new Holder<>("???");
- workflow = unwrapWorkflow(workflow, wfid);
- out.println("Creating run from workflow <" + wfid.value + "> for <"
+ URI wfid = io.readBundle(new ByteArrayInputStream(workflow), null)
+ .getMainWorkflow().getWorkflowIdentifier();
+ out.println("Creating run from workflow <" + wfid + "> for <"
+ creator + ">");
return new LocalWorker(command, workflow, urReceiver, id,
seedEnvironment, javaInitParams, this);
@@ -235,6 +190,7 @@
for (int i = 1; i < args.length - 1; i++)
man.addArgument(args[i]);
registry = getRegistry(LOCALHOST);
+
registry.bind(factoryName, man);
getRuntime().addShutdownHook(new Thread() {
@Override
diff --git a/server-worker/src/main/java/org/taverna/server/localworker/impl/WorkerCore.java b/server-worker/src/main/java/org/taverna/server/localworker/impl/WorkerCore.java
index c91fdcc..1a6cff8 100644
--- a/server-worker/src/main/java/org/taverna/server/localworker/impl/WorkerCore.java
+++ b/server-worker/src/main/java/org/taverna/server/localworker/impl/WorkerCore.java
@@ -44,6 +44,7 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -200,19 +201,22 @@
* If any of quite a large number of things goes wrong.
*/
@Override
- public boolean initWorker(@Nonnull final LocalWorker local,
+ public boolean initWorker(
+ @Nonnull final LocalWorker local,
@Nonnull final String executeWorkflowCommand,
- @Nonnull final String workflow, @Nonnull final File workingDir,
+ @Nonnull final byte[] workflow,
+ @Nonnull final File workingDir,
@Nullable final File inputBaclava,
@Nonnull final Map<String, File> inputFiles,
- @Nonnull final Map<String, String> inputValues,
+ @Nonnull final Map<String, String> inputValues,
@Nonnull final Map<String, String> inputDelimiters,
@Nullable final File outputBaclava,
- @Nonnull final File securityDir, @Nullable final char[] password,
+ @Nonnull final File securityDir,
+ @Nullable final char[] password,
final boolean generateProvenance,
@Nonnull final Map<String, String> environment,
- @Nonnull final String token, @Nonnull final List<String> runtime)
- throws IOException {
+ @Nullable final String token,
+ @Nonnull final List<String> runtime) throws IOException {
try {
new TimingOutTask() {
@Override
@@ -291,7 +295,7 @@
*/
@Nonnull
ProcessBuilder createProcessBuilder(@Nonnull LocalWorker local,
- @Nonnull String executeWorkflowCommand, @Nonnull String workflow,
+ @Nonnull String executeWorkflowCommand, @Nonnull byte[] workflow,
@Nonnull File workingDir, @Nullable File inputBaclava,
@Nonnull Map<String, File> inputFiles,
@Nonnull Map<String, String> inputValues,
@@ -407,11 +411,10 @@
}
// Add an argument holding the workflow
- workflowFile = createTempFile(".wf_", ".t2flow", workingDir);
- write(workflowFile, workflow, "UTF-8");
- if (!workflowFile.exists())
- throw new IOException("failed to instantiate workflow file at "
- + workflowFile);
+ File tmp = createTempFile(".wf_", ".scufl2", workingDir);
+ try (OutputStream os = new FileOutputStream(tmp)) {
+ os.write(workflow);
+ }
pb.command().add(workflowFile.getAbsolutePath());
// Indicate what working directory to use
@@ -430,7 +433,7 @@
env.put("PATH", new File(System.getProperty("java.home"), "bin")
+ pathSeparator + env.get("PATH"));
// Patch the environment to deal with TAVSERV-189
- env.put("RAVEN_APPHOME", workingDir.getCanonicalPath());
+ env.put("TAVERNA_APPHOME", workingDir.getCanonicalPath());
// Patch the environment to deal with TAVSERV-224
env.put("TAVERNA_RUN_ID", token);
if (interactionHost != null || local.interactionFeedURL != null
diff --git a/server-worker/src/test/java/org/taverna/server/localworker/impl/LocalWorkerTest.java b/server-worker/src/test/java/org/taverna/server/localworker/impl/LocalWorkerTest.java
index ef92cc5..7bcd92e 100644
--- a/server-worker/src/test/java/org/taverna/server/localworker/impl/LocalWorkerTest.java
+++ b/server-worker/src/test/java/org/taverna/server/localworker/impl/LocalWorkerTest.java
@@ -90,7 +90,7 @@
@Override
public boolean initWorker(LocalWorker local,
- String executeWorkflowCommand, String workflow,
+ String executeWorkflowCommand, byte[] workflow,
File workingDir, File inputBaclava,
Map<String, File> inputFiles, Map<String, String> inputValues,
Map<String, String> delimiters, File outputBaclava, File cmdir,
@@ -98,7 +98,7 @@
String id, List<String> conf) throws Exception {
events.add("init[");
events.add(executeWorkflowCommand);
- events.add(workflow);
+ events.add(new String(workflow, "UTF-8"));
int dirLen = workingDir.getName().length();
events.add(Integer.toString(dirLen));
events.add(inputBaclava == null ? "<null>" : inputBaclava
@@ -154,7 +154,7 @@
@Before
public void setUp() throws Exception {
- lw = new LocalWorker("XWC", "WF", null, randomUUID(),
+ lw = new LocalWorker("XWC", "WF".getBytes("UTF-8"), null, randomUUID(),
new HashMap<String, String>(), new ArrayList<String>(), factory);
events = new ArrayList<>();
returnThisStatus = RemoteStatus.Operating;
@@ -548,4 +548,4 @@
"{bar=<null>, foo=foofile}",
"{bar=barvalue, foo=null}", "boo", "]", "kill"), events);
}
-}
\ No newline at end of file
+}