blob: 80bc3fb7ad043175b1b0854567973bb2bccd14b5 [file] [log] [blame]
/*
*/
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.ogf.usage.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;
}
}