You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2018/01/09 23:30:41 UTC
[25/42] incubator-taverna-server git commit: package org.taverna ->
org.apache.taverna
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerRunREST.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerRunREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerRunREST.java
new file mode 100644
index 0000000..ef6ddd4
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerRunREST.java
@@ -0,0 +1,810 @@
+/*
+ */
+package org.taverna.server.master.rest;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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;
+import static org.taverna.server.master.rest.ContentTypes.TEXT;
+import static org.taverna.server.master.rest.ContentTypes.XML;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.DIR;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.GENERATE_PROVENANCE;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.IN;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.LISTEN;
+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;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.STATUS;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.STDERR;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.STDOUT;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.T_CREATE;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.T_EXPIRE;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.T_FINISH;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.T_START;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.USAGE;
+import static org.taverna.server.master.rest.TavernaServerRunREST.PathNames.WF;
+import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.annotation.XmlAttribute;
+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.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;
+import org.taverna.server.master.common.Workflow;
+import org.taverna.server.master.exceptions.BadStateChangeException;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.exceptions.NoDirectoryEntryException;
+import org.taverna.server.master.exceptions.NoListenerException;
+import org.taverna.server.master.exceptions.NoUpdateException;
+import org.taverna.server.master.exceptions.NotOwnerException;
+import org.taverna.server.master.interfaces.Listener;
+import org.taverna.server.master.interfaces.TavernaRun;
+import org.taverna.server.port_description.OutputDescription;
+
+/**
+ * This represents how a Taverna Server workflow run looks to a RESTful API.
+ *
+ * @author Donal Fellows.
+ */
+@Description("This represents how a Taverna Server workflow run looks to a "
+ + "RESTful API.")
+@RolesAllowed(USER)
+public interface TavernaServerRunREST {
+ /**
+ * Describes a workflow run.
+ *
+ * @param ui
+ * About the URI used to access this resource.
+ * @return The description.
+ */
+ @GET
+ @Path(ROOT)
+ @Description("Describes a workflow run.")
+ @Produces({ XML, JSON })
+ @Nonnull
+ public RunDescription getDescription(@Nonnull @Context UriInfo ui);
+
+ /**
+ * Deletes a workflow run.
+ *
+ * @return An HTTP response to the deletion.
+ * @throws NoUpdateException
+ * If the user may see the handle but may not delete it.
+ */
+ @DELETE
+ @Path(ROOT)
+ @Description("Deletes a workflow run.")
+ @Nonnull
+ public Response destroy() throws NoUpdateException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(ROOT)
+ @Description("Produces the description of the run.")
+ Response runOptions();
+
+ /**
+ * Returns the workflow document used to create the workflow run.
+ *
+ * @return The workflow document.
+ */
+ @GET
+ @Path(WF)
+ @Produces({ T2FLOW, SCUFL2, XML, JSON })
+ @Description("Gives the workflow document used to create the workflow run.")
+ @Nonnull
+ public Workflow getWorkflow();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(WF)
+ @Description("Produces the description of the run workflow.")
+ Response workflowOptions();
+
+ /** Get the workflow name. */
+ @GET
+ @Path(NAME)
+ @Produces(TEXT)
+ @Description("Gives the descriptive name of the workflow run.")
+ @Nonnull
+ public String getName();
+
+ /**
+ * Set the workflow name.
+ *
+ * @throws NoUpdateException
+ * If the user is not permitted to change the workflow.
+ */
+ @PUT
+ @Path(NAME)
+ @Consumes(TEXT)
+ @Produces(TEXT)
+ @Description("Set the descriptive name of the workflow run. Note that "
+ + "this value may be arbitrarily truncated by the implementation.")
+ @Nonnull
+ public String setName(String name) throws NoUpdateException;
+
+ /** Produce the workflow name HTTP operations. */
+ @OPTIONS
+ @Path(NAME)
+ @Description("Produces the description of the operations on the run's "
+ + "descriptive name.")
+ @Nonnull
+ 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.
+ *
+ * @return The security resource.
+ * @throws NotOwnerException
+ * If the accessing principal isn't the owning principal.
+ */
+ @Path(SEC)
+ @Description("Access the workflow run's security.")
+ @Nonnull
+ public TavernaServerSecurityREST getSecurity() throws NotOwnerException;
+
+ /**
+ * Returns the time when the workflow run becomes eligible for automatic
+ * deletion.
+ *
+ * @return When the run expires.
+ */
+ @GET
+ @Path(T_EXPIRE)
+ @Produces(TEXT)
+ @Description("Gives the time when the workflow run becomes eligible for "
+ + "automatic deletion.")
+ @Nonnull
+ public String getExpiryTime();
+
+ /**
+ * Sets the time when the workflow run becomes eligible for automatic
+ * deletion.
+ *
+ * @param expiry
+ * When the run will expire.
+ * @return When the run will actually expire.
+ * @throws NoUpdateException
+ * If the current user is not permitted to manage the lifetime
+ * of the run.
+ */
+ @PUT
+ @Path(T_EXPIRE)
+ @Consumes(TEXT)
+ @Produces(TEXT)
+ @Description("Sets the time when the workflow run becomes eligible for "
+ + "automatic deletion.")
+ @Nonnull
+ public String setExpiryTime(@Nonnull String expiry)
+ throws NoUpdateException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(T_EXPIRE)
+ @Description("Produces the description of the run expiry.")
+ Response expiryOptions();
+
+ /**
+ * Returns the time when the workflow run was created.
+ *
+ * @return When the run was first submitted to the server.
+ */
+ @GET
+ @Path(T_CREATE)
+ @Produces(TEXT)
+ @Description("Gives the time when the workflow run was first submitted "
+ + "to the server.")
+ @Nonnull
+ public String getCreateTime();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(T_CREATE)
+ @Description("Produces the description of the run create time.")
+ Response createTimeOptions();
+
+ /**
+ * Returns the time when the workflow run was started (through a user-driven
+ * state change).
+ *
+ * @return When the run was started, or <tt>null</tt>.
+ */
+ @GET
+ @Path(T_START)
+ @Produces(TEXT)
+ @Description("Gives the time when the workflow run was started, or an "
+ + "empty string if the run has not yet started.")
+ @Nonnull
+ public String getStartTime();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(T_START)
+ @Description("Produces the description of the run start time.")
+ Response startTimeOptions();
+
+ /**
+ * Returns the time when the workflow run was detected to have finished.
+ *
+ * @return When the run finished, or <tt>null</tt>.
+ */
+ @GET
+ @Path(T_FINISH)
+ @Produces(TEXT)
+ @Description("Gives the time when the workflow run was first detected as "
+ + "finished, or an empty string if it has not yet finished "
+ + "(including if it has never started).")
+ @Nonnull
+ public String getFinishTime();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(T_FINISH)
+ @Description("Produces the description of the run finish time.")
+ Response finishTimeOptions();
+
+ /**
+ * Gets the current status of the workflow run.
+ *
+ * @return The status code.
+ */
+ @GET
+ @Path(STATUS)
+ @Produces(TEXT)
+ @Description("Gives the current status of the workflow run.")
+ @Nonnull
+ public String getStatus();
+
+ /**
+ * Sets the status of the workflow run. This does nothing if the status code
+ * is the same as the run's current state.
+ *
+ * @param status
+ * The new status code.
+ * @return Description of what status the run is actually in, or a 202 to
+ * indicate that things are still changing.
+ * @throws NoUpdateException
+ * If the current user is not permitted to update the run.
+ * @throws BadStateChangeException
+ * If the state cannot be modified in the manner requested.
+ */
+ @PUT
+ @Path(STATUS)
+ @Consumes(TEXT)
+ @Produces(TEXT)
+ @Description("Attempts to update the status of the workflow run.")
+ @Nonnull
+ public Response setStatus(@Nonnull String status) throws NoUpdateException,
+ BadStateChangeException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(STATUS)
+ @Description("Produces the description of the run status.")
+ Response statusOptions();
+
+ /**
+ * Get the working directory of this workflow run.
+ *
+ * @return A RESTful delegate for the working directory.
+ */
+ @Path(DIR)
+ @Description("Get the working directory of this workflow run.")
+ @Nonnull
+ public TavernaServerDirectoryREST getWorkingDirectory();
+
+ /**
+ * Get the event listeners attached to this workflow run.
+ *
+ * @return A RESTful delegate for the list of listeners.
+ */
+ @Path(LISTEN)
+ @Description("Get the event listeners attached to this workflow run.")
+ @Nonnull
+ public TavernaServerListenersREST getListeners();
+
+ /**
+ * Get a delegate for working with the inputs to this workflow run.
+ *
+ * @param ui
+ * About the URI used to access this resource.
+ * @return A RESTful delegate for the inputs.
+ */
+ @Path(IN)
+ @Description("Get the inputs to this workflow run.")
+ @Nonnull
+ public TavernaServerInputREST getInputs(@Nonnull @Context UriInfo ui);
+
+ /**
+ * Get the output Baclava file for this workflow run.
+ *
+ * @return The filename, or empty string to indicate that the outputs will
+ * be written to the <tt>out</tt> directory.
+ */
+ @GET
+ @Path(OUT)
+ @Produces(TEXT)
+ @Description("Gives the Baclava file where output will be written; empty "
+ + "means use multiple simple files in the out directory.")
+ @Nonnull
+ public String getOutputFile();
+
+ /**
+ * Get a description of the outputs.
+ *
+ * @param ui
+ * About the URI used to access this operation.
+ * @return A description of the outputs (higher level than the filesystem).
+ * @throws BadStateChangeException
+ * If the run is in the {@link Status#Initialized Initialized}
+ * state.
+ * @throws FilesystemAccessException
+ * If problems occur when accessing the filesystem.
+ * @throws NoDirectoryEntryException
+ * If things are odd in the filesystem.
+ */
+ @GET
+ @Path(OUT)
+ @Produces({ XML, JSON })
+ @Description("Gives a description of the outputs, as currently understood")
+ @Nonnull
+ public OutputDescription getOutputDescription(@Nonnull @Context UriInfo ui)
+ throws BadStateChangeException, FilesystemAccessException,
+ NoDirectoryEntryException;
+
+ /**
+ * Set the output Baclava file for this workflow run.
+ *
+ * @param filename
+ * The Baclava file to use, or empty to make the outputs be
+ * written to individual files in the <tt>out</tt> subdirectory
+ * of the working directory.
+ * @return The Baclava file as actually set.
+ * @throws NoUpdateException
+ * If the current user is not permitted to update the run.
+ * @throws FilesystemAccessException
+ * If the filename is invalid (starts with <tt>/</tt> or
+ * contains a <tt>..</tt> segment).
+ * @throws BadStateChangeException
+ * If the workflow is not in the Initialized state.
+ */
+ @PUT
+ @Path(OUT)
+ @Consumes(TEXT)
+ @Produces(TEXT)
+ @Description("Sets the Baclava file where output will be written; empty "
+ + "means use multiple simple files in the out directory.")
+ @Nonnull
+ public String setOutputFile(@Nonnull String filename)
+ throws NoUpdateException, FilesystemAccessException,
+ BadStateChangeException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(OUT)
+ @Description("Produces the description of the run output.")
+ Response outputOptions();
+
+ /**
+ * Get a handle to the interaction feed.
+ *
+ * @return
+ */
+ @Path(FEED_URL_DIR)
+ @Description("Access the interaction feed for the workflow run.")
+ @Nonnull
+ InteractionFeedREST getInteractionFeed();
+
+ /**
+ * @return The stdout for the workflow run, or empty string if the run has
+ * not yet started.
+ * @throws NoListenerException
+ */
+ @GET
+ @Path(STDOUT)
+ @Description("Return the stdout for the workflow run.")
+ @Produces(TEXT)
+ @Nonnull
+ String getStdout() throws NoListenerException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(STDOUT)
+ @Description("Return the stdout for the workflow run.")
+ Response stdoutOptions();
+
+ /**
+ * @return The stderr for the workflow run, or empty string if the run has
+ * not yet started.
+ * @throws NoListenerException
+ */
+ @GET
+ @Path(STDERR)
+ @Description("Return the stderr for the workflow run.")
+ @Produces(TEXT)
+ @Nonnull
+ String getStderr() throws NoListenerException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(STDERR)
+ @Description("Return the stderr for the workflow run.")
+ Response stderrOptions();
+
+ /**
+ * @return The usage record for the workflow run, wrapped in a Response, or
+ * "empty content" if the run has not yet finished.
+ * @throws NoListenerException
+ * @throws JAXBException
+ */
+ @GET
+ @Path(USAGE)
+ @Description("Return the usage record for the workflow run.")
+ @Produces(XML)
+ @Nonnull
+ Response getUsage() throws NoListenerException, JAXBException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(USAGE)
+ @Description("Return the usage record for the workflow run.")
+ Response usageOptions();
+
+ /**
+ * @return The log for the workflow run, or empty string if the run has not
+ * yet started.
+ */
+ @GET
+ @Path(LOG)
+ @Description("Return the log for the workflow run.")
+ @Produces(TEXT)
+ @Nonnull
+ Response getLogContents();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(LOG)
+ @Description("Return the log for the workflow run.")
+ Response logOptions();
+
+ /**
+ * @return The log for the workflow run, or empty string if the run has not
+ * yet started.
+ */
+ @GET
+ @Path(RUNBUNDLE)
+ @Description("Return the run bundle for the workflow run.")
+ @Produces(ROBUNDLE)
+ @Nonnull
+ Response getRunBundle();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(RUNBUNDLE)
+ @Description("Return the run bundle for the workflow run.")
+ Response runBundleOptions();
+
+ /**
+ * @return Whether to create the run bundle for the workflow run. Only
+ * usefully set-able before the start of the run.
+ */
+ @GET
+ @Path(GENERATE_PROVENANCE)
+ @Description("Whether to create the run bundle for the workflow run.")
+ @Produces(TEXT)
+ @Nonnull
+ boolean getGenerateProvenance();
+
+ /**
+ * @param provenanceFlag
+ * Whether to create the run bundle for the workflow run. Only
+ * usefully set-able before the start of the run.
+ * @return What it was actually set to.
+ * @throws NoUpdateException
+ */
+ @PUT
+ @Path(GENERATE_PROVENANCE)
+ @Description("Whether to create the run bundle for the workflow run.")
+ @Consumes(TEXT)
+ @Produces(TEXT)
+ @Nonnull
+ boolean setGenerateProvenance(boolean provenanceFlag) throws NoUpdateException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(GENERATE_PROVENANCE)
+ @Description("Whether to create the run bundle for the workflow run.")
+ Response generateProvenanceOptions();
+
+ /**
+ * Factored out path names used in the {@link TavernaServerRunREST}
+ * interface and related places.
+ *
+ * @author Donal Fellows
+ */
+ interface PathNames {
+ public static final String ROOT = "/";
+ public static final String WF = "workflow";
+ public static final String DIR = "wd";
+ public static final String NAME = "name";
+ public static final String T_EXPIRE = "expiry";
+ public static final String T_CREATE = "createTime";
+ public static final String T_START = "startTime";
+ public static final String T_FINISH = "finishTime";
+ 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";
+ public static final String STDERR = "stderr";
+ public static final String USAGE = "usage";
+ public static final String LOG = "log";
+ public static final String RUNBUNDLE = "run-bundle";
+ public static final String GENERATE_PROVENANCE = "generate-provenance";
+ }
+
+ /**
+ * The description of where everything is in a RESTful view of a workflow
+ * run. Done with JAXB.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement
+ @XmlType(name = "")
+ public static class RunDescription extends VersionedElement {
+ /** The identity of the owner of the workflow run. */
+ @XmlAttribute(namespace = Namespaces.SERVER_REST)
+ public String owner;
+ /** The description of the expiry. */
+ public Expiry expiry;
+ /** The location of the creation workflow description. */
+ public Uri creationWorkflow;
+ /** The location of the creation time property. */
+ public Uri createTime;
+ /** The location of the start time property. */
+ public Uri startTime;
+ /** The location of the finish time property. */
+ public Uri finishTime;
+ /** The location of the status description. */
+ public Uri status;
+ /** The location of the working directory. */
+ public Uri workingDirectory;
+ /** The location of the inputs. */
+ public Uri inputs;
+ /** The location of the Baclava output. */
+ public Uri output;
+ /** The location of the security context. */
+ public Uri securityContext;
+ /** The list of listeners. */
+ public ListenerList listeners;
+ /** The location of the interaction feed. */
+ public Uri interaction;
+ /** The name of the run. */
+ public Uri name;
+ /** The stdout of the run. */
+ public Uri stdout;
+ /** The stderr of the run. */
+ public Uri stderr;
+ /** The usage record for the run. */
+ public Uri usage;
+ /** The log from the run. */
+ public Uri log;
+ /** The bundle describing the run. */
+ @XmlElement(name = RUNBUNDLE)
+ public Uri runBundle;
+ /** Whether to generate a bundle describing the run. */
+ @XmlElement(name = GENERATE_PROVENANCE)
+ public Uri generateProvenance;
+
+ /**
+ * How to describe a run's expiry.
+ *
+ * @author Donal Fellows
+ */
+ @XmlType(name = "")
+ public static class Expiry {
+ /**
+ * Where to go to read the exiry
+ */
+ @XmlAttribute(name = "href", namespace = Namespaces.XLINK)
+ @XmlSchemaType(name = "anyURI")
+ public URI ref;
+ /**
+ * What the expiry currently is.
+ */
+ @XmlValue
+ public String timeOfDeath;
+
+ /**
+ * Make a blank expiry description.
+ */
+ public Expiry() {
+ }
+
+ private static DateTimeFormatter dtf;
+
+ Expiry(TavernaRun r, UriInfo ui, String path, String... parts) {
+ ref = fromUri(new Uri(ui, true, path, parts).ref).build();
+ if (dtf == null)
+ dtf = basicDateTime();
+ timeOfDeath = dtf.print(r.getExpiry().getTime());
+ }
+ }
+
+ /**
+ * The description of a list of listeners attached to a run.
+ *
+ * @author Donal Fellows
+ */
+ @XmlType(name = "")
+ public static class ListenerList extends Uri {
+ /**
+ * The references to the individual listeners.
+ */
+ public List<Uri> listener;
+
+ /**
+ * An empty description of listeners.
+ */
+ public ListenerList() {
+ listener = new ArrayList<>();
+ }
+
+ /**
+ * @param r
+ * The run whose listeners we're talking about.
+ * @param ub
+ * Uri factory; must've been secured
+ */
+ private ListenerList(TavernaRun r, UriBuilder ub) {
+ super(ub);
+ listener = new ArrayList<>(r.getListeners().size());
+ UriBuilder pathUB = ub.clone().path("{name}");
+ for (Listener l : r.getListeners())
+ listener.add(new Uri(pathUB.build(l.getName())));
+ }
+
+ /**
+ * @param run
+ * The run whose listeners we're talking about.
+ * @param ui
+ * The source of information about URIs.
+ * @param path
+ * Where we are relative to the URI source.
+ * @param parts
+ * Anything required to fill out the path.
+ */
+ ListenerList(TavernaRun run, UriInfo ui, String path,
+ String... parts) {
+ this(run, secure(fromUri(new Uri(ui, path, parts).ref)));
+ }
+ }
+
+ /**
+ * An empty description of a run.
+ */
+ public RunDescription() {
+ }
+
+ /**
+ * A description of a particular run.
+ *
+ * @param run
+ * The run to describe.
+ * @param ui
+ * The factory for URIs.
+ */
+ public RunDescription(TavernaRun run, UriInfo ui) {
+ super(true);
+ creationWorkflow = new Uri(ui, WF);
+ expiry = new Expiry(run, ui, T_EXPIRE);
+ status = new Uri(ui, STATUS);
+ workingDirectory = new Uri(ui, DIR);
+ listeners = new ListenerList(run, ui, LISTEN);
+ securityContext = new Uri(ui, SEC);
+ inputs = new Uri(ui, IN);
+ output = new Uri(ui, OUT);
+ createTime = new Uri(ui, T_CREATE);
+ startTime = new Uri(ui, T_START);
+ finishTime = new Uri(ui, T_FINISH);
+ interaction = new Uri(ui, FEED_URL_DIR);
+ name = new Uri(ui, NAME);
+ owner = run.getSecurityContext().getOwner().getName();
+ stdout = new Uri(ui, STDOUT);
+ stderr = new Uri(ui, STDERR);
+ usage = new Uri(ui, USAGE);
+ log = new Uri(ui, LOG);
+ runBundle = new Uri(ui, RUNBUNDLE);
+ generateProvenance = new Uri(ui, GENERATE_PROVENANCE);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerSecurityREST.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerSecurityREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerSecurityREST.java
new file mode 100644
index 0000000..f5101e7
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/TavernaServerSecurityREST.java
@@ -0,0 +1,788 @@
+/*
+ */
+package org.taverna.server.master.rest;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static java.util.Collections.emptyList;
+import static org.taverna.server.master.common.Namespaces.SERVER;
+import static org.taverna.server.master.common.Namespaces.XLINK;
+import static org.taverna.server.master.common.Roles.USER;
+import static org.taverna.server.master.rest.ContentTypes.JSON;
+import static org.taverna.server.master.rest.ContentTypes.TEXT;
+import static org.taverna.server.master.rest.ContentTypes.XML;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.CREDS;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.ONE_CRED;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.ONE_PERM;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.ONE_TRUST;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.OWNER;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.PERMS;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.ROOT;
+import static org.taverna.server.master.rest.TavernaServerSecurityREST.PathNames.TRUSTS;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.taverna.server.master.common.Credential;
+import org.taverna.server.master.common.Permission;
+import org.taverna.server.master.common.Trust;
+import org.taverna.server.master.common.Uri;
+import org.taverna.server.master.common.VersionedElement;
+import org.taverna.server.master.exceptions.BadStateChangeException;
+import org.taverna.server.master.exceptions.InvalidCredentialException;
+import org.taverna.server.master.exceptions.NoCredentialException;
+
+/**
+ * Manages the security of the workflow run. In general, only the owner of a run
+ * may access this resource. Many of these security-related resources may only
+ * be changed before the run is set to operating.
+ *
+ * @author Donal Fellows
+ */
+@RolesAllowed(USER)
+@Description("Manages the security of the workflow run. In general, only the "
+ + "owner of a run may access this resource.")
+public interface TavernaServerSecurityREST {
+ interface PathNames {
+ final String ROOT = "/";
+ final String OWNER = "owner";
+ final String CREDS = "credentials";
+ final String ONE_CRED = CREDS + "/{id}";
+ final String TRUSTS = "trusts";
+ final String ONE_TRUST = TRUSTS + "/{id}";
+ final String PERMS = "permissions";
+ final String ONE_PERM = PERMS + "/{id}";
+ }
+
+ /**
+ * Gets a description of the security information supported by the workflow
+ * run.
+ *
+ * @param ui
+ * About the URI used to access this resource.
+ * @return A description of the security information.
+ */
+ @GET
+ @Path(ROOT)
+ @Produces({ XML, JSON })
+ @Description("Gives a description of the security information supported "
+ + "by the workflow run.")
+ @Nonnull
+ Descriptor describe(@Nonnull @Context UriInfo ui);
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(ROOT)
+ @Description("Produces the description of the run security.")
+ Response descriptionOptions();
+
+ /**
+ * Gets the identity of who owns the workflow run.
+ *
+ * @return The name of the owner of the run.
+ */
+ @GET
+ @Path(OWNER)
+ @Produces(TEXT)
+ @Description("Gives the identity of who owns the workflow run.")
+ @Nonnull
+ String getOwner();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(OWNER)
+ @Description("Produces the description of the run owner.")
+ Response ownerOptions();
+
+ /*
+ * @PUT @Path("/") @Consumes(ContentTypes.BYTES) @CallCounted @Nonnull
+ * public void set(@Nonnull InputStream contents, @Nonnull @Context UriInfo
+ * ui);
+ */
+
+ /**
+ * @return A list of credentials supplied to this workflow run.
+ */
+ @GET
+ @Path(CREDS)
+ @Produces({ XML, JSON })
+ @Description("Gives a list of credentials supplied to this workflow run.")
+ @Nonnull
+ CredentialList listCredentials();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(CREDS)
+ @Description("Produces the description of the run credentials' operations.")
+ Response credentialsOptions();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(ONE_CRED)
+ @Description("Produces the description of one run credential's operations.")
+ Response credentialOptions(@PathParam("id") String id);
+
+ /**
+ * Describe a particular credential.
+ *
+ * @param id
+ * The id of the credential to fetch.
+ * @return The description of the credential.
+ * @throws NoCredentialException
+ * If the credential doesn't exist.
+ */
+ @GET
+ @Path(ONE_CRED)
+ @Produces({ XML, JSON })
+ @Description("Describes a particular credential.")
+ @Nonnull
+ CredentialHolder getParticularCredential(@Nonnull @PathParam("id") String id)
+ throws NoCredentialException;
+
+ /**
+ * Update a particular credential.
+ *
+ * @param id
+ * The id of the credential to update.
+ * @param c
+ * The details of the credential to use in the update.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return Description of the updated credential.
+ * @throws InvalidCredentialException
+ * If the credential description isn't valid.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @PUT
+ @Path(ONE_CRED)
+ @Consumes({ XML, JSON })
+ @Produces({ XML, JSON })
+ @Description("Updates a particular credential.")
+ @Nonnull
+ CredentialHolder setParticularCredential(
+ @Nonnull @PathParam("id") String id, @Nonnull CredentialHolder c,
+ @Nonnull @Context UriInfo ui) throws InvalidCredentialException,
+ BadStateChangeException;
+
+ /**
+ * Adds a new credential.
+ *
+ * @param c
+ * The details of the credential to create.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return Description of the created credential.
+ * @throws InvalidCredentialException
+ * If the credential description isn't valid.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @POST
+ @Path(CREDS)
+ @Consumes({ XML, JSON })
+ @Description("Creates a new credential.")
+ @Nonnull
+ Response addCredential(@Nonnull CredentialHolder c,
+ @Nonnull @Context UriInfo ui) throws InvalidCredentialException,
+ BadStateChangeException;
+
+ /**
+ * Deletes all credentials associated with a run.
+ *
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return A characterisation of a successful delete.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @DELETE
+ @Path(CREDS)
+ @Description("Deletes all credentials.")
+ @Nonnull
+ Response deleteAllCredentials(@Nonnull @Context UriInfo ui)
+ throws BadStateChangeException;
+
+ /**
+ * Deletes one credential associated with a run.
+ *
+ * @param id
+ * The identity of the credential to delete.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return A characterisation of a successful delete.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @DELETE
+ @Path(ONE_CRED)
+ @Description("Deletes a particular credential.")
+ @Nonnull
+ Response deleteCredential(@Nonnull @PathParam("id") String id,
+ @Nonnull @Context UriInfo ui) throws BadStateChangeException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(TRUSTS)
+ @Description("Produces the description of the run trusted certificates' "
+ + "operations.")
+ Response trustsOptions();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(ONE_TRUST)
+ @Description("Produces the description of one run trusted certificate's "
+ + "operations.")
+ Response trustOptions(@PathParam("id") String id);
+
+ /**
+ * @return A list of trusted identities supplied to this workflow run.
+ */
+ @GET
+ @Path(TRUSTS)
+ @Produces({ XML, JSON })
+ @Description("Gives a list of trusted identities supplied to this "
+ + "workflow run.")
+ @Nonnull
+ TrustList listTrusted();
+
+ /**
+ * Describe a particular trusted identity.
+ *
+ * @param id
+ * The id of the trusted identity to fetch.
+ * @return The description of the trusted identity.
+ * @throws NoCredentialException
+ * If the trusted identity doesn't exist.
+ */
+ @GET
+ @Path(ONE_TRUST)
+ @Produces({ XML, JSON })
+ @Description("Describes a particular trusted identity.")
+ @Nonnull
+ Trust getParticularTrust(@Nonnull @PathParam("id") String id)
+ throws NoCredentialException;
+
+ /**
+ * Update a particular trusted identity.
+ *
+ * @param id
+ * The id of the trusted identity to update.
+ * @param t
+ * The details of the trusted identity to use in the update.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return Description of the updated trusted identity.
+ * @throws InvalidCredentialException
+ * If the trusted identity description isn't valid.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @PUT
+ @Path(ONE_TRUST)
+ @Consumes({ XML, JSON })
+ @Produces({ XML, JSON })
+ @Description("Updates a particular trusted identity.")
+ @Nonnull
+ Trust setParticularTrust(@Nonnull @PathParam("id") String id,
+ @Nonnull Trust t, @Nonnull @Context UriInfo ui)
+ throws InvalidCredentialException, BadStateChangeException;
+
+ /**
+ * Adds a new trusted identity.
+ *
+ * @param t
+ * The details of the trusted identity to create.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return Description of the created trusted identity.
+ * @throws InvalidCredentialException
+ * If the trusted identity description isn't valid.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @POST
+ @Path(TRUSTS)
+ @Consumes({ XML, JSON })
+ @Description("Adds a new trusted identity.")
+ @Nonnull
+ Response addTrust(@Nonnull Trust t, @Nonnull @Context UriInfo ui)
+ throws InvalidCredentialException, BadStateChangeException;
+
+ /**
+ * Deletes all trusted identities associated with a run.
+ *
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return A characterisation of a successful delete.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @DELETE
+ @Path(TRUSTS)
+ @Description("Deletes all trusted identities.")
+ @Nonnull
+ Response deleteAllTrusts(@Nonnull @Context UriInfo ui)
+ throws BadStateChangeException;
+
+ /**
+ * Deletes one trusted identity associated with a run.
+ *
+ * @param id
+ * The identity of the trusted identity to delete.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return A characterisation of a successful delete.
+ * @throws BadStateChangeException
+ * If the workflow run is not in the initialising state.
+ */
+ @DELETE
+ @Path(ONE_TRUST)
+ @Description("Deletes a particular trusted identity.")
+ @Nonnull
+ Response deleteTrust(@Nonnull @PathParam("id") String id,
+ @Nonnull @Context UriInfo ui) throws BadStateChangeException;
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(PERMS)
+ @Description("Produces the description of the run permissions' operations.")
+ Response permissionsOptions();
+
+ /** Get an outline of the operations supported. */
+ @OPTIONS
+ @Path(ONE_PERM)
+ @Description("Produces the description of one run permission's operations.")
+ Response permissionOptions(@PathParam("id") String id);
+
+ /**
+ * @return A list of (non-default) permissions associated with this workflow
+ * run.
+ * @param ui
+ * Information about the URI used to access this resource.
+ */
+ @GET
+ @Path(PERMS)
+ @Produces({ XML, JSON })
+ @Description("Gives a list of all non-default permissions associated with "
+ + "the enclosing workflow run. By default, nobody has any access "
+ + "at all except for the owner of the run.")
+ @Nonnull
+ PermissionsDescription describePermissions(@Nonnull @Context UriInfo ui);
+
+ /**
+ * Describe the particular permission granted to a user.
+ *
+ * @param id
+ * The name of the user whose permissions are to be described.
+ * @return The permission they are granted.
+ */
+ @GET
+ @Path(ONE_PERM)
+ @Produces(TEXT)
+ @Description("Describes the permission granted to a particular user.")
+ @Nonnull
+ Permission describePermission(@Nonnull @PathParam("id") String id);
+
+ /**
+ * Update the permission granted to a user.
+ *
+ * @param id
+ * The name of the user whose permissions are to be updated. Note
+ * that the owner always has full permissions.
+ * @param perm
+ * The permission level to set.
+ * @return The permission level that has actually been set.
+ */
+ @PUT
+ @Consumes(TEXT)
+ @Produces(TEXT)
+ @Path(ONE_PERM)
+ @Description("Updates the permissions granted to a particular user.")
+ @Nonnull
+ Permission setPermission(@Nonnull @PathParam("id") String id,
+ @Nonnull Permission perm);
+
+ /**
+ * Delete the permissions associated with a user, which restores them to the
+ * default (no access unless they are the owner or have admin privileges).
+ *
+ * @param id
+ * The name of the user whose permissions are to be revoked.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return An indication that the delete has been successful (or not).
+ */
+ @DELETE
+ @Path(ONE_PERM)
+ @Description("Deletes (by resetting to default) the permissions "
+ + "associated with a particular user.")
+ @Nonnull
+ Response deletePermission(@Nonnull @PathParam("id") String id,
+ @Nonnull @Context UriInfo ui);
+
+ /**
+ * Manufacture a permission setting for a previously-unknown user.
+ *
+ * @param desc
+ * A description of the name of the user and the permission level
+ * to grant them.
+ * @param ui
+ * Information about the URI used to access this resource.
+ * @return An indication that the create has been successful (or not).
+ */
+ @POST
+ @Path(PERMS)
+ @Consumes({ XML, JSON })
+ @Description("Creates a new assignment of permissions to a particular user.")
+ @Nonnull
+ Response makePermission(@Nonnull PermissionDescription desc,
+ @Nonnull @Context UriInfo ui);
+
+ /**
+ * A description of the security resources associated with a workflow run.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "securityDescriptor")
+ @XmlType(name = "SecurityDescriptor")
+ public static final class Descriptor extends VersionedElement {
+ /** The identity of the owner of the enclosing workflow run. */
+ @XmlElement
+ public String owner;
+ /** Where to get the permissions on the run. */
+ @XmlElement
+ public Uri permissions;
+
+ /** Characterisation of the credentials attached to the run. */
+ @XmlElement
+ public Credentials credentials;
+ /** Characterisation of the trusted certificates attached to the run. */
+ @XmlElement
+ public Trusts trusts;
+
+ public Descriptor() {
+ }
+
+ /**
+ * Initialise a description of the security context.
+ *
+ * @param ub
+ * How to build URIs.
+ * @param owner
+ * Who owns the context.
+ * @param credential
+ * The credentials associated with the context.
+ * @param trust
+ * The trusted certificates associated with the context.
+ */
+ public Descriptor(@Nonnull UriBuilder ub, @Nonnull String owner,
+ @Nonnull Credential[] credential, @Nonnull Trust[] trust) {
+ super(true);
+ this.owner = owner;
+ this.permissions = new Uri(ub, PERMS);
+ this.credentials = new Credentials(new Uri(ub, CREDS).ref,
+ credential);
+ this.trusts = new Trusts(new Uri(ub, TRUSTS).ref, trust);
+ }
+
+ /**
+ * A description of credentials associated with a workflow run.
+ *
+ * @author Donal Fellows
+ */
+ @XmlType(name = "CredentialCollection")
+ public static final class Credentials {
+ /** Reference to the collection of credentials */
+ @XmlAttribute(name = "href", namespace = XLINK)
+ @XmlSchemaType(name = "anyURI")
+ public URI href;
+ /** Descriptions of the credentials themselves. */
+ @XmlElement
+ public List<CredentialHolder> credential = new ArrayList<>();
+
+ public Credentials() {
+ }
+
+ /**
+ * Initialise a description of the credentials.
+ *
+ * @param uri
+ * the URI of the collection.
+ * @param credential
+ * The credentials in the collection.
+ */
+ public Credentials(@Nonnull URI uri,
+ @Nonnull Credential[] credential) {
+ this.href = uri;
+ for (Credential c : credential)
+ this.credential.add(new CredentialHolder(c));
+ }
+ }
+
+ /**
+ * A description of trusted certificates associated with a workflow run.
+ *
+ * @author Donal Fellows
+ */
+ @XmlType(name = "TrustCollection")
+ public static final class Trusts {
+ /** Reference to the collection of trusted certs */
+ @XmlAttribute(name = "href", namespace = XLINK)
+ @XmlSchemaType(name = "anyURI")
+ public URI href;
+ /** Descriptions of the trusted certs themselves. */
+ @XmlElement
+ public Trust[] trust;
+
+ public Trusts() {
+ }
+
+ /**
+ * Initialise a description of the trusted certificates.
+ *
+ * @param uri
+ * the URI of the collection.
+ * @param trust
+ * The trusted certificates in the collection.
+ */
+ public Trusts(@Nonnull URI uri, @Nonnull Trust[] trust) {
+ this.href = uri;
+ this.trust = trust.clone();
+ }
+ }
+ }
+
+ /**
+ * A container for a credential, used to work around issues with type
+ * inference in CXF's REST service handling and JAXB.
+ *
+ * @see Credential.KeyPair
+ * @see Credential.Password
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "credential")
+ @XmlType(name = "Credential")
+ public static final class CredentialHolder {
+ /**
+ * The credential inside this holder.
+ */
+ @XmlElements({
+ @XmlElement(name = "keypair", namespace = SERVER, type = Credential.KeyPair.class, required = true),
+ @XmlElement(name = "userpass", namespace = SERVER, type = Credential.Password.class, required = true) })
+ public Credential credential;
+
+ public CredentialHolder() {
+ }
+
+ public CredentialHolder(Credential credential) {
+ this.credential = credential;
+ }
+
+ /**
+ * Convenience accessor function.
+ *
+ * @return The keypair credential held in this holder.
+ */
+ @XmlTransient
+ public Credential.KeyPair getKeypair() {
+ return (Credential.KeyPair) this.credential;
+ }
+
+ /**
+ * Convenience accessor function.
+ *
+ * @return The userpass credential held in this holder.
+ */
+ @XmlTransient
+ public Credential.Password getUserpass() {
+ return (Credential.Password) this.credential;
+ }
+ }
+
+ /**
+ * A simple list of credential descriptions.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "credentials")
+ public static final class CredentialList extends VersionedElement {
+ /** The descriptions of the credentials */
+ @XmlElement
+ @Nonnull
+ public List<CredentialHolder> credential = new ArrayList<>();
+
+ public CredentialList() {
+ }
+
+ /**
+ * Initialise the list of credentials.
+ *
+ * @param credential
+ * The descriptions of individual credentials.
+ */
+ public CredentialList(@Nonnull Credential[] credential) {
+ super(true);
+ for (Credential c : credential)
+ this.credential.add(new CredentialHolder(c));
+ }
+ }
+
+ /**
+ * A simple list of trusted certificate descriptions.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "trustedIdentities")
+ public static final class TrustList extends VersionedElement {
+ /** The descriptions of the trusted certificates */
+ @XmlElement
+ public Trust[] trust;
+
+ public TrustList() {
+ }
+
+ /**
+ * Initialise the list of trusted certificates.
+ *
+ * @param trust
+ * The descriptions of individual certificates.
+ */
+ public TrustList(@Nonnull Trust[] trust) {
+ super(true);
+ this.trust = trust.clone();
+ }
+ }
+
+ /**
+ * A description of the permissions granted to others by the owner of a
+ * workflow run.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "permissionsDescriptor")
+ public static class PermissionsDescription extends VersionedElement {
+ /**
+ * A description of the permissions granted to one user by the owner of
+ * a workflow run.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "userPermission")
+ public static class LinkedPermissionDescription extends Uri {
+ /** Who is this granted to? */
+ @XmlElement
+ public String userName;
+ /** What are they granted? */
+ @XmlElement
+ public Permission permission;
+
+ public LinkedPermissionDescription() {
+ }
+
+ /**
+ * Initialise a description of one user's permissions.
+ *
+ * @param ub
+ * How to build the URI to this permission. Already
+ * secured.
+ * @param userName
+ * Who this relates to.
+ * @param permission
+ * What permission is granted.
+ * @param strings
+ * Parameters to the URI builder.
+ */
+ LinkedPermissionDescription(@Nonnull UriBuilder ub,
+ @Nonnull String userName, @Nonnull Permission permission,
+ String... strings) {
+ super(ub, strings);
+ this.userName = userName;
+ this.permission = permission;
+ }
+ }
+
+ /** List of descriptions of permissions. */
+ @XmlElement
+ public List<LinkedPermissionDescription> permission;
+
+ public PermissionsDescription() {
+ permission = emptyList();
+ }
+
+ /**
+ * Initialise the description of a collection of permissions.
+ *
+ * @param ub
+ * How to build URIs to this collection. Must have already
+ * been secured.
+ * @param permissionMap
+ * The permissions to describe.
+ */
+ public PermissionsDescription(@Nonnull UriBuilder ub,
+ @Nonnull Map<String, Permission> permissionMap) {
+ permission = new ArrayList<>();
+ List<String> userNames = new ArrayList<>(permissionMap.keySet());
+ Collections.sort(userNames);
+ for (String user : userNames)
+ permission.add(new LinkedPermissionDescription(ub, user,
+ permissionMap.get(user), user));
+ }
+ }
+
+ /**
+ * An instruction to update the permissions for a user.
+ *
+ * @author Donal Fellows
+ */
+ @XmlRootElement(name = "permissionUpdate")
+ public static class PermissionDescription {
+ /** Who to set the permission for? */
+ @XmlElement
+ public String userName;
+ /** What permission to grant them? */
+ @XmlElement
+ public Permission permission;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/AccessDeniedHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/AccessDeniedHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/AccessDeniedHandler.java
new file mode 100644
index 0000000..3418975
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/AccessDeniedHandler.java
@@ -0,0 +1,34 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.springframework.security.access.AccessDeniedException;
+
+public class AccessDeniedHandler extends HandlerCore implements
+ ExceptionMapper<AccessDeniedException> {
+ @Override
+ public Response toResponse(AccessDeniedException exception) {
+ return respond(FORBIDDEN, exception);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadInputPortNameHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadInputPortNameHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadInputPortNameHandler.java
new file mode 100644
index 0000000..a78693d
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadInputPortNameHandler.java
@@ -0,0 +1,36 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.BadInputPortNameException;
+
+@Provider
+public class BadInputPortNameHandler extends HandlerCore implements
+ ExceptionMapper<BadInputPortNameException> {
+ @Override
+ public Response toResponse(BadInputPortNameException exn) {
+ return respond(NOT_FOUND, exn);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadPropertyValueHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadPropertyValueHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadPropertyValueHandler.java
new file mode 100644
index 0000000..e956749
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadPropertyValueHandler.java
@@ -0,0 +1,36 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.BadPropertyValueException;
+
+@Provider
+public class BadPropertyValueHandler extends HandlerCore implements
+ ExceptionMapper<BadPropertyValueException> {
+ @Override
+ public Response toResponse(BadPropertyValueException exn) {
+ return respond(BAD_REQUEST, exn);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadStateChangeHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadStateChangeHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadStateChangeHandler.java
new file mode 100644
index 0000000..53f441b
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/BadStateChangeHandler.java
@@ -0,0 +1,36 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.BadStateChangeException;
+
+@Provider
+public class BadStateChangeHandler extends HandlerCore implements
+ ExceptionMapper<BadStateChangeException> {
+ @Override
+ public Response toResponse(BadStateChangeException exn) {
+ return respond(BAD_REQUEST, exn);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/EntryHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/EntryHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/EntryHandler.java
new file mode 100644
index 0000000..bc79c22
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/EntryHandler.java
@@ -0,0 +1,147 @@
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonMap;
+import static javax.ws.rs.core.Response.notAcceptable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Variant;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.parser.Parser;
+import org.apache.abdera.writer.Writer;
+import org.springframework.beans.factory.annotation.Required;
+
+@Provider
+@Produces({ "application/atom+xml", "application/atom+xml;type=entry" })
+@Consumes({ "application/atom+xml", "application/atom+xml;type=entry" })
+public class EntryHandler implements MessageBodyWriter<Entry>,
+ MessageBodyReader<Entry> {
+ private static final String ENC = "UTF-8";
+ private static final MediaType ENTRY = new MediaType("application",
+ "atom+xml", singletonMap("type", "entry"));
+ private static final Variant VARIANT = new Variant(ENTRY, (String) null,
+ ENC);
+ private static final Charset UTF8 = Charset.forName(ENC);
+
+ @Required
+ public void setAbdera(Abdera abdera) {
+ parser = abdera.getParser();
+ writer = abdera.getWriterFactory().getWriter("prettyxml");
+ }
+
+ private Parser parser;
+ private Writer writer;
+
+ @Override
+ public boolean isReadable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ if (!Entry.class.isAssignableFrom(type))
+ return false;
+ if (!ENTRY.isCompatible(mediaType))
+ return false;
+ if (mediaType.getParameters().containsKey("type"))
+ return "entry".equalsIgnoreCase(mediaType.getParameters().get(
+ "type"));
+ return true;
+ }
+
+ @Override
+ public Entry readFrom(Class<Entry> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+ throws IOException, WebApplicationException {
+ Charset cs = UTF8;
+ try {
+ String charset = mediaType.getParameters().get("charset");
+ if (charset != null)
+ cs = Charset.forName(charset);
+ } catch (IllegalCharsetNameException e) {
+ throw new WebApplicationException(notAcceptable(asList(VARIANT))
+ .entity("bad charset name").build());
+ } catch (UnsupportedCharsetException e) {
+ throw new WebApplicationException(notAcceptable(asList(VARIANT))
+ .entity("unsupportd charset name").build());
+ }
+ try {
+ Document<Entry> doc = parser.parse(new InputStreamReader(
+ entityStream, cs));
+ if (!Entry.class.isAssignableFrom(doc.getRoot().getClass())) {
+ throw new WebApplicationException(
+ notAcceptable(asList(VARIANT)).entity(
+ "not really a feed entry").build());
+ }
+ return doc.getRoot();
+ } catch (ClassCastException e) {
+ throw new WebApplicationException(notAcceptable(asList(VARIANT))
+ .entity("not really a feed entry").build());
+
+ }
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ if (!Entry.class.isAssignableFrom(type))
+ return false;
+ if (!ENTRY.isCompatible(mediaType))
+ return false;
+ if (mediaType.getParameters().containsKey("type"))
+ return "entry".equalsIgnoreCase(mediaType.getParameters().get(
+ "type"));
+ return true;
+ }
+
+ @Override
+ public long getSize(Entry t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(Entry t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ httpHeaders.putSingle("Content-Type", ENTRY.toString() + ";charset="
+ + ENC);
+ writer.writeTo(t, new OutputStreamWriter(entityStream, UTF8));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FeedHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FeedHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FeedHandler.java
new file mode 100644
index 0000000..77e7e49
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FeedHandler.java
@@ -0,0 +1,82 @@
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static java.util.Collections.singletonMap;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.writer.Writer;
+import org.springframework.beans.factory.annotation.Required;
+
+@Provider
+@Produces({ "application/atom+xml", "application/atom+xml;type=feed" })
+public class FeedHandler implements MessageBodyWriter<Feed> {
+ private static final MediaType FEED = new MediaType("application",
+ "atom+xml", singletonMap("type", "feed"));
+ private static final String ENC = "UTF-8";
+
+ @Required
+ public void setAbdera(Abdera abdera) {
+ writer = abdera.getWriterFactory().getWriter("prettyxml");
+ }
+
+ private Writer writer;
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ if (!Feed.class.isAssignableFrom(type))
+ return false;
+ if (!FEED.isCompatible(mediaType))
+ return false;
+ if (mediaType.getParameters().containsKey("type"))
+ return "feed".equalsIgnoreCase(mediaType.getParameters()
+ .get("type"));
+ return true;
+ }
+
+ @Override
+ public long getSize(Feed t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(Feed t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ httpHeaders.putSingle("Content-Type", FEED.toString() + ";charset="
+ + ENC);
+ writer.writeTo(t, new OutputStreamWriter(entityStream, ENC));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileConcatenationHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileConcatenationHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileConcatenationHandler.java
new file mode 100644
index 0000000..e0924ad
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileConcatenationHandler.java
@@ -0,0 +1,77 @@
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.springframework.beans.factory.annotation.Required;
+import org.taverna.server.master.FileConcatenation;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.interfaces.File;
+
+public class FileConcatenationHandler implements
+ MessageBodyWriter<FileConcatenation> {
+ /** How much to pull from the worker in one read. */
+ private int maxChunkSize;
+
+ /**
+ * @param maxChunkSize
+ * How much to pull from the worker in one read.
+ */
+ @Required
+ public void setMaxChunkSize(int maxChunkSize) {
+ this.maxChunkSize = maxChunkSize;
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return type.isAssignableFrom(FileConcatenation.class);
+ }
+
+ @Override
+ public long getSize(FileConcatenation fc, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return fc.size();
+ }
+
+ @Override
+ public void writeTo(FileConcatenation fc, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException {
+ for (File f : fc)
+ try {
+ byte[] buffer;
+ for (int off = 0; true ; off += buffer.length) {
+ buffer = f.getContents(off, maxChunkSize);
+ if (buffer == null || buffer.length == 0)
+ break;
+ entityStream.write(buffer);
+ }
+ } catch (FilesystemAccessException e) {
+ // Ignore/skip to next file
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileMessageHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileMessageHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileMessageHandler.java
new file mode 100644
index 0000000..7d2b381
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileMessageHandler.java
@@ -0,0 +1,93 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static org.apache.commons.logging.LogFactory.getLog;
+
+import java.io.IOException;
+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.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.commons.logging.Log;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.interfaces.File;
+
+/**
+ * How to write out a File object with JAX-RS.
+ *
+ * @author Donal Fellows
+ */
+@Provider
+public class FileMessageHandler implements MessageBodyWriter<File> {
+ private Log log = getLog("Taverna.Server.Webapp");
+ /** How much to pull from the worker in one read. */
+ private int maxChunkSize;
+
+ /**
+ * @param maxChunkSize
+ * How much to pull from the worker in one read.
+ */
+ public void setMaxChunkSize(int maxChunkSize) {
+ this.maxChunkSize = maxChunkSize;
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return File.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public long getSize(File t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ try {
+ return t.getSize(); // Is it really raw bytes?
+ } catch (FilesystemAccessException e) {
+ log.info("failed to get file length", e);
+ return -1;
+ }
+ }
+
+ @Override
+ public void writeTo(File t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ try {
+ int off = 0;
+ while (true) {
+ byte[] buffer = t.getContents(off, maxChunkSize);
+ if (buffer == null || buffer.length == 0)
+ break;
+ entityStream.write(buffer);
+ off += buffer.length;
+ }
+ } catch (FilesystemAccessException e) {
+ throw new IOException("problem when reading file", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileSegmentHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileSegmentHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileSegmentHandler.java
new file mode 100644
index 0000000..82d5e0a
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FileSegmentHandler.java
@@ -0,0 +1,87 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static java.lang.Math.min;
+
+import java.io.IOException;
+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.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.rest.FileSegment;
+
+/**
+ * How to write out a segment of a file with JAX-RS.
+ *
+ * @author Donal Fellows
+ */
+@Provider
+public class FileSegmentHandler implements MessageBodyWriter<FileSegment> {
+ /** How much to pull from the worker in one read. */
+ private int maxChunkSize;
+
+ /**
+ * @param maxChunkSize
+ * How much to pull from the worker in one read.
+ */
+ public void setMaxChunkSize(int maxChunkSize) {
+ this.maxChunkSize = maxChunkSize;
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return FileSegment.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public long getSize(FileSegment t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType) {
+ return t.to - t.from;
+ }
+
+ @Override
+ public void writeTo(FileSegment t, Class<?> type, Type genericType,
+ Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ try {
+ int off = t.from;
+ while (off < t.to) {
+ byte[] buffer = t.file.getContents(off,
+ min(maxChunkSize, t.to - off));
+ if (buffer == null || buffer.length == 0)
+ break;
+ entityStream.write(buffer);
+ off += buffer.length;
+ }
+ } catch (FilesystemAccessException e) {
+ throw new IOException("problem when reading file", e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FilesystemAccessHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FilesystemAccessHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FilesystemAccessHandler.java
new file mode 100644
index 0000000..cfa863c
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/FilesystemAccessHandler.java
@@ -0,0 +1,36 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+
+@Provider
+public class FilesystemAccessHandler extends HandlerCore implements
+ ExceptionMapper<FilesystemAccessException> {
+ @Override
+ public Response toResponse(FilesystemAccessException exn) {
+ return respond(FORBIDDEN, exn);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/GeneralFailureHandler.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/GeneralFailureHandler.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/GeneralFailureHandler.java
new file mode 100644
index 0000000..fe4ba0b
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/GeneralFailureHandler.java
@@ -0,0 +1,34 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.taverna.server.master.exceptions.GeneralFailureException;
+
+public class GeneralFailureHandler extends HandlerCore implements
+ ExceptionMapper<GeneralFailureException> {
+ @Override
+ public Response toResponse(GeneralFailureException exception) {
+ return respond(INTERNAL_SERVER_ERROR, exception);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/HandlerCore.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/HandlerCore.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/HandlerCore.java
new file mode 100644
index 0000000..bc92154
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/rest/handler/HandlerCore.java
@@ -0,0 +1,84 @@
+/*
+ */
+package org.taverna.server.master.rest.handler;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.commons.logging.LogFactory.getLog;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.logging.Log;
+import org.taverna.server.master.api.ManagementModel;
+
+/**
+ * Base class for handlers that grants Spring-enabled access to the management
+ * model.
+ *
+ * @author Donal Fellows
+ */
+public class HandlerCore {
+ private Log log = getLog("Taverna.Server.Webapp");
+ private ManagementModel managementModel;
+
+ /**
+ * @param managementModel
+ * the managementModel to set
+ */
+ public void setManagementModel(ManagementModel managementModel) {
+ this.managementModel = managementModel;
+ }
+
+ /**
+ * Simplified interface for building responses.
+ *
+ * @param status
+ * What status code to use?
+ * @param exception
+ * What exception to report on?
+ * @return The build response.
+ */
+ protected Response respond(Response.Status status, Exception exception) {
+ if (managementModel.getLogOutgoingExceptions()
+ || status.getStatusCode() >= 500)
+ log.info("converting exception to response", exception);
+ return status(status).type(TEXT_PLAIN_TYPE)
+ .entity(exception.getMessage()).build();
+ }
+
+ /**
+ * Simplified interface for building responses.
+ *
+ * @param status
+ * What status code to use?
+ * @param partialMessage
+ * The prefix to the message.
+ * @param exception
+ * What exception to report on?
+ * @return The build response.
+ */
+ protected Response respond(Response.Status status, String partialMessage,
+ Exception exception) {
+ if (managementModel.getLogOutgoingExceptions()
+ || status.getStatusCode() >= 500)
+ log.info("converting exception to response", exception);
+ return status(status).type(TEXT_PLAIN_TYPE)
+ .entity(partialMessage + "\n" + exception.getMessage()).build();
+ }
+}