Java tutorial
/* */ 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; } }