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(); + } +}
