http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenersREST.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenersREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenersREST.java new file mode 100644 index 0000000..3cc0d73 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenersREST.java @@ -0,0 +1,106 @@ +/* + */ +package org.taverna.server.master; +/* + * 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.created; +import static javax.ws.rs.core.UriBuilder.fromUri; +import static org.taverna.server.master.common.Uri.secure; +import static org.taverna.server.master.utils.RestUtils.opt; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.taverna.server.master.api.ListenersBean; +import org.taverna.server.master.exceptions.NoListenerException; +import org.taverna.server.master.exceptions.NoUpdateException; +import org.taverna.server.master.interfaces.Listener; +import org.taverna.server.master.interfaces.TavernaRun; +import org.taverna.server.master.rest.ListenerDefinition; +import org.taverna.server.master.rest.TavernaServerListenersREST; +import org.taverna.server.master.utils.CallTimeLogger.PerfLogged; +import org.taverna.server.master.utils.InvocationCounter.CallCounted; + +/** + * RESTful interface to a single workflow run's event listeners. + * + * @author Donal Fellows + */ +abstract class ListenersREST implements TavernaServerListenersREST, + ListenersBean { + private TavernaRun run; + private TavernaServerSupport support; + + @Override + public void setSupport(TavernaServerSupport support) { + this.support = support; + } + + @Override + public ListenersREST connect(TavernaRun run) { + this.run = run; + return this; + } + + @Override + @CallCounted + @PerfLogged + public Response addListener(ListenerDefinition typeAndConfiguration, + UriInfo ui) throws NoUpdateException, NoListenerException { + String name = support.makeListener(run, typeAndConfiguration.type, + typeAndConfiguration.configuration).getName(); + return created(secure(ui).path("{listenerName}").build(name)).build(); + } + + @Override + @CallCounted + @PerfLogged + public TavernaServerListenerREST getListener(String name) + throws NoListenerException { + Listener l = support.getListener(run, name); + if (l == null) + throw new NoListenerException(); + return makeListenerInterface().connect(l, run); + } + + @Nonnull + protected abstract SingleListenerREST makeListenerInterface(); + + @Override + @CallCounted + @PerfLogged + public Listeners getDescription(UriInfo ui) { + List<ListenerDescription> result = new ArrayList<>(); + UriBuilder ub = secure(ui).path("{name}"); + for (Listener l : run.getListeners()) + result.add(new ListenerDescription(l, + fromUri(ub.build(l.getName())))); + return new Listeners(result, ub); + } + + @Override + @CallCounted + public Response listenersOptions() { + return opt(); + } +} \ 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/ManagementState.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ManagementState.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ManagementState.java new file mode 100644 index 0000000..c65a334 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ManagementState.java @@ -0,0 +1,228 @@ +/* + */ +package org.taverna.server.master; +/* + * 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 javax.annotation.PostConstruct; +import javax.jdo.Query; +import javax.jdo.annotations.PersistenceAware; +import javax.jdo.annotations.PersistenceCapable; +import javax.jdo.annotations.Persistent; +import javax.jdo.annotations.PrimaryKey; + +import org.springframework.beans.factory.annotation.Required; +import org.taverna.server.master.api.ManagementModel; +import org.taverna.server.master.utils.JDOSupport; + +/** The persistent, manageable state of the Taverna Server web application. */ +@PersistenceAware +class ManagementState extends JDOSupport<WebappState> implements + ManagementModel { + public ManagementState() { + super(WebappState.class); + } + + /** Whether we should log all workflows sent to us. */ + private boolean logIncomingWorkflows = false; + + /** Whether we allow the creation of new workflow runs. */ + private boolean allowNewWorkflowRuns = true; + + /** + * Whether outgoing exceptions should be logged before being converted to + * responses. + */ + private boolean logOutgoingExceptions = false; + + /** + * The file that all usage records should be appended to, or <tt>null</tt> + * if they should be just dropped. + */ + private String usageRecordLogFile = null; + + @Override + public void setLogIncomingWorkflows(boolean logIncomingWorkflows) { + this.logIncomingWorkflows = logIncomingWorkflows; + if (loadedState) + self.store(); + } + + @Override + public boolean getLogIncomingWorkflows() { + self.load(); + return logIncomingWorkflows; + } + + @Override + public void setAllowNewWorkflowRuns(boolean allowNewWorkflowRuns) { + this.allowNewWorkflowRuns = allowNewWorkflowRuns; + if (loadedState) + self.store(); + } + + @Override + public boolean getAllowNewWorkflowRuns() { + self.load(); + return allowNewWorkflowRuns; + } + + @Override + public void setLogOutgoingExceptions(boolean logOutgoingExceptions) { + this.logOutgoingExceptions = logOutgoingExceptions; + if (loadedState) + self.store(); + } + + @Override + public boolean getLogOutgoingExceptions() { + self.load(); + return logOutgoingExceptions || true; + } + + @Override + public String getUsageRecordLogFile() { + self.load(); + return usageRecordLogFile; + } + + @Override + public void setUsageRecordLogFile(String usageRecordLogFile) { + this.usageRecordLogFile = usageRecordLogFile; + if (loadedState) + self.store(); + } + + private static final int KEY = 42; // whatever + + private WebappState get() { + Query<WebappState> q = query("id == " + KEY); + q.setUnique(true); + return q.executeUnique(); + } + + private boolean loadedState; + private ManagementState self; + + @Required + public void setSelf(ManagementState self) { + this.self = self; + } + + @PostConstruct + @WithinSingleTransaction + public void load() { + if (loadedState || !isPersistent()) + return; + WebappState state = get(); + if (state == null) + return; + allowNewWorkflowRuns = state.getAllowNewWorkflowRuns(); + logIncomingWorkflows = state.getLogIncomingWorkflows(); + logOutgoingExceptions = state.getLogOutgoingExceptions(); + usageRecordLogFile = state.getUsageRecordLogFile(); + loadedState = true; + } + + @WithinSingleTransaction + public void store() { + if (!isPersistent()) + return; + WebappState state = get(); + if (state == null) { + state = new WebappState(); + // save state + state.id = KEY; // whatever... + state = persist(state); + } + state.setAllowNewWorkflowRuns(allowNewWorkflowRuns); + state.setLogIncomingWorkflows(logIncomingWorkflows); + state.setLogOutgoingExceptions(logOutgoingExceptions); + state.setUsageRecordLogFile(usageRecordLogFile); + loadedState = true; + } +} + +// WARNING! If you change the name of this class, update persistence.xml as +// well! +@PersistenceCapable(table = "MANAGEMENTSTATE__WEBAPPSTATE") +class WebappState implements ManagementModel { + public WebappState() { + } + + @PrimaryKey + protected int id; + + /** Whether we should log all workflows sent to us. */ + @Persistent + private boolean logIncomingWorkflows; + + /** Whether we allow the creation of new workflow runs. */ + @Persistent + private boolean allowNewWorkflowRuns; + + /** + * Whether outgoing exceptions should be logged before being converted to + * responses. + */ + @Persistent + private boolean logOutgoingExceptions; + + /** Where to write usage records. */ + @Persistent + private String usageRecordLogFile; + + @Override + public void setLogIncomingWorkflows(boolean logIncomingWorkflows) { + this.logIncomingWorkflows = logIncomingWorkflows; + } + + @Override + public boolean getLogIncomingWorkflows() { + return logIncomingWorkflows; + } + + @Override + public void setAllowNewWorkflowRuns(boolean allowNewWorkflowRuns) { + this.allowNewWorkflowRuns = allowNewWorkflowRuns; + } + + @Override + public boolean getAllowNewWorkflowRuns() { + return allowNewWorkflowRuns; + } + + @Override + public void setLogOutgoingExceptions(boolean logOutgoingExceptions) { + this.logOutgoingExceptions = logOutgoingExceptions; + } + + @Override + public boolean getLogOutgoingExceptions() { + return logOutgoingExceptions; + } + + @Override + public String getUsageRecordLogFile() { + return usageRecordLogFile; + } + + @Override + public void setUsageRecordLogFile(String usageRecordLogFile) { + this.usageRecordLogFile = usageRecordLogFile; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunREST.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunREST.java new file mode 100644 index 0000000..a04de46 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunREST.java @@ -0,0 +1,512 @@ +/* + */ +package org.taverna.server.master; +/* + * 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.APPLICATION_XML; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; +import static javax.ws.rs.core.Response.noContent; +import static javax.ws.rs.core.Response.ok; +import static javax.ws.rs.core.Response.status; +import static org.apache.commons.logging.LogFactory.getLog; +import static org.joda.time.format.ISODateTimeFormat.dateTime; +import static org.joda.time.format.ISODateTimeFormat.dateTimeParser; +import static org.taverna.server.master.common.Roles.SELF; +import static org.taverna.server.master.common.Roles.USER; +import static org.taverna.server.master.common.Status.Initialized; +import static org.taverna.server.master.common.Status.Operating; +import static org.taverna.server.master.utils.RestUtils.opt; + +import java.util.Date; + +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import javax.xml.bind.JAXBException; + +import org.apache.commons.logging.Log; +import org.joda.time.DateTime; +import org.apache.taverna.server.usagerecord.JobUsageRecord; +import org.springframework.beans.factory.annotation.Required; +import org.taverna.server.master.api.RunBean; +import org.taverna.server.master.common.ProfileList; +import org.taverna.server.master.common.Status; +import org.taverna.server.master.common.Workflow; +import org.taverna.server.master.exceptions.BadStateChangeException; +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.exceptions.OverloadedException; +import org.taverna.server.master.exceptions.UnknownRunException; +import org.taverna.server.master.interfaces.TavernaRun; +import org.taverna.server.master.interfaces.TavernaSecurityContext; +import org.taverna.server.master.rest.InteractionFeedREST; +import org.taverna.server.master.rest.TavernaServerInputREST; +import org.taverna.server.master.rest.TavernaServerListenersREST; +import org.taverna.server.master.rest.TavernaServerRunREST; +import org.taverna.server.master.rest.TavernaServerSecurityREST; +import org.taverna.server.master.utils.CallTimeLogger.PerfLogged; +import org.taverna.server.master.utils.InvocationCounter.CallCounted; +import org.taverna.server.port_description.OutputDescription; + +/** + * RESTful interface to a single workflow run. + * + * @author Donal Fellows + */ +abstract class RunREST implements TavernaServerRunREST, RunBean { + private Log log = getLog("Taverna.Server.Webapp"); + private String runName; + private TavernaRun run; + private TavernaServerSupport support; + private ContentsDescriptorBuilder cdBuilder; + + @Override + @Required + public void setSupport(TavernaServerSupport support) { + this.support = support; + } + + @Override + @Required + public void setCdBuilder(ContentsDescriptorBuilder cdBuilder) { + this.cdBuilder = cdBuilder; + } + + @Override + public void setRunName(String runName) { + this.runName = runName; + } + + @Override + public void setRun(TavernaRun run) { + this.run = run; + } + + @Override + @CallCounted + @PerfLogged + public RunDescription getDescription(UriInfo ui) { + return new RunDescription(run, ui); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response destroy() throws NoUpdateException { + try { + support.unregisterRun(runName, run); + } catch (UnknownRunException e) { + log.fatal("can't happen", e); + } + return noContent().build(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public TavernaServerListenersREST getListeners() { + return makeListenersInterface().connect(run); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public TavernaServerSecurityREST getSecurity() throws NotOwnerException { + TavernaSecurityContext secContext = run.getSecurityContext(); + if (!support.getPrincipal().equals(secContext.getOwner())) + throw new NotOwnerException(); + + // context.getBean("run.security", run, secContext); + return makeSecurityInterface().connect(secContext, run); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getExpiryTime() { + return dateTime().print(new DateTime(run.getExpiry())); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getCreateTime() { + return dateTime().print(new DateTime(run.getCreationTimestamp())); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getFinishTime() { + Date f = run.getFinishTimestamp(); + return f == null ? "" : dateTime().print(new DateTime(f)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getStartTime() { + Date f = run.getStartTimestamp(); + return f == null ? "" : dateTime().print(new DateTime(f)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getStatus() { + return run.getStatus().toString(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Workflow getWorkflow() { + return run.getWorkflow(); + } + + @Override + @CallCounted + public String getMainProfileName() { + String name = run.getWorkflow().getMainProfileName(); + return (name == null ? "" : name); + } + + @Override + @CallCounted + public ProfileList getProfiles() { + return support.getProfileDescriptor(run.getWorkflow()); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed({ USER, SELF }) + public DirectoryREST getWorkingDirectory() { + return makeDirectoryInterface().connect(run); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String setExpiryTime(String expiry) throws NoUpdateException, + IllegalArgumentException { + DateTime wanted = dateTimeParser().parseDateTime(expiry.trim()); + Date achieved = support.updateExpiry(run, wanted.toDate()); + return dateTime().print(new DateTime(achieved)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response setStatus(String status) throws NoUpdateException { + Status newStatus = Status.valueOf(status.trim()); + support.permitUpdate(run); + if (newStatus == Operating && run.getStatus() == Initialized) { + if (!support.getAllowStartWorkflowRuns()) + throw new OverloadedException(); + String issue = run.setStatus(newStatus); + if (issue == null) + issue = "starting run..."; + return status(202).entity(issue).type("text/plain").build(); + } + run.setStatus(newStatus); // Ignore the result + return ok(run.getStatus().toString()).type("text/plain").build(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public TavernaServerInputREST getInputs(UriInfo ui) { + return makeInputInterface().connect(run, ui); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getOutputFile() { + String o = run.getOutputBaclavaFile(); + return o == null ? "" : o; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String setOutputFile(String filename) throws NoUpdateException, + FilesystemAccessException, BadStateChangeException { + support.permitUpdate(run); + if (filename != null && filename.length() == 0) + filename = null; + run.setOutputBaclavaFile(filename); + String o = run.getOutputBaclavaFile(); + return o == null ? "" : o; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public OutputDescription getOutputDescription(UriInfo ui) + throws BadStateChangeException, FilesystemAccessException, + NoDirectoryEntryException { + if (run.getStatus() == Initialized) + throw new BadStateChangeException( + "may not get output description in initial state"); + return cdBuilder.makeOutputDescriptor(run, ui); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed({ USER, SELF }) + public InteractionFeedREST getInteractionFeed() { + return makeInteractionFeed().connect(run); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getName() { + return run.getName(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String setName(String name) throws NoUpdateException { + support.permitUpdate(run); + run.setName(name); + return run.getName(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getStdout() throws NoListenerException { + return support.getProperty(run, "io", "stdout"); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getStderr() throws NoListenerException { + return support.getProperty(run, "io", "stderr"); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response getUsage() throws NoListenerException, JAXBException { + String ur = support.getProperty(run, "io", "usageRecord"); + if (ur.isEmpty()) + return noContent().build(); + return ok(JobUsageRecord.unmarshal(ur), APPLICATION_XML).build(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response getLogContents() { + FileConcatenation fc = support.getLogs(run); + if (fc.isEmpty()) + return Response.noContent().build(); + return Response.ok(fc, TEXT_PLAIN).build(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response getRunBundle() { + FileConcatenation fc = support.getProv(run); + if (fc.isEmpty()) + return Response.status(404).entity("no provenance currently available").build(); + return Response.ok(fc, "application/vnd.wf4ever.robundle+zip").build(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public boolean getGenerateProvenance() { + return run.getGenerateProvenance(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public boolean setGenerateProvenance(boolean newValue) throws NoUpdateException { + support.permitUpdate(run); + run.setGenerateProvenance(newValue); + return run.getGenerateProvenance(); + } + + /** + * Construct a RESTful interface to a run's filestore. + * + * @return The handle to the interface, as decorated by Spring. + */ + protected abstract DirectoryREST makeDirectoryInterface(); + + /** + * Construct a RESTful interface to a run's input descriptors. + * + * @return The handle to the interface, as decorated by Spring. + */ + protected abstract InputREST makeInputInterface(); + + /** + * Construct a RESTful interface to a run's listeners. + * + * @return The handle to the interface, as decorated by Spring. + */ + protected abstract ListenersREST makeListenersInterface(); + + /** + * Construct a RESTful interface to a run's security. + * + * @return The handle to the interface, as decorated by Spring. + */ + protected abstract RunSecurityREST makeSecurityInterface(); + + /** + * Construct a RESTful interface to a run's interaction feed. + * + * @return The handle to the interaface, as decorated by Spring. + */ + protected abstract InteractionFeed makeInteractionFeed(); + + @Override + @CallCounted + public Response runOptions() { + return opt(); + } + + @Override + @CallCounted + public Response workflowOptions() { + return opt(); + } + + @Override + @CallCounted + public Response profileOptions() { + return opt(); + } + + @Override + @CallCounted + public Response expiryOptions() { + return opt("PUT"); + } + + @Override + @CallCounted + public Response createTimeOptions() { + return opt(); + } + + @Override + @CallCounted + public Response startTimeOptions() { + return opt(); + } + + @Override + @CallCounted + public Response finishTimeOptions() { + return opt(); + } + + @Override + @CallCounted + public Response statusOptions() { + return opt("PUT"); + } + + @Override + @CallCounted + public Response outputOptions() { + return opt("PUT"); + } + + @Override + @CallCounted + public Response nameOptions() { + return opt("PUT"); + } + + @Override + @CallCounted + public Response stdoutOptions() { + return opt(); + } + + @Override + @CallCounted + public Response stderrOptions() { + return opt(); + } + + @Override + @CallCounted + public Response usageOptions() { + return opt(); + } + + @Override + @CallCounted + public Response logOptions() { + return opt(); + } + + @Override + @CallCounted + public Response runBundleOptions() { + return opt(); + } + + @Override + @CallCounted + public Response generateProvenanceOptions() { + return opt("PUT"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunSecurityREST.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunSecurityREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunSecurityREST.java new file mode 100644 index 0000000..9dc69d7 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/RunSecurityREST.java @@ -0,0 +1,316 @@ +/* + */ +package org.taverna.server.master; +/* + * 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.UUID.randomUUID; +import static javax.ws.rs.core.Response.created; +import static javax.ws.rs.core.Response.noContent; +import static org.taverna.server.master.common.Status.Initialized; +import static org.taverna.server.master.common.Uri.secure; +import static org.taverna.server.master.utils.RestUtils.opt; + +import java.net.URI; +import java.util.Map; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.taverna.server.master.api.SecurityBean; +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.exceptions.BadStateChangeException; +import org.taverna.server.master.exceptions.InvalidCredentialException; +import org.taverna.server.master.exceptions.NoCredentialException; +import org.taverna.server.master.interfaces.TavernaRun; +import org.taverna.server.master.interfaces.TavernaSecurityContext; +import org.taverna.server.master.rest.TavernaServerSecurityREST; +import org.taverna.server.master.utils.CallTimeLogger.PerfLogged; +import org.taverna.server.master.utils.InvocationCounter.CallCounted; + +/** + * RESTful interface to a single workflow run's security settings. + * + * @author Donal Fellows + */ +class RunSecurityREST implements TavernaServerSecurityREST, SecurityBean { + private TavernaServerSupport support; + private TavernaSecurityContext context; + private TavernaRun run; + + @Override + public void setSupport(TavernaServerSupport support) { + this.support = support; + } + + @Override + public RunSecurityREST connect(TavernaSecurityContext context, + TavernaRun run) { + this.context = context; + this.run = run; + return this; + } + + @Override + @CallCounted + @PerfLogged + public Descriptor describe(UriInfo ui) { + return new Descriptor(secure(ui).path("{element}"), context.getOwner() + .getName(), context.getCredentials(), context.getTrusted()); + } + + @Override + @CallCounted + @PerfLogged + public String getOwner() { + return context.getOwner().getName(); + } + + @Override + @CallCounted + @PerfLogged + public CredentialList listCredentials() { + return new CredentialList(context.getCredentials()); + } + + @Override + @CallCounted + @PerfLogged + public CredentialHolder getParticularCredential(String id) + throws NoCredentialException { + for (Credential c : context.getCredentials()) + if (c.id.equals(id)) + return new CredentialHolder(c); + throw new NoCredentialException(); + } + + @Override + @CallCounted + @PerfLogged + public CredentialHolder setParticularCredential(String id, + CredentialHolder cred, UriInfo ui) + throws InvalidCredentialException, BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + Credential c = cred.credential; + c.id = id; + c.href = ui.getAbsolutePath().toString(); + context.validateCredential(c); + context.deleteCredential(c); + context.addCredential(c); + return new CredentialHolder(c); + } + + @Override + @CallCounted + @PerfLogged + public Response addCredential(CredentialHolder cred, UriInfo ui) + throws InvalidCredentialException, BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + Credential c = cred.credential; + c.id = randomUUID().toString(); + URI uri = secure(ui).path("{id}").build(c.id); + c.href = uri.toString(); + context.validateCredential(c); + context.addCredential(c); + return created(uri).build(); + } + + @Override + @CallCounted + @PerfLogged + public Response deleteAllCredentials(UriInfo ui) + throws BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + for (Credential c : context.getCredentials()) + context.deleteCredential(c); + return noContent().build(); + } + + @Override + @CallCounted + @PerfLogged + public Response deleteCredential(String id, UriInfo ui) + throws BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + context.deleteCredential(new Credential.Dummy(id)); + return noContent().build(); + } + + @Override + @CallCounted + @PerfLogged + public TrustList listTrusted() { + return new TrustList(context.getTrusted()); + } + + @Override + @CallCounted + @PerfLogged + public Trust getParticularTrust(String id) throws NoCredentialException { + for (Trust t : context.getTrusted()) + if (t.id.equals(id)) + return t; + throw new NoCredentialException(); + } + + @Override + @CallCounted + @PerfLogged + public Trust setParticularTrust(String id, Trust t, UriInfo ui) + throws InvalidCredentialException, BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + t.id = id; + t.href = ui.getAbsolutePath().toString(); + context.validateTrusted(t); + context.deleteTrusted(t); + context.addTrusted(t); + return t; + } + + @Override + @CallCounted + @PerfLogged + public Response addTrust(Trust t, UriInfo ui) + throws InvalidCredentialException, BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + t.id = randomUUID().toString(); + URI uri = secure(ui).path("{id}").build(t.id); + t.href = uri.toString(); + context.validateTrusted(t); + context.addTrusted(t); + return created(uri).build(); + } + + @Override + @CallCounted + @PerfLogged + public Response deleteAllTrusts(UriInfo ui) throws BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + for (Trust t : context.getTrusted()) + context.deleteTrusted(t); + return noContent().build(); + } + + @Override + @CallCounted + @PerfLogged + public Response deleteTrust(String id, UriInfo ui) + throws BadStateChangeException { + if (run.getStatus() != Initialized) + throw new BadStateChangeException(); + Trust toDelete = new Trust(); + toDelete.id = id; + context.deleteTrusted(toDelete); + return noContent().build(); + } + + @Override + @CallCounted + @PerfLogged + public PermissionsDescription describePermissions(UriInfo ui) { + Map<String, Permission> perm = support.getPermissionMap(context); + return new PermissionsDescription(secure(ui).path("{id}"), perm); + } + + @Override + @CallCounted + @PerfLogged + public Permission describePermission(String id) { + return support.getPermission(context, id); + } + + @Override + @CallCounted + @PerfLogged + public Permission setPermission(String id, Permission perm) { + support.setPermission(context, id, perm); + return support.getPermission(context, id); + } + + @Override + @CallCounted + @PerfLogged + public Response deletePermission(String id, UriInfo ui) { + support.setPermission(context, id, Permission.None); + return noContent().build(); + } + + @Override + @CallCounted + @PerfLogged + public Response makePermission(PermissionDescription desc, UriInfo ui) { + support.setPermission(context, desc.userName, desc.permission); + return created(secure(ui).path("{user}").build(desc.userName)).build(); + } + + @Override + @CallCounted + public Response descriptionOptions() { + return opt(); + } + + @Override + @CallCounted + public Response ownerOptions() { + return opt(); + } + + @Override + @CallCounted + public Response credentialsOptions() { + return opt("POST", "DELETE"); + } + + @Override + @CallCounted + public Response credentialOptions(String id) { + return opt("PUT", "DELETE"); + } + + @Override + @CallCounted + public Response trustsOptions() { + return opt("POST", "DELETE"); + } + + @Override + @CallCounted + public Response trustOptions(String id) { + return opt("PUT", "DELETE"); + } + + @Override + @CallCounted + public Response permissionsOptions() { + return opt("POST"); + } + + @Override + @CallCounted + public Response permissionOptions(String id) { + return opt("PUT", "DELETE"); + } +} \ 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/SingleListenerREST.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/SingleListenerREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/SingleListenerREST.java new file mode 100644 index 0000000..2c841cc --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/SingleListenerREST.java @@ -0,0 +1,110 @@ +/* + */ +package org.taverna.server.master; +/* + * 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 org.taverna.server.master.common.Uri.secure; +import static org.taverna.server.master.utils.RestUtils.opt; + +import java.util.List; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.taverna.server.master.api.OneListenerBean; +import org.taverna.server.master.exceptions.NoListenerException; +import org.taverna.server.master.interfaces.Listener; +import org.taverna.server.master.interfaces.TavernaRun; +import org.taverna.server.master.rest.TavernaServerListenersREST; +import org.taverna.server.master.rest.TavernaServerListenersREST.ListenerDescription; +import org.taverna.server.master.rest.TavernaServerListenersREST.TavernaServerListenerREST; +import org.taverna.server.master.utils.CallTimeLogger.PerfLogged; +import org.taverna.server.master.utils.InvocationCounter.CallCounted; + +/** + * RESTful interface to a single listener attached to a workflow run. + * + * @author Donal Fellows + */ +abstract class SingleListenerREST implements TavernaServerListenerREST, + OneListenerBean { + private Listener listen; + private TavernaRun run; + + @Override + public SingleListenerREST connect(Listener listen, TavernaRun run) { + this.listen = listen; + this.run = run; + return this; + } + + @Override + @CallCounted + @PerfLogged + public String getConfiguration() { + return listen.getConfiguration(); + } + + @Override + @CallCounted + @PerfLogged + public ListenerDescription getDescription(UriInfo ui) { + return new ListenerDescription(listen, secure(ui)); + } + + @Override + @CallCounted + @PerfLogged + public TavernaServerListenersREST.Properties getProperties(UriInfo ui) { + return new TavernaServerListenersREST.Properties(secure(ui).path( + "{prop}"), listen.listProperties()); + } + + @Override + @CallCounted + @PerfLogged + public TavernaServerListenersREST.Property getProperty( + final String propertyName) throws NoListenerException { + List<String> p = asList(listen.listProperties()); + if (p.contains(propertyName)) { + return makePropertyInterface().connect(listen, run, propertyName); + } + throw new NoListenerException("no such property"); + } + + protected abstract ListenerPropertyREST makePropertyInterface(); + + @Override + @CallCounted + public Response listenerOptions() { + return opt(); + } + + @Override + @CallCounted + public Response configurationOptions() { + return opt(); + } + + @Override + @CallCounted + public Response propertiesOptions() { + return opt(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServer.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServer.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServer.java new file mode 100644 index 0000000..4844147 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServer.java @@ -0,0 +1,1438 @@ +/* + */ +package org.taverna.server.master; +/* + * 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 static java.util.Collections.emptyMap; +import static java.util.Collections.sort; +import static java.util.UUID.randomUUID; +import static javax.ws.rs.core.Response.created; +import static javax.ws.rs.core.UriBuilder.fromUri; +import static javax.xml.ws.handler.MessageContext.HTTP_REQUEST_HEADERS; +import static javax.xml.ws.handler.MessageContext.PATH_INFO; +import static org.apache.commons.io.IOUtils.toByteArray; +import static org.apache.commons.logging.LogFactory.getLog; +import static org.taverna.server.master.TavernaServerSupport.PROV_BUNDLE; +import static org.taverna.server.master.common.DirEntryReference.newInstance; +import static org.taverna.server.master.common.Namespaces.SERVER_SOAP; +import static org.taverna.server.master.common.Roles.ADMIN; +import static org.taverna.server.master.common.Roles.SELF; +import static org.taverna.server.master.common.Roles.USER; +import static org.taverna.server.master.common.Status.Initialized; +import static org.taverna.server.master.common.Uri.secure; +import static org.taverna.server.master.soap.DirEntry.convert; +import static org.taverna.server.master.utils.RestUtils.opt; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.net.URI; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import javax.annotation.security.DeclareRoles; +import javax.annotation.security.RolesAllowed; +import javax.jws.WebService; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +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.ws.WebServiceContext; + +import org.apache.commons.logging.Log; +import org.apache.cxf.annotations.WSDLDocumentation; +import org.apache.taverna.server.usagerecord.JobUsageRecord; +import org.springframework.beans.factory.annotation.Required; +import org.taverna.server.master.api.SupportAware; +import org.taverna.server.master.api.TavernaServerBean; +import org.taverna.server.master.common.Capability; +import org.taverna.server.master.common.Credential; +import org.taverna.server.master.common.DirEntryReference; +import org.taverna.server.master.common.InputDescription; +import org.taverna.server.master.common.Permission; +import org.taverna.server.master.common.ProfileList; +import org.taverna.server.master.common.RunReference; +import org.taverna.server.master.common.Status; +import org.taverna.server.master.common.Trust; +import org.taverna.server.master.common.Workflow; +import org.taverna.server.master.common.version.Version; +import org.taverna.server.master.exceptions.BadPropertyValueException; +import org.taverna.server.master.exceptions.BadStateChangeException; +import org.taverna.server.master.exceptions.FilesystemAccessException; +import org.taverna.server.master.exceptions.InvalidCredentialException; +import org.taverna.server.master.exceptions.NoCreateException; +import org.taverna.server.master.exceptions.NoCredentialException; +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.exceptions.OverloadedException; +import org.taverna.server.master.exceptions.UnknownRunException; +import org.taverna.server.master.factories.ListenerFactory; +import org.taverna.server.master.interfaces.Directory; +import org.taverna.server.master.interfaces.DirectoryEntry; +import org.taverna.server.master.interfaces.File; +import org.taverna.server.master.interfaces.Input; +import org.taverna.server.master.interfaces.Listener; +import org.taverna.server.master.interfaces.Policy; +import org.taverna.server.master.interfaces.RunStore; +import org.taverna.server.master.interfaces.TavernaRun; +import org.taverna.server.master.interfaces.TavernaSecurityContext; +import org.taverna.server.master.notification.NotificationEngine; +import org.taverna.server.master.notification.atom.EventDAO; +import org.taverna.server.master.rest.TavernaServerREST; +import org.taverna.server.master.rest.TavernaServerREST.EnabledNotificationFabrics; +import org.taverna.server.master.rest.TavernaServerREST.PermittedListeners; +import org.taverna.server.master.rest.TavernaServerREST.PermittedWorkflows; +import org.taverna.server.master.rest.TavernaServerREST.PolicyView; +import org.taverna.server.master.rest.TavernaServerRunREST; +import org.taverna.server.master.soap.DirEntry; +import org.taverna.server.master.soap.FileContents; +import org.taverna.server.master.soap.PermissionList; +import org.taverna.server.master.soap.TavernaServerSOAP; +import org.taverna.server.master.soap.WrappedWorkflow; +import org.taverna.server.master.soap.ZippedDirectory; +import org.taverna.server.master.utils.CallTimeLogger.PerfLogged; +import org.taverna.server.master.utils.FilenameUtils; +import org.taverna.server.master.utils.InvocationCounter.CallCounted; +import org.taverna.server.port_description.OutputDescription; + +/** + * The core implementation of the web application. + * + * @author Donal Fellows + */ +@Path("/") +@DeclareRoles({ USER, ADMIN }) +@WebService(endpointInterface = "org.taverna.server.master.soap.TavernaServerSOAP", serviceName = "TavernaServer", targetNamespace = SERVER_SOAP) +@WSDLDocumentation("An instance of Taverna " + Version.JAVA + " Server.") +public abstract class TavernaServer implements TavernaServerSOAP, + TavernaServerREST, TavernaServerBean { + /** + * The root of descriptions of the server in JMX. + */ + public static final String JMX_ROOT = "Taverna:group=Server-" + + Version.JAVA + ",name="; + + /** The logger for the server framework. */ + public Log log = getLog("Taverna.Server.Webapp"); + + @PreDestroy + void closeLog() { + log = null; + } + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // CONNECTIONS TO JMX, SPRING AND CXF + + @Resource + WebServiceContext jaxws; + @Context + private HttpHeaders jaxrsHeaders; + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // STATE VARIABLES AND SPRING SETTERS + + /** + * For building descriptions of the expected inputs and actual outputs of a + * workflow. + */ + private ContentsDescriptorBuilder cdBuilder; + /** + * Utilities for accessing files on the local-worker. + */ + private FilenameUtils fileUtils; + /** How notifications are dispatched. */ + private NotificationEngine notificationEngine; + /** Main support class. */ + private TavernaServerSupport support; + /** A storage facility for workflow runs. */ + private RunStore runStore; + /** Encapsulates the policies applied by this server. */ + private Policy policy; + /** Where Atom events come from. */ + EventDAO eventSource; + /** Reference to the main interaction feed. */ + private String interactionFeed; + + @Override + @Required + public void setFileUtils(FilenameUtils converter) { + this.fileUtils = converter; + } + + @Override + @Required + public void setContentsDescriptorBuilder(ContentsDescriptorBuilder cdBuilder) { + this.cdBuilder = cdBuilder; + } + + @Override + @Required + public void setNotificationEngine(NotificationEngine notificationEngine) { + this.notificationEngine = notificationEngine; + } + + /** + * @param support + * the support to set + */ + @Override + @Required + public void setSupport(TavernaServerSupport support) { + this.support = support; + } + + @Override + @Required + public void setRunStore(RunStore runStore) { + this.runStore = runStore; + } + + @Override + @Required + public void setPolicy(Policy policy) { + this.policy = policy; + } + + @Override + @Required + public void setEventSource(EventDAO eventSource) { + this.eventSource = eventSource; + } + + /** + * The location of a service-wide interaction feed, derived from a + * properties file. Expected to be <i>actually</i> not set (to a real + * value). + * + * @param interactionFeed + * The URL, which will be resolved relative to the location of + * the webapp, or the string "<tt>none</tt>" (which corresponds + * to a <tt>null</tt>). + */ + public void setInteractionFeed(String interactionFeed) { + if ("none".equals(interactionFeed)) + interactionFeed = null; + else if (interactionFeed != null && interactionFeed.startsWith("${")) + interactionFeed = null; + this.interactionFeed = interactionFeed; + } + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // REST INTERFACE + + @Override + @CallCounted + @PerfLogged + public ServerDescription describeService(UriInfo ui) { + jaxrsUriInfo.set(new WeakReference<>(ui)); + return new ServerDescription(ui, resolve(interactionFeed)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public RunList listUsersRuns(UriInfo ui) { + jaxrsUriInfo.set(new WeakReference<>(ui)); + return new RunList(runs(), secure(ui).path("{name}")); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response submitWorkflow(Workflow workflow, UriInfo ui) + throws NoUpdateException { + jaxrsUriInfo.set(new WeakReference<>(ui)); + checkCreatePolicy(workflow); + String name = support.buildWorkflow(workflow); + return created(secure(ui).path("{uuid}").build(name)).build(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Response submitWorkflowByURL(List<URI> referenceList, UriInfo ui) + throws NoCreateException { + jaxrsUriInfo.set(new WeakReference<>(ui)); + if (referenceList == null || referenceList.size() == 0) + throw new NoCreateException("no workflow URI supplied"); + URI workflowURI = referenceList.get(0); + checkCreatePolicy(workflowURI); + Workflow workflow; + try { + workflow = support.getWorkflowDocumentFromURI(workflowURI); + } catch (IOException e) { + throw new NoCreateException("could not read workflow", e); + } + String name = support.buildWorkflow(workflow); + return created(secure(ui).path("{uuid}").build(name)).build(); + } + + @Override + @CallCounted + @PerfLogged + public int getServerMaxRuns() { + return support.getMaxSimultaneousRuns(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed({ USER, SELF }) + public TavernaServerRunREST getRunResource(String runName, UriInfo ui) + throws UnknownRunException { + jaxrsUriInfo.set(new WeakReference<>(ui)); + RunREST rr = makeRunInterface(); + rr.setRun(support.getRun(runName)); + rr.setRunName(runName); + return rr; + } + + private ThreadLocal<Reference<UriInfo>> jaxrsUriInfo = new InheritableThreadLocal<>(); + + private UriInfo getUriInfo() { + if (jaxrsUriInfo.get() == null) + return null; + return jaxrsUriInfo.get().get(); + } + + @Override + @CallCounted + public abstract PolicyView getPolicyDescription(); + + @Override + @CallCounted + public Response serviceOptions() { + return opt(); + } + + @Override + @CallCounted + public Response runsOptions() { + return opt("POST"); + } + + /** + * Construct a RESTful interface to a run. + * + * @return The handle to the interface, as decorated by Spring. + */ + protected abstract RunREST makeRunInterface(); + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // SOAP INTERFACE + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public RunReference[] listRuns() { + ArrayList<RunReference> ws = new ArrayList<>(); + UriBuilder ub = getRunUriBuilder(); + for (String runName : runs().keySet()) + ws.add(new RunReference(runName, ub)); + return ws.toArray(new RunReference[ws.size()]); + } + + private void checkCreatePolicy(Workflow workflow) throws NoCreateException { + List<URI> pwu = policy + .listPermittedWorkflowURIs(support.getPrincipal()); + if (pwu == null || pwu.size() == 0) + return; + throw new NoCreateException("server policy: will only start " + + "workflows sourced from permitted URI list"); + } + + private void checkCreatePolicy(URI workflowURI) throws NoCreateException { + List<URI> pwu = policy + .listPermittedWorkflowURIs(support.getPrincipal()); + if (pwu == null || pwu.size() == 0 || pwu.contains(workflowURI)) + return; + throw new NoCreateException("workflow URI not on permitted list"); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public RunReference submitWorkflow(Workflow workflow) + throws NoUpdateException { + checkCreatePolicy(workflow); + String name = support.buildWorkflow(workflow); + return new RunReference(name, getRunUriBuilder()); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public RunReference submitWorkflowMTOM(WrappedWorkflow workflow) + throws NoUpdateException { + Workflow wf; + try { + wf = workflow.getWorkflow(); + } catch (IOException e) { + throw new NoCreateException(e.getMessage(), e); + } + checkCreatePolicy(wf); + String name = support.buildWorkflow(wf); + return new RunReference(name, getRunUriBuilder()); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public RunReference submitWorkflowByURI(URI workflowURI) + throws NoCreateException { + checkCreatePolicy(workflowURI); + Workflow workflow; + try { + workflow = support.getWorkflowDocumentFromURI(workflowURI); + } catch (IOException e) { + throw new NoCreateException("could not read workflow", e); + } + String name = support.buildWorkflow(workflow); + return new RunReference(name, getRunUriBuilder()); + } + + @Override + @CallCounted + @PerfLogged + public URI[] getServerWorkflows() { + return support.getPermittedWorkflowURIs(); + } + + @Override + @CallCounted + @PerfLogged + public String[] getServerListeners() { + List<String> types = support.getListenerTypes(); + return types.toArray(new String[types.size()]); + } + + @Override + @CallCounted + @PerfLogged + public String[] getServerNotifiers() { + List<String> dispatchers = notificationEngine + .listAvailableDispatchers(); + return dispatchers.toArray(new String[dispatchers.size()]); + } + + @Override + @CallCounted + @PerfLogged + public List<Capability> getServerCapabilities() { + return support.getCapabilities(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void destroyRun(String runName) throws UnknownRunException, + NoUpdateException { + support.unregisterRun(runName, null); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunDescriptiveName(String runName) + throws UnknownRunException { + return support.getRun(runName).getName(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunDescriptiveName(String runName, String descriptiveName) + throws UnknownRunException, NoUpdateException { + TavernaRun run = support.getRun(runName); + support.permitUpdate(run); + run.setName(descriptiveName); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Workflow getRunWorkflow(String runName) throws UnknownRunException { + return support.getRun(runName).getWorkflow(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public WrappedWorkflow getRunWorkflowMTOM(String runName) + throws UnknownRunException { + WrappedWorkflow ww = new WrappedWorkflow(); + ww.setWorkflow(support.getRun(runName).getWorkflow()); + return ww; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public ProfileList getRunWorkflowProfiles(String runName) + throws UnknownRunException { + return support.getProfileDescriptor(support.getRun(runName) + .getWorkflow()); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Date getRunExpiry(String runName) throws UnknownRunException { + return support.getRun(runName).getExpiry(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunExpiry(String runName, Date d) + throws UnknownRunException, NoUpdateException { + support.updateExpiry(support.getRun(runName), d); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Date getRunCreationTime(String runName) throws UnknownRunException { + return support.getRun(runName).getCreationTimestamp(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Date getRunFinishTime(String runName) throws UnknownRunException { + return support.getRun(runName).getFinishTimestamp(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Date getRunStartTime(String runName) throws UnknownRunException { + return support.getRun(runName).getStartTimestamp(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Status getRunStatus(String runName) throws UnknownRunException { + return support.getRun(runName).getStatus(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String setRunStatus(String runName, Status s) + throws UnknownRunException, NoUpdateException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + if (s == Status.Operating && w.getStatus() == Status.Initialized) { + if (!support.getAllowStartWorkflowRuns()) + throw new OverloadedException(); + try { + String issue = w.setStatus(s); + if (issue == null) + return ""; + if (issue.isEmpty()) + return "unknown reason for partial change"; + return issue; + } catch (RuntimeException | NoUpdateException e) { + log.info("failed to start run " + runName, e); + throw e; + } + } else { + w.setStatus(s); + return ""; + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunStdout(String runName) throws UnknownRunException { + try { + return support.getProperty(runName, "io", "stdout"); + } catch (NoListenerException e) { + return ""; + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunStderr(String runName) throws UnknownRunException { + try { + return support.getProperty(runName, "io", "stderr"); + } catch (NoListenerException e) { + return ""; + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public JobUsageRecord getRunUsageRecord(String runName) + throws UnknownRunException { + try { + String ur = support.getProperty(runName, "io", "usageRecord"); + if (ur.isEmpty()) + return null; + return JobUsageRecord.unmarshal(ur); + } catch (NoListenerException e) { + return null; + } catch (JAXBException e) { + log.info("failed to deserialize non-empty usage record", e); + return null; + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunLog(String runName) throws UnknownRunException { + try { + return support.getLogs(support.getRun(runName)).get("UTF-8"); + } catch (UnsupportedEncodingException e) { + log.warn("unexpected encoding problem", e); + return ""; + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public FileContents getRunBundle(String runName) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + File f = fileUtils.getFile(support.getRun(runName), PROV_BUNDLE); + FileContents fc = new FileContents(); + // We *know* the content type, by definition + fc.setFile(f, "application/vnd.wf4ever.robundle+zip"); + return fc; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public boolean getRunGenerateProvenance(String runName) + throws UnknownRunException { + return support.getRun(runName).getGenerateProvenance(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunGenerateProvenance(String runName, boolean generate) + throws UnknownRunException, NoUpdateException { + TavernaRun run = support.getRun(runName); + support.permitUpdate(run); + run.setGenerateProvenance(generate); + } + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // SOAP INTERFACE - Security + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunOwner(String runName) throws UnknownRunException { + return support.getRun(runName).getSecurityContext().getOwner() + .getName(); + } + + /** + * Look up a security context, applying access control rules for access to + * the parts of the context that are only open to the owner. + * + * @param runName + * The name of the workflow run. + * @param initialOnly + * Whether to check if we're in the initial state. + * @return The security context. Never <tt>null</tt>. + * @throws UnknownRunException + * @throws NotOwnerException + * @throws BadStateChangeException + */ + private TavernaSecurityContext getRunSecurityContext(String runName, + boolean initialOnly) throws UnknownRunException, NotOwnerException, + BadStateChangeException { + TavernaRun run = support.getRun(runName); + TavernaSecurityContext c = run.getSecurityContext(); + if (!c.getOwner().equals(support.getPrincipal())) + throw new NotOwnerException(); + if (initialOnly && run.getStatus() != Initialized) + throw new BadStateChangeException(); + return c; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Credential[] getRunCredentials(String runName) + throws UnknownRunException, NotOwnerException { + try { + return getRunSecurityContext(runName, false).getCredentials(); + } catch (BadStateChangeException e) { + Error e2 = new Error("impossible"); + e2.initCause(e); + throw e2; + } + } + + private Credential findCredential(TavernaSecurityContext c, String id) + throws NoCredentialException { + for (Credential t : c.getCredentials()) + if (t.id.equals(id)) + return t; + throw new NoCredentialException(); + } + + private Trust findTrust(TavernaSecurityContext c, String id) + throws NoCredentialException { + for (Trust t : c.getTrusted()) + if (t.id.equals(id)) + return t; + throw new NoCredentialException(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String setRunCredential(String runName, String credentialID, + Credential credential) throws UnknownRunException, + NotOwnerException, InvalidCredentialException, + NoCredentialException, BadStateChangeException { + TavernaSecurityContext c = getRunSecurityContext(runName, true); + if (credentialID == null || credentialID.isEmpty()) { + credential.id = randomUUID().toString(); + } else { + credential.id = findCredential(c, credentialID).id; + } + URI uri = getRunUriBuilder().path("security/credentials/{credid}") + .build(runName, credential.id); + credential.href = uri.toString(); + c.validateCredential(credential); + c.addCredential(credential); + return credential.id; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void deleteRunCredential(String runName, String credentialID) + throws UnknownRunException, NotOwnerException, + NoCredentialException, BadStateChangeException { + getRunSecurityContext(runName, true).deleteCredential( + new Credential.Dummy(credentialID)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Trust[] getRunCertificates(String runName) + throws UnknownRunException, NotOwnerException { + try { + return getRunSecurityContext(runName, false).getTrusted(); + } catch (BadStateChangeException e) { + Error e2 = new Error("impossible"); + e2.initCause(e); + throw e2; + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String setRunCertificates(String runName, String certificateID, + Trust certificate) throws UnknownRunException, NotOwnerException, + InvalidCredentialException, NoCredentialException, + BadStateChangeException { + TavernaSecurityContext c = getRunSecurityContext(runName, true); + if (certificateID == null || certificateID.isEmpty()) { + certificate.id = randomUUID().toString(); + } else { + certificate.id = findTrust(c, certificateID).id; + } + URI uri = getRunUriBuilder().path("security/trusts/{certid}").build( + runName, certificate.id); + certificate.href = uri.toString(); + c.validateTrusted(certificate); + c.addTrusted(certificate); + return certificate.id; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void deleteRunCertificates(String runName, String certificateID) + throws UnknownRunException, NotOwnerException, + NoCredentialException, BadStateChangeException { + TavernaSecurityContext c = getRunSecurityContext(runName, true); + Trust toDelete = new Trust(); + toDelete.id = certificateID; + c.deleteTrusted(toDelete); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public PermissionList listRunPermissions(String runName) + throws UnknownRunException, NotOwnerException { + PermissionList pl = new PermissionList(); + pl.permission = new ArrayList<>(); + Map<String, Permission> perm; + try { + perm = support.getPermissionMap(getRunSecurityContext(runName, + false)); + } catch (BadStateChangeException e) { + log.error("unexpected error from internal API", e); + perm = emptyMap(); + } + List<String> users = new ArrayList<>(perm.keySet()); + sort(users); + for (String user : users) + pl.permission.add(new PermissionList.SinglePermissionMapping(user, + perm.get(user))); + return pl; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunPermission(String runName, String userName, + Permission permission) throws UnknownRunException, + NotOwnerException { + try { + support.setPermission(getRunSecurityContext(runName, false), + userName, permission); + } catch (BadStateChangeException e) { + log.error("unexpected error from internal API", e); + } + } + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // SOAP INTERFACE - Filesystem connection + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public OutputDescription getRunOutputDescription(String runName) + throws UnknownRunException, BadStateChangeException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun run = support.getRun(runName); + if (run.getStatus() == Initialized) + throw new BadStateChangeException( + "may not get output description in initial state"); + return cdBuilder.makeOutputDescriptor(run, null); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public DirEntry[] getRunDirectoryContents(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + List<DirEntry> result = new ArrayList<>(); + for (DirectoryEntry e : fileUtils.getDirectory(support.getRun(runName), + convert(d)).getContents()) + result.add(convert(newInstance(null, e))); + return result.toArray(new DirEntry[result.size()]); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public byte[] getRunDirectoryAsZip(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + try { + return toByteArray(fileUtils.getDirectory(support.getRun(runName), + convert(d)).getContentsAsZip()); + } catch (IOException e) { + throw new FilesystemAccessException("problem serializing ZIP data", + e); + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public ZippedDirectory getRunDirectoryAsZipMTOM(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + return new ZippedDirectory(fileUtils.getDirectory( + support.getRun(runName), convert(d))); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public DirEntry makeRunDirectory(String runName, DirEntry parent, + String name) throws UnknownRunException, NoUpdateException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + Directory dir = fileUtils.getDirectory(w, convert(parent)) + .makeSubdirectory(support.getPrincipal(), name); + return convert(newInstance(null, dir)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public DirEntry makeRunFile(String runName, DirEntry parent, String name) + throws UnknownRunException, NoUpdateException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + File f = fileUtils.getDirectory(w, convert(parent)).makeEmptyFile( + support.getPrincipal(), name); + return convert(newInstance(null, f)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void destroyRunDirectoryEntry(String runName, DirEntry d) + throws UnknownRunException, NoUpdateException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + fileUtils.getDirEntry(w, convert(d)).destroy(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public byte[] getRunFileContents(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + File f = fileUtils.getFile(support.getRun(runName), convert(d)); + return f.getContents(0, -1); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunFileContents(String runName, DirEntry d, + byte[] newContents) throws UnknownRunException, NoUpdateException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + fileUtils.getFile(w, convert(d)).setContents(newContents); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public FileContents getRunFileContentsMTOM(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + File f = fileUtils.getFile(support.getRun(runName), convert(d)); + FileContents fc = new FileContents(); + fc.setFile(f, support.getEstimatedContentType(f)); + return fc; + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunFileContentsFromURI(String runName, + DirEntryReference file, URI reference) + throws UnknownRunException, NoUpdateException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun run = support.getRun(runName); + support.permitUpdate(run); + File f = fileUtils.getFile(run, file); + try { + support.copyDataToFile(reference, f); + } catch (IOException e) { + throw new FilesystemAccessException( + "problem transferring data from URI", e); + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunFileContentsMTOM(String runName, FileContents newContents) + throws UnknownRunException, NoUpdateException, + FilesystemAccessException, NoDirectoryEntryException { + TavernaRun run = support.getRun(runName); + support.permitUpdate(run); + File f = fileUtils.getFile(run, newContents.name); + f.setContents(new byte[0]); + support.copyDataToFile(newContents.fileData, f); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunFileType(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + return support.getEstimatedContentType(fileUtils.getFile( + support.getRun(runName), convert(d))); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public long getRunFileLength(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + return fileUtils.getFile(support.getRun(runName), convert(d)).getSize(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public Date getRunFileModified(String runName, DirEntry d) + throws UnknownRunException, FilesystemAccessException, + NoDirectoryEntryException { + return fileUtils.getFile(support.getRun(runName), convert(d)) + .getModificationDate(); + } + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // SOAP INTERFACE - Run listeners + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String[] getRunListeners(String runName) throws UnknownRunException { + TavernaRun w = support.getRun(runName); + List<String> result = new ArrayList<>(); + for (Listener l : w.getListeners()) + result.add(l.getName()); + return result.toArray(new String[result.size()]); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String addRunListener(String runName, String listenerType, + String configuration) throws UnknownRunException, + NoUpdateException, NoListenerException { + return support.makeListener(support.getRun(runName), listenerType, + configuration).getName(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunListenerConfiguration(String runName, + String listenerName) throws UnknownRunException, + NoListenerException { + return support.getListener(runName, listenerName).getConfiguration(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String[] getRunListenerProperties(String runName, String listenerName) + throws UnknownRunException, NoListenerException { + return support.getListener(runName, listenerName).listProperties() + .clone(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunListenerProperty(String runName, String listenerName, + String propName) throws UnknownRunException, NoListenerException { + return support.getListener(runName, listenerName).getProperty(propName); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunListenerProperty(String runName, String listenerName, + String propName, String value) throws UnknownRunException, + NoUpdateException, NoListenerException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + Listener l = support.getListener(w, listenerName); + try { + l.getProperty(propName); // sanity check! + l.setProperty(propName, value); + } catch (RuntimeException e) { + throw new NoListenerException("problem setting property: " + + e.getMessage(), e); + } + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public InputDescription getRunInputs(String runName) + throws UnknownRunException { + return new InputDescription(support.getRun(runName)); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getRunOutputBaclavaFile(String runName) + throws UnknownRunException { + return support.getRun(runName).getOutputBaclavaFile(); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunInputBaclavaFile(String runName, String fileName) + throws UnknownRunException, NoUpdateException, + FilesystemAccessException, BadStateChangeException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + w.setInputBaclavaFile(fileName); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunInputPortFile(String runName, String portName, + String portFilename) throws UnknownRunException, NoUpdateException, + FilesystemAccessException, BadStateChangeException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + Input i = support.getInput(w, portName); + if (i == null) + i = w.makeInput(portName); + i.setFile(portFilename); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunInputPortValue(String runName, String portName, + String portValue) throws UnknownRunException, NoUpdateException, + BadStateChangeException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + Input i = support.getInput(w, portName); + if (i == null) + i = w.makeInput(portName); + i.setValue(portValue); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunInputPortListDelimiter(String runName, String portName, + String delimiter) throws UnknownRunException, NoUpdateException, + BadStateChangeException, BadPropertyValueException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + Input i = support.getInput(w, portName); + if (i == null) + i = w.makeInput(portName); + if (delimiter != null && delimiter.isEmpty()) + delimiter = null; + if (delimiter != null) { + if (delimiter.length() > 1) + throw new BadPropertyValueException("delimiter too long"); + if (delimiter.charAt(0) < 1 || delimiter.charAt(0) > 127) + throw new BadPropertyValueException( + "delimiter character must be non-NUL ASCII"); + } + i.setDelimiter(delimiter); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public void setRunOutputBaclavaFile(String runName, String outputFile) + throws UnknownRunException, NoUpdateException, + FilesystemAccessException, BadStateChangeException { + TavernaRun w = support.getRun(runName); + support.permitUpdate(w); + w.setOutputBaclavaFile(outputFile); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public org.taverna.server.port_description.InputDescription getRunInputDescriptor( + String runName) throws UnknownRunException { + return cdBuilder.makeInputDescriptor(support.getRun(runName), null); + } + + @Override + @CallCounted + @PerfLogged + @RolesAllowed(USER) + public String getServerStatus() { + return support.getAllowNewWorkflowRuns() ? "operational" : "suspended"; + } + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + // SUPPORT METHODS + + @Override + public boolean initObsoleteSOAPSecurity(TavernaSecurityContext c) { + try { + javax.xml.ws.handler.MessageContext msgCtxt = (jaxws == null ? null + : jaxws.getMessageContext()); + if (msgCtxt == null) + return true; + c.initializeSecurityFromSOAPContext(msgCtxt); + return false; + } catch (IllegalStateException e) { + /* ignore; not much we can do */ + return true; + } + } + + @Override + public boolean initObsoleteRESTSecurity(TavernaSecurityContext c) { + if (jaxrsHeaders == null) + return true; + c.initializeSecurityFromRESTContext(jaxrsHeaders); + return false; + } + + /** + * A creator of substitute {@link URI} builders. + * + * @return A URI builder configured so that it takes a path parameter that + * corresponds to the run ID (but with no such ID applied). + */ + UriBuilder getRunUriBuilder() { + return getBaseUriBuilder().path("runs/{uuid}"); + } + + @Override + public UriBuilder getRunUriBuilder(TavernaRun run) { + return fromUri(getRunUriBuilder().build(run.getId())); + } + + private final String DEFAULT_HOST = "localhost:8080"; // Crappy default + + private String getHostLocation() { + @java.lang.SuppressWarnings("unchecked") + Map<String, List<String>> headers = (Map<String, List<String>>) jaxws + .getMessageContext().get(HTTP_REQUEST_HEADERS); + if (headers != null) { + List<String> host = headers.get("HOST"); + if (host != null && !host.isEmpty()) + return host.get(0); + } + return DEFAULT_HOST; + } + + @Nonnull + private URI getPossiblyInsecureBaseUri() { + // See if JAX-RS can supply the info + UriInfo ui = getUriInfo(); + if (ui != null && ui.getBaseUri() != null) + return ui.getBaseUri(); + // See if JAX-WS *cannot* supply the info + if (jaxws == null || jaxws.getMessageContext() == null) + // Hack to make the test suite work + return URI.create("http://" + DEFAULT_HOST + + "/taverna-server/rest/"); + String pathInfo = (String) jaxws.getMessageContext().get(PATH_INFO); + pathInfo = pathInfo.replaceFirst("/soap$", "/rest/"); + pathInfo = pathInfo.replaceFirst("/rest/.+$", "/rest/"); + return URI.create("http://" + getHostLocation() + pathInfo); + } + + @Override + public UriBuilder getBaseUriBuilder() { + return secure(fromUri(getPossiblyInsecureBaseUri())); + } + + @Override + @Nullable + public String resolve(@Nullable String uri) { + if (uri == null) + return null; + return secure(getPossiblyInsecureBaseUri(), uri).toString(); + } + + private Map<String, TavernaRun> runs() { + return runStore.listRuns(support.getPrincipal(), policy); + } +} + +/** + * RESTful interface to the policies of a Taverna Server installation. + * + * @author Donal Fellows + */ +class PolicyREST implements PolicyView, SupportAware { + private TavernaServerSupport support; + private Policy policy; + private ListenerFactory listenerFactory; + private NotificationEngine notificationEngine; + + @Override + public void setSupport(TavernaServerSupport support) { + this.support = support; + } + + @Required + public void setPolicy(Policy policy) { + this.policy = policy; + } + + @Required + public void setListenerFactory(ListenerFactory listenerFactory) { + this.listenerFactory = listenerFactory; + } + + @Required + public void setNotificationEngine(NotificationEngine notificationEngine) { + this.notificationEngine = notificationEngine; + } + + @Override + @CallCounted + @PerfLogged + public PolicyDescription getDescription(UriInfo ui) { + return new PolicyDescription(ui); + } + + @Override + @CallCounted + @PerfLogged + public int getMaxSimultaneousRuns() { + Integer limit = policy.getMaxRuns(support.getPrincipal()); + if (limit == null) + return policy.getMaxRuns(); + return min(limit.intValue(), policy.getMaxRuns()); + } + + @Override + @CallCounted + @PerfLogged + public PermittedListeners getPermittedListeners() { + return new PermittedListeners( + listenerFactory.getSupportedListenerTypes()); + } + + @Override + @CallCounted + @PerfLogged + public PermittedWorkflows getPermittedWorkflows() { + return new PermittedWorkflows(policy.listPermittedWorkflowURIs(support + .getPrincipal())); + } + + @Override + @CallCounted + @PerfLogged + public EnabledNotificationFabrics getEnabledNotifiers() { + return new EnabledNotificationFabrics( + notificationEngine.listAvailableDispatchers()); + } + + @Override + @CallCounted + @PerfLogged + public int getMaxOperatingRuns() { + return policy.getOperatingLimit(); + } + + @Override + @CallCounted + @PerfLogged + public CapabilityList getCapabilities() { + CapabilityList cl = new CapabilityList(); + cl.capability.addAll(support.getCapabilities()); + return cl; + } +}
