http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServerSupport.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServerSupport.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServerSupport.java new file mode 100644 index 0000000..533acf5 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/TavernaServerSupport.java @@ -0,0 +1,970 @@ +/* + */ +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 eu.medsea.util.MimeUtil.UNKNOWN_MIME_TYPE; +import static eu.medsea.util.MimeUtil.getExtensionMimeTypes; +import static eu.medsea.util.MimeUtil.getMimeType; +import static java.lang.Math.min; +import static org.apache.commons.logging.LogFactory.getLog; +import static org.springframework.jmx.support.MetricType.COUNTER; +import static org.springframework.jmx.support.MetricType.GAUGE; +import static org.taverna.server.master.TavernaServer.JMX_ROOT; +import static org.taverna.server.master.common.Roles.ADMIN; +import static org.taverna.server.master.rest.handler.T2FlowDocumentHandler.T2FLOW; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.activation.DataHandler; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PreDestroy; +import javax.ws.rs.WebApplicationException; +import javax.xml.bind.JAXBException; + +import org.apache.commons.logging.Log; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Required; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedMetric; +import org.springframework.jmx.export.annotation.ManagedResource; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.taverna.server.master.api.ManagementModel; +import org.taverna.server.master.api.TavernaServerBean; +import org.taverna.server.master.common.Capability; +import org.taverna.server.master.common.Permission; +import org.taverna.server.master.common.ProfileList; +import org.taverna.server.master.common.VersionedElement; +import org.taverna.server.master.common.Workflow; +import org.taverna.server.master.common.version.Version; +import org.taverna.server.master.exceptions.FilesystemAccessException; +import org.taverna.server.master.exceptions.NoCreateException; +import org.taverna.server.master.exceptions.NoDestroyException; +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.UnknownRunException; +import org.taverna.server.master.factories.ListenerFactory; +import org.taverna.server.master.factories.RunFactory; +import org.taverna.server.master.identity.WorkflowInternalAuthProvider.WorkflowSelfAuthority; +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.LocalIdentityMapper; +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.rest.handler.T2FlowDocumentHandler; +import org.taverna.server.master.utils.CapabilityLister; +import org.taverna.server.master.utils.FilenameUtils; +import org.taverna.server.master.utils.InvocationCounter; +import org.taverna.server.master.utils.UsernamePrincipal; + +import org.apache.taverna.scufl2.api.profiles.Profile; + +/** + * Web application support utilities. + * + * @author Donal Fellows + */ +@ManagedResource(objectName = JMX_ROOT + "Webapp", description = "The main Taverna Server " + + Version.JAVA + " web-application interface.") +public class TavernaServerSupport { + /** The main webapp log. */ + private Log log = getLog("Taverna.Server.Webapp"); + private Log accessLog = getLog("Taverna.Server.Webapp.Access");; + /** Bean used to log counts of external calls. */ + private InvocationCounter counter; + /** A storage facility for workflow runs. */ + private RunStore runStore; + /** Encapsulates the policies applied by this server. */ + private Policy policy; + /** Connection to the persistent state of this service. */ + private ManagementModel stateModel; + /** A factory for event listeners to attach to workflow runs. */ + private ListenerFactory listenerFactory; + /** A factory for workflow runs. */ + private RunFactory runFactory; + /** How to map the user ID to who to run as. */ + private LocalIdentityMapper idMapper; + /** The code that is coupled to CXF. */ + private TavernaServerBean webapp; + /** How to handle files. */ + private FilenameUtils fileUtils; + /** How to get the server capabilities. */ + private CapabilityLister capabilitySource; + /** + * Whether to log failures during principal retrieval. Should be normally on + * as it indicates a serious problem, but can be switched off for testing. + */ + private boolean logGetPrincipalFailures = true; + private Map<String, String> contentTypeMap; + /** Number of bytes to read when guessing the MIME type. */ + private static final int SAMPLE_SIZE = 1024; + /** Number of bytes to ask for when copying a stream to a file. */ + private static final int TRANSFER_SIZE = 32768; + + @PreDestroy + void closeLog() { + log = null; + } + + /** + * @return Count of the number of external calls into this webapp. + */ + @ManagedMetric(description = "Count of the number of external calls into this webapp.", metricType = COUNTER, category = "throughput") + public int getInvocationCount() { + return counter.getCount(); + } + + /** + * @return Current number of runs. + */ + @ManagedMetric(description = "Current number of runs.", metricType = GAUGE, category = "utilization") + public int getCurrentRunCount() { + return runStore.listRuns(null, policy).size(); + } + + /** + * @return Whether to write submitted workflows to the log. + */ + @ManagedAttribute(description = "Whether to write submitted workflows to the log.") + public boolean getLogIncomingWorkflows() { + return stateModel.getLogIncomingWorkflows(); + } + + /** + * @param logIncomingWorkflows + * Whether to write submitted workflows to the log. + */ + @ManagedAttribute(description = "Whether to write submitted workflows to the log.") + public void setLogIncomingWorkflows(boolean logIncomingWorkflows) { + stateModel.setLogIncomingWorkflows(logIncomingWorkflows); + } + + /** + * @return Whether outgoing exceptions should be logged before being + * converted to responses. + */ + @ManagedAttribute(description = "Whether outgoing exceptions should be logged before being converted to responses.") + public boolean getLogOutgoingExceptions() { + return stateModel.getLogOutgoingExceptions(); + } + + /** + * @param logOutgoing + * Whether outgoing exceptions should be logged before being + * converted to responses. + */ + @ManagedAttribute(description = "Whether outgoing exceptions should be logged before being converted to responses.") + public void setLogOutgoingExceptions(boolean logOutgoing) { + stateModel.setLogOutgoingExceptions(logOutgoing); + } + + /** + * @return Whether to permit any new workflow runs to be created. + */ + @ManagedAttribute(description = "Whether to permit any new workflow runs to be created; has no effect on existing runs.") + public boolean getAllowNewWorkflowRuns() { + return stateModel.getAllowNewWorkflowRuns(); + } + + /** + * @param allowNewWorkflowRuns + * Whether to permit any new workflow runs to be created. + */ + @ManagedAttribute(description = "Whether to permit any new workflow runs to be created; has no effect on existing runs.") + public void setAllowNewWorkflowRuns(boolean allowNewWorkflowRuns) { + stateModel.setAllowNewWorkflowRuns(allowNewWorkflowRuns); + } + + /** + * @return The server's version identifier. + */ + @ManagedAttribute(description = "The installed version of the server.") + public String getServerVersion() { + return VersionedElement.VERSION + " " + VersionedElement.REVISION + " " + + VersionedElement.TIMESTAMP; + } + + @ManagedAttribute(description = "The URIs of the workfows that this server will allow to be instantiated.") + public URI[] getPermittedWorkflowURIs() { + List<URI> pw = policy.listPermittedWorkflowURIs(null); + if (pw == null) + return new URI[0]; + return pw.toArray(new URI[pw.size()]); + } + + @ManagedAttribute(description = "The URIs of the workfows that this server will allow to be instantiated.") + public void setPermittedWorkflowURIs(URI[] pw) { + if (pw == null) + policy.setPermittedWorkflowURIs(null, new ArrayList<URI>()); + else + policy.setPermittedWorkflowURIs(null, Arrays.asList(pw)); + } + + public int getMaxSimultaneousRuns() { + Integer limit = policy.getMaxRuns(getPrincipal()); + if (limit == null) + return policy.getMaxRuns(); + return min(limit.intValue(), policy.getMaxRuns()); + } + + @Autowired + private T2FlowDocumentHandler t2flowHandler; + + public Workflow getWorkflowDocumentFromURI(URI uri) + throws WebApplicationException, IOException { + URLConnection conn = uri.toURL().openConnection(); + conn.setRequestProperty("Accept", T2FLOW); + conn.connect(); + // Tricky point: we know the reader part of the handler only cares + // about the stream argument. + return t2flowHandler.readFrom(null, null, null, null, null, + conn.getInputStream()); + } + + public List<String> getListenerTypes() { + return listenerFactory.getSupportedListenerTypes(); + } + + /** + * @param policy + * The policy being installed by Spring. + */ + @Required + public void setPolicy(Policy policy) { + this.policy = policy; + } + + /** + * @param listenerFactory + * The listener factory being installed by Spring. + */ + @Required + public void setListenerFactory(ListenerFactory listenerFactory) { + this.listenerFactory = listenerFactory; + } + + /** + * @param runFactory + * The run factory being installed by Spring. + */ + @Required + public void setRunFactory(RunFactory runFactory) { + this.runFactory = runFactory; + } + + /** + * @param runStore + * The run store being installed by Spring. + */ + @Required + public void setRunStore(RunStore runStore) { + this.runStore = runStore; + } + + /** + * @param stateModel + * The state model engine being installed by Spring. + */ + @Required + public void setStateModel(ManagementModel stateModel) { + this.stateModel = stateModel; + } + + /** + * @param mapper + * The identity mapper being installed by Spring. + */ + @Required + public void setIdMapper(LocalIdentityMapper mapper) { + this.idMapper = mapper; + } + + /** + * @param counter + * The object whose job it is to manage the counting of + * invocations. Installed by Spring. + */ + @Required + public void setInvocationCounter(InvocationCounter counter) { + this.counter = counter; + } + + /** + * @param webapp + * The web-app being installed by Spring. + */ + @Required + public void setWebapp(TavernaServerBean webapp) { + this.webapp = webapp; + } + + /** + * @param fileUtils + * The file handling utilities. + */ + @Required + public void setFileUtils(FilenameUtils fileUtils) { + this.fileUtils = fileUtils; + } + + /** + * @param logthem + * Whether to log failures relating to principals. + */ + public void setLogGetPrincipalFailures(boolean logthem) { + logGetPrincipalFailures = logthem; + } + + public Map<String, String> getContentTypeMap() { + return contentTypeMap; + } + + /** + * Mapping from filename suffixes (e.g., "baclava") to content types. + * + * @param contentTypeMap + * The mapping to install. + */ + @Required + public void setContentTypeMap(Map<String, String> contentTypeMap) { + this.contentTypeMap = contentTypeMap; + } + + @Required + public void setCapabilitySource(CapabilityLister capabilitySource) { + this.capabilitySource = capabilitySource; + } + + /** + * Test whether the current user can do updates to the given run. + * + * @param run + * The workflow run to do the test on. + * @throws NoUpdateException + * If the current user is not permitted to update the run. + */ + public void permitUpdate(@Nonnull TavernaRun run) throws NoUpdateException { + if (isSuperUser()) { + accessLog + .warn("check for admin powers passed; elevated access rights granted for update"); + return; // Superusers are fully authorized to access others things + } + if (getSelfAuthority() != null) { + // At this point, must already be accessing self as that is checked + // in getRun(). + return; + } + policy.permitUpdate(getPrincipal(), run); + } + + /** + * Test whether the current user can destroy or control the lifespan of the + * given run. + * + * @param run + * The workflow run to do the test on. + * @throws NoDestroyException + * If the current user is not permitted to destroy the run. + */ + public void permitDestroy(TavernaRun run) throws NoDestroyException { + if (isSuperUser()) { + accessLog + .warn("check for admin powers passed; elevated access rights granted for destroy"); + return; // Superusers are fully authorized to access others things + } + if (getSelfAuthority() != null) + throw new NoDestroyException(); + policy.permitDestroy(getPrincipal(), run); + } + + /** + * Gets the identity of the user currently accessing the webapp, which is + * stored in a thread-safe way in the webapp's container's context. + * + * @return The identity of the user accessing the webapp. + */ + @Nonnull + public UsernamePrincipal getPrincipal() { + try { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + if (auth == null || !auth.isAuthenticated()) { + if (logGetPrincipalFailures) + log.warn("failed to get auth; going with <NOBODY>"); + return new UsernamePrincipal("<NOBODY>"); + } + return new UsernamePrincipal(auth); + } catch (RuntimeException e) { + if (logGetPrincipalFailures) + log.info("failed to map principal", e); + throw e; + } + } + + private WorkflowSelfAuthority getSelfAuthority() { + try { + Authentication a = SecurityContextHolder.getContext() + .getAuthentication(); + for (GrantedAuthority ga : a.getAuthorities()) + if (ga instanceof WorkflowSelfAuthority) + return (WorkflowSelfAuthority) ga; + } catch (RuntimeException e) { + } + return null; + } + + /** + * Obtain the workflow run with a particular name. + * + * @param name + * The name of the run to look up. + * @return A workflow run handle that the current user has at least + * permission to read. + * @throws UnknownRunException + * If the workflow run doesn't exist or the current user doesn't + * have permission to see it. + */ + @Nonnull + public TavernaRun getRun(@Nonnull String name) throws UnknownRunException { + if (isSuperUser()) { + accessLog + .info("check for admin powers passed; elevated access rights granted for read"); + return runStore.getRun(name); + } + WorkflowSelfAuthority wsa = getSelfAuthority(); + if (wsa != null) { + if (wsa.getWorkflowID().equals(name)) + return runStore.getRun(name); + throw new UnknownRunException(); + } + return runStore.getRun(getPrincipal(), policy, name); + } + + /** + * Construct a listener attached to the given run. + * + * @param run + * The workflow run to attach the listener to. + * @param type + * The name of the type of run to create. + * @param configuration + * The configuration description to pass into the listener. The + * format of this string is up to the listener to define. + * @return A handle to the listener which can be used to further configure + * any properties. + * @throws NoListenerException + * If the listener type is unrecognized or the configuration is + * invalid. + * @throws NoUpdateException + * If the run does not permit the current user to add listeners + * (or perform other types of update). + */ + @Nonnull + public Listener makeListener(@Nonnull TavernaRun run, @Nonnull String type, + @Nonnull String configuration) throws NoListenerException, + NoUpdateException { + permitUpdate(run); + return listenerFactory.makeListener(run, type, configuration); + } + + /** + * Obtain a listener that is already attached to a workflow run. + * + * @param run + * The workflow run to search. + * @param listenerName + * The name of the listener to look up. + * @return The listener instance interface. + * @throws NoListenerException + * If no listener with that name exists. + */ + @Nonnull + public Listener getListener(TavernaRun run, String listenerName) + throws NoListenerException { + for (Listener l : run.getListeners()) + if (l.getName().equals(listenerName)) + return l; + throw new NoListenerException(); + } + + /** + * Obtain a property from a listener that is already attached to a workflow + * run. + * + * @param runName + * The ID of the workflow run to search. + * @param listenerName + * The name of the listener to look up in. + * @param propertyName + * The name of the property to fetch. + * @return The property value. + * @throws NoListenerException + * If no listener with that name exists, or no property with + * that name exists. + * @throws UnknownRunException + * If no run with that name exists. + */ + @Nonnull + public String getProperty(String runName, String listenerName, + String propertyName) throws NoListenerException, + UnknownRunException { + return getListener(runName, listenerName).getProperty(propertyName); + } + + /** + * Obtain a property from a listener that is already attached to a workflow + * run. + * + * @param run + * The workflow run to search. + * @param listenerName + * The name of the listener to look up in. + * @param propertyName + * The name of the property to fetch. + * @return The property value. + * @throws NoListenerException + * If no listener with that name exists, or no property with + * that name exists. + */ + @Nonnull + public String getProperty(TavernaRun run, String listenerName, + String propertyName) throws NoListenerException { + return getListener(run, listenerName).getProperty(propertyName); + } + + /** + * Get the permission description for the given user. + * + * @param context + * A security context associated with a particular workflow run. + * Note that only the owner of a workflow run may get the + * security context in the first place. + * @param userName + * The name of the user to look up the permission for. + * @return A permission description. + */ + @Nonnull + public Permission getPermission(@Nonnull TavernaSecurityContext context, + @Nonnull String userName) { + if (context.getPermittedDestroyers().contains(userName)) + return Permission.Destroy; + if (context.getPermittedUpdaters().contains(userName)) + return Permission.Update; + if (context.getPermittedReaders().contains(userName)) + return Permission.Read; + return Permission.None; + } + + /** + * Set the permissions for the given user. + * + * @param context + * A security context associated with a particular workflow run. + * Note that only the owner of a workflow run may get the + * security context in the first place. + * @param userName + * The name of the user to set the permission for. + * @param permission + * The description of the permission to grant. Note that the + * owner of a workflow run always has the equivalent of + * {@link Permission#Destroy}; this is always enforced before + * checking for other permissions. + */ + public void setPermission(TavernaSecurityContext context, String userName, + Permission permission) { + Set<String> permSet; + boolean doRead = false, doWrite = false, doKill = false; + + switch (permission) { + case Destroy: + doKill = true; + case Update: + doWrite = true; + case Read: + doRead = true; + default: + break; + } + + permSet = context.getPermittedReaders(); + if (doRead) { + if (!permSet.contains(userName)) { + permSet = new HashSet<>(permSet); + permSet.add(userName); + context.setPermittedReaders(permSet); + } + } else if (permSet.contains(userName)) { + permSet = new HashSet<>(permSet); + permSet.remove(userName); + context.setPermittedReaders(permSet); + } + + permSet = context.getPermittedUpdaters(); + if (doWrite) { + if (!permSet.contains(userName)) { + permSet = new HashSet<>(permSet); + permSet.add(userName); + context.setPermittedUpdaters(permSet); + } + } else if (permSet.contains(userName)) { + permSet = new HashSet<>(permSet); + permSet.remove(userName); + context.setPermittedUpdaters(permSet); + } + + permSet = context.getPermittedDestroyers(); + if (doKill) { + if (!permSet.contains(userName)) { + permSet = new HashSet<>(permSet); + permSet.add(userName); + context.setPermittedDestroyers(permSet); + } + } else if (permSet.contains(userName)) { + permSet = new HashSet<>(permSet); + permSet.remove(userName); + context.setPermittedDestroyers(permSet); + } + } + + public Map<String, Permission> getPermissionMap( + TavernaSecurityContext context) { + Map<String, Permission> perm = new HashMap<>(); + for (String u : context.getPermittedReaders()) + perm.put(u, Permission.Read); + for (String u : context.getPermittedUpdaters()) + perm.put(u, Permission.Update); + for (String u : context.getPermittedDestroyers()) + perm.put(u, Permission.Destroy); + return perm; + } + + /** + * Stops a run from being possible to be looked up and destroys it. + * + * @param runName + * The name of the run. + * @param run + * The workflow run. <i>Must</i> correspond to the name. + * @throws NoDestroyException + * If the user is not permitted to destroy the workflow run. + * @throws UnknownRunException + * If the run is unknown (e.g., because it is already + * destroyed). + */ + public void unregisterRun(@Nonnull String runName, @Nonnull TavernaRun run) + throws NoDestroyException, UnknownRunException { + if (run == null) + run = getRun(runName); + permitDestroy(run); + runStore.unregisterRun(runName); + run.destroy(); + } + + /** + * Changes the expiry date of a workflow run. The expiry date is when the + * workflow run becomes eligible for automated destruction. + * + * @param run + * The handle to the workflow run. + * @param date + * When the workflow run should be expired. + * @return When the workflow run will actually be expired. + * @throws NoDestroyException + * If the user is not permitted to destroy the workflow run. + * (Note that lifespan management requires the ability to + * destroy.) + */ + @Nonnull + public Date updateExpiry(@Nonnull TavernaRun run, @Nonnull Date date) + throws NoDestroyException { + permitDestroy(run); + run.setExpiry(date); + return run.getExpiry(); + } + + /** + * Manufacture a workflow run instance. + * + * @param workflow + * The workflow document (t2flow, scufl2?) to instantiate. + * @return The ID of the created workflow run. + * @throws NoCreateException + * If the user is not permitted to create workflows. + */ + public String buildWorkflow(Workflow workflow) throws NoCreateException { + UsernamePrincipal p = getPrincipal(); + if (getSelfAuthority() != null) + throw new NoCreateException( + "runs may not create workflows on their host server"); + if (!stateModel.getAllowNewWorkflowRuns()) + throw new NoCreateException("run creation not currently enabled"); + try { + if (stateModel.getLogIncomingWorkflows()) { + log.info(workflow.marshal()); + } + } catch (JAXBException e) { + log.warn("problem when logging workflow", e); + } + + // Security checks + policy.permitCreate(p, workflow); + if (idMapper != null && idMapper.getUsernameForPrincipal(p) == null) { + log.error("cannot map principal to local user id"); + throw new NoCreateException( + "failed to map security token to local user id"); + } + + TavernaRun run; + try { + run = runFactory.create(p, workflow); + TavernaSecurityContext c = run.getSecurityContext(); + c.initializeSecurityFromContext(SecurityContextHolder.getContext()); + /* + * These next pieces of security initialisation are (hopefully) + * obsolete now that we use Spring Security, but we keep them Just + * In Case. + */ + boolean doRESTinit = webapp.initObsoleteSOAPSecurity(c); + if (doRESTinit) + webapp.initObsoleteRESTSecurity(c); + } catch (Exception e) { + log.error("failed to build workflow run worker", e); + throw new NoCreateException("failed to build workflow run worker"); + } + + return runStore.registerRun(run); + } + + private boolean isSuperUser() { + try { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + if (auth == null || !auth.isAuthenticated()) + return false; + UserDetails details = (UserDetails) auth.getPrincipal(); + if (log.isDebugEnabled()) + log.debug("checking for admin role for user <" + auth.getName() + + "> in collection " + details.getAuthorities()); + return details.getAuthorities().contains(ADMIN); + } catch (ClassCastException e) { + return false; + } + } + + /** + * Get a particular input to a workflow run. + * + * @param run + * The workflow run to search. + * @param portName + * The name of the input. + * @return The handle of the input, or <tt>null</tt> if no such handle + * exists. + */ + @Nullable + public Input getInput(TavernaRun run, String portName) { + for (Input i : run.getInputs()) + if (i.getName().equals(portName)) + return i; + return null; + } + + /** + * Get a listener attached to a run. + * + * @param runName + * The name of the run to look up + * @param listenerName + * The name of the listener. + * @return The handle of the listener. + * @throws NoListenerException + * If no such listener exists. + * @throws UnknownRunException + * If no such workflow run exists, or if the user does not have + * permission to access it. + */ + public Listener getListener(String runName, String listenerName) + throws NoListenerException, UnknownRunException { + return getListener(getRun(runName), listenerName); + } + + /** + * Given a file, produce a guess at its content type. This uses the content + * type map property, and if that search fails it falls back on the Medsea + * mime type library. + * + * @param f + * The file handle. + * @return The content type. If all else fails, produces good old + * "application/octet-stream". + */ + @Nonnull + public String getEstimatedContentType(@Nonnull File f) { + String name = f.getName(); + for (int idx = name.indexOf('.'); idx != -1; idx = name.indexOf('.', + idx + 1)) { + String mt = contentTypeMap.get(name.substring(idx + 1)); + if (mt != null) + return mt; + } + @Nonnull + String type = getExtensionMimeTypes(name); + if (!type.equals(UNKNOWN_MIME_TYPE)) + return type; + try { + return getMimeType(new ByteArrayInputStream(f.getContents(0, + SAMPLE_SIZE))); + } catch (FilesystemAccessException e) { + return type; + } + } + + public void copyDataToFile(DataHandler handler, File file) + throws FilesystemAccessException { + try { + copyStreamToFile(handler.getInputStream(), file); + } catch (IOException e) { + throw new FilesystemAccessException( + "problem constructing stream from data source", e); + } + } + + public void copyDataToFile(URI uri, File file) + throws MalformedURLException, FilesystemAccessException, + IOException { + copyStreamToFile(uri.toURL().openStream(), file); + } + + public void copyStreamToFile(InputStream stream, File file) + throws FilesystemAccessException { + String name = file.getFullName(); + long total = 0; + try { + byte[] buffer = new byte[TRANSFER_SIZE]; + boolean first = true; + while (true) { + int len = stream.read(buffer); + if (len < 0) + break; + total += len; + if (log.isDebugEnabled()) + log.debug("read " + len + + " bytes from source stream (total: " + total + + ") bound for " + name); + if (len == buffer.length) { + if (first) + file.setContents(buffer); + else + file.appendContents(buffer); + } else { + byte[] newBuf = new byte[len]; + System.arraycopy(buffer, 0, newBuf, 0, len); + if (first) + file.setContents(newBuf); + else + file.appendContents(newBuf); + } + first = false; + } + } catch (IOException exn) { + throw new FilesystemAccessException("failed to transfer bytes", exn); + } + } + + /** + * Build a description of the profiles supported by a workflow. Note that we + * expect the set of profiles to be fairly small. + * + * @param workflow + * The workflow to describe the profiles of. + * @return The descriptor (which might be empty). + */ + public ProfileList getProfileDescriptor(Workflow workflow) { + ProfileList result = new ProfileList(); + String main = workflow.getMainProfileName(); + for (Profile p : workflow.getProfiles()) { + ProfileList.Info i = new ProfileList.Info(); + i.name = p.getName(); + if (main != null && main.equals(i.name)) + i.main = true; + result.profile.add(i); + } + return result; + } + + public boolean getAllowStartWorkflowRuns() { + return runFactory.isAllowingRunsToStart(); + } + + /** + * The list of filenames that logs may occupy. + */ + private static final String[] LOGS = { "logs/detail.log.4", + "logs/detail.log.3", "logs/detail.log.2", "logs/detail.log.1", + "logs/detail.log" }; + + public FileConcatenation getLogs(TavernaRun run) { + FileConcatenation fc = new FileConcatenation(); + for (String name : LOGS) { + try { + fc.add(fileUtils.getFile(run, name)); + } catch (FilesystemAccessException | NoDirectoryEntryException e) { + // Ignore + } + } + return fc; + } + + @Nonnull + public List<Capability> getCapabilities() { + return capabilitySource.getCapabilities(); + } + + static final String PROV_BUNDLE = "out.bundle.zip"; + + public FileConcatenation getProv(TavernaRun run) { + FileConcatenation fc = new FileConcatenation(); + try { + fc.add(fileUtils.getFile(run, PROV_BUNDLE)); + } catch (FilesystemAccessException | NoDirectoryEntryException e) { + // Ignore + } + return fc; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/Admin.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/Admin.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/Admin.java new file mode 100644 index 0000000..03c0c8b --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/Admin.java @@ -0,0 +1,1113 @@ +/* + */ +package org.taverna.server.master.admin; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import static org.taverna.server.master.admin.Paths.ALLOW_NEW; +import static org.taverna.server.master.admin.Paths.ARGS; +import static org.taverna.server.master.admin.Paths.EXEC_WF; +import static org.taverna.server.master.admin.Paths.EXITCODE; +import static org.taverna.server.master.admin.Paths.FACTORIES; +import static org.taverna.server.master.admin.Paths.GEN_PROV; +import static org.taverna.server.master.admin.Paths.INVOKES; +import static org.taverna.server.master.admin.Paths.JAR_FORKER; +import static org.taverna.server.master.admin.Paths.JAR_WORKER; +import static org.taverna.server.master.admin.Paths.JAVA; +import static org.taverna.server.master.admin.Paths.LIFE; +import static org.taverna.server.master.admin.Paths.LOG_EXN; +import static org.taverna.server.master.admin.Paths.LOG_WFS; +import static org.taverna.server.master.admin.Paths.OPERATING; +import static org.taverna.server.master.admin.Paths.OP_LIMIT; +import static org.taverna.server.master.admin.Paths.PASSFILE; +import static org.taverna.server.master.admin.Paths.PERM_WF; +import static org.taverna.server.master.admin.Paths.REG_HOST; +import static org.taverna.server.master.admin.Paths.REG_JAR; +import static org.taverna.server.master.admin.Paths.REG_POLL; +import static org.taverna.server.master.admin.Paths.REG_PORT; +import static org.taverna.server.master.admin.Paths.REG_WAIT; +import static org.taverna.server.master.admin.Paths.ROOT; +import static org.taverna.server.master.admin.Paths.RUNS; +import static org.taverna.server.master.admin.Paths.RUN_LIMIT; +import static org.taverna.server.master.admin.Paths.STARTUP; +import static org.taverna.server.master.admin.Paths.TOTAL_RUNS; +import static org.taverna.server.master.admin.Paths.URS; +import static org.taverna.server.master.admin.Paths.UR_FILE; +import static org.taverna.server.master.admin.Paths.USER; +import static org.taverna.server.master.admin.Paths.USERS; +import static org.taverna.server.master.admin.Types.JSON; +import static org.taverna.server.master.admin.Types.PLAIN; +import static org.taverna.server.master.admin.Types.XML; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.cxf.jaxrs.model.wadl.Description; +import org.apache.taverna.server.usagerecord.JobUsageRecord; +import org.taverna.server.master.common.Uri; +import org.taverna.server.master.common.VersionedElement; + +/** + * The administration interface for Taverna Server. + * + * @author Donal Fellows + */ +@Description("Administration interface for Taverna Server.") +public interface Admin { + /** + * Get a simple administration user interface. + * + * @return The description document in a response. + * @throws IOException + */ + @GET + @Path(ROOT) + @Produces("text/html") + @Nonnull + Response getUserInterface() throws IOException; + + /** + * Gets support resources for the administration user interface. + * + * @param file + * The name of the static resource to provide. + * @return The requested document in a response. + * @throws IOException + */ + @GET + @Path("static/{file}") + @Produces("*/*") + Response getStaticResource(@PathParam("file") String file) + throws IOException; + + /** + * Get a description of the administration interface. + * + * @param ui + * What URI was used to access this resource? + * @return The description document. + */ + @GET + @Path(ROOT) + @Produces({ XML, JSON }) + @Nonnull + AdminDescription getDescription(@Context UriInfo ui); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(ROOT) + Response optionsRoot(); + + /** + * Get whether to allow new workflow runs to be created. + * + * @return The current setting. + */ + @GET + @Path(ALLOW_NEW) + @Produces(PLAIN) + @Description("Whether to allow new workflow runs to be created.") + boolean getAllowNew(); + + /** + * Set whether to allow new workflow runs to be created. + * + * @param newValue + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(ALLOW_NEW) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("Whether to allow new workflow runs to be created.") + boolean setAllowNew(boolean newValue); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(ALLOW_NEW) + @Description("Whether to allow new workflow runs to be created.") + Response optionsAllowNew(); + + /** + * Get whether to log the workflows submitted. + * + * @return The current setting. + */ + @GET + @Path(LOG_WFS) + @Produces(PLAIN) + @Description("Whether to log the workflows submitted.") + boolean getLogWorkflows(); + + /** + * Set whether to log the workflows submitted. + * + * @param logWorkflows + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(LOG_WFS) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("Whether to log the workflows submitted.") + boolean setLogWorkflows(boolean logWorkflows); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(LOG_WFS) + @Description("Whether to log the workflows submitted.") + Response optionsLogWorkflows(); + + /** + * Get whether to log the user-directed faults. + * + * @return The current setting. + */ + @GET + @Path(LOG_EXN) + @Produces(PLAIN) + @Description("Whether to log the user-directed faults.") + boolean getLogFaults(); + + /** + * Set whether to log the user-directed faults. + * + * @param logFaults + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(LOG_EXN) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("Whether to log the user-directed faults.") + boolean setLogFaults(boolean logFaults); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(LOG_EXN) + @Description("Whether to log the user-directed faults.") + Response optionsLogFaults(); + + /** + * Get what file to dump usage records to. + * + * @return The current setting. + */ + @GET + @Path(UR_FILE) + @Produces(PLAIN) + @Description("What file to dump usage records to.") + @Nonnull + String getURFile(); + + /** + * Set what file to dump usage records to. + * + * @param urFile + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(UR_FILE) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What file to dump usage records to.") + @Nonnull + String setURFile(@Nonnull String urFile); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(UR_FILE) + @Description("What file to dump usage records to.") + Response optionsURFile(); + + /** + * The property for the number of times the service methods have been + * invoked. + * + * @return The property value (read-only). + */ + @GET + @Path(INVOKES) + @Produces(PLAIN) + @Description("How many times have the service methods been invoked.") + int invokeCount(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(INVOKES) + @Description("How many times have the service methods been invoked.") + Response optionsInvokationCount(); + + /** + * The property for the number of runs that are currently in existence. + * + * @return The property value (read-only). + */ + @GET + @Path(TOTAL_RUNS) + @Produces(PLAIN) + @Description("How many runs are currently in existence.") + int runCount(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(TOTAL_RUNS) + @Description("How many runs are currently in existence.") + Response optionsRunCount(); + + /** + * The property for the number of runs that are currently running. + * + * @return The property value (read-only). + */ + @GET + @Path(OPERATING) + @Produces(PLAIN) + @Description("How many runs are currently actually running.") + int operatingCount(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(OPERATING) + @Description("How many runs are currently actually running.") + Response optionsOperatingCount(); + + /** + * Get the full pathname of the RMI registry's JAR. + * + * @return The current setting. + */ + @GET + @Path(REG_JAR) + @Produces(PLAIN) + @Description("What is the full pathname of the server's custom RMI registry executable JAR file?") + @Nonnull + String getRegistryJar(); + + /** + * Set the full pathname of the RMI registry's JAR. + * + * @param registryJar + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(REG_JAR) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the full pathname of the server's custom RMI registry executable JAR file?") + @Nonnull + String setRegistryJar(@Nonnull String registryJar); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(REG_JAR) + @Description("What is the full pathname of the server's special custom RMI registry executable JAR file?") + Response optionsRegistryJar(); + + /** + * Get the location of the RMI registry. + * + * @return The current setting. + */ + @GET + @Path(REG_HOST) + @Produces(PLAIN) + @Description("Where is the RMI registry?") + @Nonnull + String getRegistryHost(); + + /** + * Set the location of the RMI registry. + * + * @param registryHost + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(REG_HOST) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("Where is the RMI registry?") + @Nonnull + String setRegistryHost(@Nonnull String registryHost); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(REG_HOST) + @Description("Where is the RMI registry?") + Response optionsRegistryHost(); + + /** + * Get the port of the RMI registry. + * + * @return The current setting. + */ + @GET + @Path(REG_PORT) + @Produces(PLAIN) + @Description("On what port is the RMI registry?") + int getRegistryPort(); + + /** + * Set the port of the RMI registry. + * + * @param registryPort + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(REG_PORT) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("On what port is the RMI registry?") + int setRegistryPort(int registryPort); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(REG_PORT) + @Description("On what port is the RMI registry?") + Response optionsRegistryPort(); + + /** + * Get the maximum number of simultaneous runs. + * + * @return The current setting. + */ + @GET + @Path(RUN_LIMIT) + @Produces(PLAIN) + @Description("What is the maximum number of simultaneous runs?") + int getRunLimit(); + + /** + * Set the maximum number of simultaneous runs. + * + * @param runLimit + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(RUN_LIMIT) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the maximum number of simultaneous runs?") + int setRunLimit(int runLimit); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(RUN_LIMIT) + @Description("What is the maximum number of simultaneous runs?") + Response optionsRunLimit(); + + /** + * Get the maximum number of simultaneous executing runs. + * + * @return The current setting. + */ + @GET + @Path(OP_LIMIT) + @Produces(PLAIN) + @Description("What is the maximum number of simultaneous executing runs?") + int getOperatingLimit(); + + /** + * Set the maximum number of simultaneous executing runs. + * + * @param operatingLimit + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(OP_LIMIT) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the maximum number of simultaneous executing runs?") + int setOperatingLimit(int operatingLimit); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(OP_LIMIT) + @Description("What is the maximum number of simultaneous executing runs?") + Response optionsOperatingLimit(); + + /** + * Get the default lifetime of workflow runs. + * + * @return The current setting. + */ + @GET + @Path(LIFE) + @Produces(PLAIN) + @Description("What is the default lifetime of workflow runs, in seconds?") + int getDefaultLifetime(); + + /** + * Set the default lifetime of workflow runs. + * + * @param defaultLifetime + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(LIFE) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the default lifetime of workflow runs, in seconds?") + int setDefaultLifetime(int defaultLifetime); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(LIFE) + @Description("What is the default lifetime of workflow runs, in seconds?") + Response optionsDefaultLifetime(); + + /** + * The property for the list of IDs of current runs. + * + * @return The property value (read-only). + */ + @GET + @Path(RUNS) + @Produces({ XML, JSON }) + @Description("List the IDs of all current runs.") + StringList currentRuns(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(RUNS) + @Description("List the IDs of all current runs.") + Response optionsCurrentRuns(); + + /** + * Get the Java binary to be used for execution of subprocesses. + * + * @return The current setting. + */ + @GET + @Path(JAVA) + @Produces(PLAIN) + @Description("Which Java binary should be used for execution of subprocesses?") + @Nonnull + String getJavaBinary(); + + /** + * Set the Java binary to be used for execution of subprocesses. + * + * @param javaBinary + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(JAVA) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("Which Java binary should be used for execution of subprocesses?") + @Nonnull + String setJavaBinary(@Nonnull String javaBinary); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(JAVA) + @Description("Which Java binary should be used for execution of subprocesses?") + Response optionsJavaBinary(); + + /** + * Get the extra arguments to be supplied to Java subprocesses. + * + * @return The current setting. + */ + @GET + @Path(ARGS) + @Produces({ XML, JSON }) + @Description("What extra arguments should be supplied to Java subprocesses?") + @Nonnull + StringList getExtraArguments(); + + /** + * Set the extra arguments to be supplied to Java subprocesses. + * + * @param extraArguments + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(ARGS) + @Consumes(XML) + @Produces({ XML, JSON }) + @Description("What extra arguments should be supplied to Java subprocesses?") + @Nonnull + StringList setExtraArguments(@Nonnull StringList extraArguments); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(ARGS) + @Description("What extra arguments should be supplied to Java subprocesses?") + Response optionsExtraArguments(); + + /** + * Get the full pathname of the worker JAR file. + * + * @return The current setting. + */ + @GET + @Path(JAR_WORKER) + @Produces(PLAIN) + @Description("What is the full pathname of the server's per-user worker executable JAR file?") + @Nonnull + String getServerWorkerJar(); + + /** + * Set the full pathname of the worker JAR file. + * + * @param serverWorkerJar + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(JAR_WORKER) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the full pathname of the server's per-user worker executable JAR file?") + @Nonnull + String setServerWorkerJar(@Nonnull String serverWorkerJar); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(JAR_WORKER) + @Description("What is the full pathname of the server's per-user worker executable JAR file?") + Response optionsServerWorkerJar(); + + /** + * Get the full pathname of the executeWorkflow.sh file. + * + * @return The current setting. + */ + @GET + @Path(EXEC_WF) + @Produces(PLAIN) + @Description("What is the full pathname of the core Taverna executeWorkflow script?") + @Nonnull + String getExecuteWorkflowScript(); + + /** + * Set the full pathname of the executeWorkflow.sh file. + * + * @param executeWorkflowScript + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(EXEC_WF) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the full pathname of the core Taverna executeWorkflow script?") + @Nonnull + String setExecuteWorkflowScript(@Nonnull String executeWorkflowScript); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(EXEC_WF) + @Description("What is the full pathname of the core Taverna executeWorkflow script?") + Response optionsExecuteWorkflowScript(); + + /** + * Get the total duration of time to wait for the start of the forker + * process. + * + * @return The current setting. + */ + @GET + @Path(REG_WAIT) + @Produces(PLAIN) + @Description("How long in total should the core wait for registration of the \"forker\" process, in seconds.") + int getRegistrationWaitSeconds(); + + /** + * Set the total duration of time to wait for the start of the forker + * process. + * + * @param registrationWaitSeconds + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(REG_WAIT) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("How long in total should the core wait for registration of the \"forker\" process, in seconds.") + int setRegistrationWaitSeconds(int registrationWaitSeconds); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(REG_WAIT) + @Description("How long in total should the core wait for registration of the \"forker\" process, in seconds.") + Response optionsRegistrationWaitSeconds(); + + /** + * Get the interval between checks for registration of the forker process. + * + * @return The current setting. + */ + @GET + @Path(REG_POLL) + @Produces(PLAIN) + @Description("What is the interval between checks for registration of the \"forker\" process, in milliseconds.") + int getRegistrationPollMillis(); + + /** + * Set the interval between checks for registration of the forker process. + * + * @param registrationPollMillis + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(REG_POLL) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the interval between checks for registration of the \"forker\" process, in milliseconds.") + int setRegistrationPollMillis(int registrationPollMillis); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(REG_POLL) + @Description("What is the interval between checks for registration of the \"forker\" process, in milliseconds.") + Response optionsRegistrationPollMillis(); + + /** + * Get the full pathname of the file containing the impersonation + * credentials for the forker process. + * + * @return The current setting. + */ + @GET + @Path(PASSFILE) + @Produces(PLAIN) + @Description("What is the full pathname of the file containing the password used for impersonating other users? (On Unix, this is the password for the deployment user to use \"sudo\".)") + @Nonnull + String getRunasPasswordFile(); + + /** + * Set the full pathname of the file containing the impersonation + * credentials for the forker process. + * + * @param runasPasswordFile + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(PASSFILE) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the full pathname of the file containing the password used for impersonating other users? (On Unix, this is the password for the deployment user to use \"sudo\".)") + @Nonnull + String setRunasPasswordFile(@Nonnull String runasPasswordFile); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(PASSFILE) + @Description("What is the full pathname of the file containing the password used for impersonating other users? (On Unix, this is the password for the deployment user to use \"sudo\".)") + Response optionsRunasPasswordFile(); + + /** + * Get the full pathname of the forker's JAR. + * + * @return The current setting. + */ + @GET + @Path(JAR_FORKER) + @Produces(PLAIN) + @Description("What is the full pathname of the server's special authorized \"forker\" executable JAR file?") + @Nonnull + String getServerForkerJar(); + + /** + * Set the full pathname of the forker's JAR. + * + * @param serverForkerJar + * What to set it to. + * @return The new setting. + */ + @PUT + @Path(JAR_FORKER) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("What is the full pathname of the server's special authorized \"forker\" executable JAR file?") + @Nonnull + String setServerForkerJar(@Nonnull String serverForkerJar); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(JAR_FORKER) + @Description("What is the full pathname of the server's special authorized \"forker\" executable JAR file?") + Response optionsServerForkerJar(); + + /** + * The property for the length of time it took to start the forker. + * + * @return The property value (read-only). + */ + @GET + @Path(STARTUP) + @Produces(PLAIN) + @Description("How long did it take for the back-end \"forker\" to set itself up, in seconds.") + int startupTime(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(STARTUP) + @Description("How long did it take for the back-end \"forker\" to set itself up, in seconds.") + Response optionsStartupTime(); + + /** + * The property for the last exit code of the forker process. + * + * @return The property value (read-only). + */ + @GET + @Path(EXITCODE) + @Produces(PLAIN) + @Description("What was the last exit code of the \"forker\"? If null, no exit has ever been recorded.") + Integer lastExitCode(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(EXITCODE) + @Description("What was the last exit code of the \"forker\"? If null, no exit has ever been recorded.") + Response optionsLastExitCode(); + + /** + * The property for the mapping of usernames to factory process handles. + * + * @return The property value (read-only). + */ + @GET + @Path(FACTORIES) + @Produces({ XML, JSON }) + @Description("What is the mapping of local usernames to factory process RMI IDs?") + StringList factoryProcessMapping(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(FACTORIES) + @Description("What is the mapping of local usernames to factory process RMI IDs?") + Response optionsFactoryProcessMapping(); + + /** + * The property for the list of usage records collected. + * + * @return The property value (read-only). + */ + @GET + @Path(URS) + @Produces(XML) + @Description("What is the list of usage records that have been collected?") + URList usageRecords(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(URS) + @Description("What is the list of usage records that have been collected?") + Response optionsUsageRecords(); + + /** + * What are the current list of workflow URIs that may be started? Empty + * means allow any, including user-supplied workflows. + * + * @return List of URIs, encoded as strings. + */ + @GET + @Path(PERM_WF) + @Produces({ XML, JSON }) + @Description("What are the current list of workflow URIs that may be started? Empty means allow any, including user-supplied workflows.") + StringList getPermittedWorkflowURIs(); + + /** Do we turn on the generate provenance option by default? */ + @GET + @Path(GEN_PROV) + @Produces(PLAIN) + @Description("Do we turn on the generate provenance option by default? (boolean)") + String getGenerateProvenance(); + + /** Do we turn on the generate provenance option by default? */ + @PUT + @Path(GEN_PROV) + @Consumes(PLAIN) + @Produces(PLAIN) + @Description("Do we turn on the generate provenance option by default? (boolean)") + String setGenerateProvenance(String newValue); + + /** Do we turn on the generate provenance option by default? */ + @OPTIONS + @Path(GEN_PROV) + @Description("Do we turn on the generate provenance option by default? (boolean)") + Response optionsGenerateProvenance(); + + /** + * What are the current list of workflow URIs that may be started? Empty + * means allow any, including user-supplied workflows. + * + * @param permitted + * List of URIs, encoded as strings. + * @return List of URIs, encoded as strings. + */ + @PUT + @Path(PERM_WF) + @Consumes(XML) + @Produces({ XML, JSON }) + @Description("What are the current list of workflow URIs that may be started? Empty means allow any, including user-supplied workflows.") + StringList setPermittedWorkflowURIs(@Nonnull StringList permitted); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(PERM_WF) + @Description("What are the current list of workflow URIs that may be started? Empty means allow any, including user-supplied workflows.") + Response optionsPermittedWorkflowURIs(); + + @GET + @Path(USERS) + @Produces({ XML, JSON }) + @Description("What users are known to the server?") + UserList users(@Context UriInfo ui); + + @GET + @Path(USER) + @Produces({ XML, JSON }) + @Description("What do we know about a particular user?") + UserDesc user(@PathParam("id") String username); + + @POST + @Path(USERS) + @Consumes(XML) + @Description("Create a user.") + Response useradd(UserDesc userdesc, @Nonnull @Context UriInfo ui); + + @PUT + @Path(USER) + @Produces({ XML, JSON }) + @Consumes(XML) + @Description("Update a user.") + UserDesc userset(@PathParam("id") String username, UserDesc userdesc); + + @DELETE + @Path(USER) + @Produces({ XML, JSON }) + @Description("What do we know about a particular user?") + Response userdel(@PathParam("id") String username); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(USERS) + @Description("What users are known to the server?") + Response optionsUsers(); + + /** What HTTP methods may we use? */ + @OPTIONS + @Path(USER) + @Description("What do we know about a particular user?") + Response optionsUser(@PathParam("id") String username); + + // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + /** + * The description of what properties are supported by the administration + * interface. + * + * @author Donal Fellows + */ + @XmlRootElement(name = "description") + @XmlType(name = "Description") + public static class AdminDescription extends VersionedElement { + public Uri allowNew; + public Uri logWorkflows; + public Uri logFaults; + public Uri usageRecordDumpFile; + public Uri invokationCount; + public Uri runCount; + public Uri registryHost; + public Uri registryPort; + public Uri runLimit; + public Uri defaultLifetime; + public Uri currentRuns; + public Uri javaBinary; + public Uri extraArguments; + public Uri serverWorkerJar; + public Uri serverForkerJar; + public Uri executeWorkflowScript; + public Uri registrationWaitSeconds; + public Uri registrationPollMillis; + public Uri runasPasswordFile; + public Uri startupTime; + public Uri lastExitCode; + public Uri factoryProcessMapping; + public Uri usageRecords; + public Uri users; + public Uri operatingLimit; + public Uri operatingCount; + public Uri permittedWorkflowURIs; + public Uri generateProvenance; + + public AdminDescription() { + } + + public AdminDescription(UriInfo ui) { + allowNew = new Uri(ui, ALLOW_NEW); + logWorkflows = new Uri(ui, LOG_WFS); + logFaults = new Uri(ui, LOG_EXN); + usageRecordDumpFile = new Uri(ui, UR_FILE); + invokationCount = new Uri(ui, INVOKES); + runCount = new Uri(ui, TOTAL_RUNS); + registryHost = new Uri(ui, REG_HOST); + registryPort = new Uri(ui, REG_PORT); + runLimit = new Uri(ui, RUN_LIMIT); + defaultLifetime = new Uri(ui, LIFE); + currentRuns = new Uri(ui, RUNS); + javaBinary = new Uri(ui, JAVA); + extraArguments = new Uri(ui, ARGS); + serverWorkerJar = new Uri(ui, JAR_WORKER); + serverForkerJar = new Uri(ui, JAR_FORKER); + executeWorkflowScript = new Uri(ui, EXEC_WF); + registrationWaitSeconds = new Uri(ui, REG_WAIT); + registrationPollMillis = new Uri(ui, REG_POLL); + runasPasswordFile = new Uri(ui, PASSFILE); + startupTime = new Uri(ui, STARTUP); + lastExitCode = new Uri(ui, EXITCODE); + factoryProcessMapping = new Uri(ui, FACTORIES); + usageRecords = new Uri(ui, URS); + users = new Uri(ui, USERS); + operatingLimit = new Uri(ui, OP_LIMIT); + operatingCount = new Uri(ui, OPERATING); + permittedWorkflowURIs = new Uri(ui, PERM_WF); + generateProvenance = new Uri(ui, GEN_PROV); + } + } + + /** + * A list of strings, as XML. + * + * @author Donal Fellows + */ + @XmlRootElement(name = "stringList") + @XmlType(name = "StringList") + public static class StringList { + @XmlElement + public List<String> string = new ArrayList<>(); + } + + /** + * A list of users, as XML. + * + * @author Donal Fellows + */ + @XmlRootElement(name = "userList") + @XmlType(name = "UserList") + public static class UserList { + @XmlElement + public List<URI> user = new ArrayList<>(); + } + + @XmlRootElement(name = "userDesc") + @XmlType(name = "UserDesc") + public static class UserDesc { + @XmlElement + public String username; + @XmlElement + public String password; + @XmlElement + public String localUserId; + @XmlElement + public Boolean enabled; + @XmlElement + public Boolean admin; + } + + /** + * A list of usage records, as XML. + * + * @author Donal Fellows + */ + @XmlRootElement(name = "usageRecordList") + @XmlType(name = "UsageRecords") + public static class URList { + @XmlElement + public List<JobUsageRecord> usageRecord; + } +} + +interface Paths { + static final String ROOT = "/"; + static final String ALLOW_NEW = "allowNew"; + static final String LOG_WFS = "logWorkflows"; + static final String LOG_EXN = "logFaults"; + static final String UR_FILE = "usageRecordDumpFile"; + static final String INVOKES = "invokationCount"; + static final String TOTAL_RUNS = "runCount"; + static final String OPERATING = "operatingCount"; + static final String REG_HOST = "registryHost"; + static final String REG_PORT = "registryPort"; + static final String REG_JAR = "registryJar"; + static final String RUN_LIMIT = "runLimit"; + static final String OP_LIMIT = "operatingLimit"; + static final String LIFE = "defaultLifetime"; + static final String RUNS = "currentRuns"; + static final String JAVA = "javaBinary"; + static final String ARGS = "extraArguments"; + static final String JAR_WORKER = "serverWorkerJar"; + static final String JAR_FORKER = "serverForkerJar"; + static final String EXEC_WF = "executeWorkflowScript"; + static final String REG_WAIT = "registrationWaitSeconds"; + static final String REG_POLL = "registrationPollMillis"; + static final String PASSFILE = "runasPasswordFile"; + static final String STARTUP = "startupTime"; + static final String EXITCODE = "lastExitCode"; + static final String FACTORIES = "factoryProcessMapping"; + static final String URS = "usageRecords"; + static final String PERM_WF = "permittedWorkflowURIs"; + static final String GEN_PROV = "generateProvenance"; + static final String USERS = "users"; + static final String USER = USERS + "/{id}"; +} + +interface Types { + static final String PLAIN = "text/plain"; + static final String XML = "application/xml"; + static final String JSON = "application/json"; +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/AdminBean.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/AdminBean.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/AdminBean.java new file mode 100644 index 0000000..0233cd5 --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/AdminBean.java @@ -0,0 +1,807 @@ +/* + */ +package org.taverna.server.master.admin; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import static java.util.Arrays.asList; +import static java.util.UUID.randomUUID; +import static javax.ws.rs.core.Response.created; +import static javax.ws.rs.core.Response.noContent; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static org.taverna.server.master.common.Roles.ADMIN; +import static org.taverna.server.master.common.Uri.secure; +import static org.taverna.server.master.utils.RestUtils.opt; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Required; +import org.taverna.server.master.api.ManagementModel; +import org.taverna.server.master.exceptions.GeneralFailureException; +import org.taverna.server.master.factories.ConfigurableRunFactory; +import org.taverna.server.master.identity.User; +import org.taverna.server.master.identity.UserStoreAPI; +import org.taverna.server.master.usage.UsageRecordRecorder; +import org.taverna.server.master.utils.InvocationCounter; +import org.taverna.server.master.worker.RunDBSupport; +import org.taverna.server.master.worker.WorkerModel; + +/** + * The administration interface to Taverna Server. + * + * @author Donal Fellows + */ +public class AdminBean implements Admin { + @Required + public void setState(ManagementModel state) { + this.state = state; + } + + @Required + public void setCounter(InvocationCounter counter) { + this.counter = counter; + } + + @Required + public void setRunDB(RunDBSupport runDB) { + this.runDB = runDB; + } + + @Required + public void setFactory(ConfigurableRunFactory factory) { + this.factory = factory; + } + + @Required + public void setUsageRecords(UsageRecordRecorder usageRecords) { + this.usageRecords = usageRecords; + } + + @Required + public void setUserStore(UserStoreAPI userStore) { + this.userStore = userStore; + } + + @Required + public void setLocalWorkerModel(WorkerModel worker) { + localWorker = worker; + } + + public void setAdminHtmlFile(String filename) { + this.adminHtmlFile = filename; + } + + public void setResourceRoot(String root) { + this.resourceRoot = root; + } + + protected byte[] getResource(String name) throws IOException { + if (AdminBean.class.getResource(name) == null) + throw new FileNotFoundException(name); + return IOUtils.toByteArray(AdminBean.class.getResourceAsStream(name)); + } + + private ManagementModel state; + private InvocationCounter counter; + private RunDBSupport runDB; + private ConfigurableRunFactory factory; + private UsageRecordRecorder usageRecords; + private UserStoreAPI userStore; + private WorkerModel localWorker; + private String adminHtmlFile = "/admin.html"; + private String resourceRoot = "/static/"; + + @RolesAllowed(ADMIN) + @Override + public Response getUserInterface() throws IOException { + return Response.ok(getResource(adminHtmlFile), "text/html").build(); + } + + @Override + @RolesAllowed(ADMIN) + public Response getStaticResource(String file) throws IOException { + if (file.matches("^[-_.a-zA-Z0-9]+$")) { + String type = "application/octet-stream"; + if (file.endsWith(".html")) + type = "text/html"; + else if (file.endsWith(".js")) + type = "text/javascript"; + else if (file.endsWith(".jpg")) + type = "image/jpeg"; + else if (file.endsWith(".gif")) + type = "image/gif"; + else if (file.endsWith(".png")) + type = "image/png"; + else if (file.endsWith(".svg")) + type = "image/svg+xml"; + else if (file.endsWith(".css")) + type = "text/css"; + try { + return Response.ok(getResource(resourceRoot + file), type) + .build(); + } catch (IOException e) { + // ignore; we just treat as 404 + } + } + return Response.status(NOT_FOUND).entity("no such resource").build(); + } + + @RolesAllowed(ADMIN) + @Override + public AdminDescription getDescription(UriInfo ui) { + return new AdminDescription(ui); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsRoot() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public boolean getAllowNew() { + return state.getAllowNewWorkflowRuns(); + } + + @RolesAllowed(ADMIN) + @Override + public boolean setAllowNew(boolean newValue) { + state.setAllowNewWorkflowRuns(newValue); + return state.getAllowNewWorkflowRuns(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsAllowNew() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public boolean getLogWorkflows() { + return state.getLogIncomingWorkflows(); + } + + @RolesAllowed(ADMIN) + @Override + public boolean setLogWorkflows(boolean newValue) { + state.setLogIncomingWorkflows(newValue); + return state.getLogIncomingWorkflows(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsLogWorkflows() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public boolean getLogFaults() { + return state.getLogOutgoingExceptions(); + } + + @RolesAllowed(ADMIN) + @Override + public boolean setLogFaults(boolean newValue) { + state.setLogOutgoingExceptions(newValue); + return state.getLogOutgoingExceptions(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsLogFaults() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getURFile() { + return state.getUsageRecordLogFile(); + } + + @RolesAllowed(ADMIN) + @Override + public String setURFile(String newValue) { + state.setUsageRecordLogFile(newValue); + return state.getUsageRecordLogFile(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsURFile() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int invokeCount() { + return counter.getCount(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public Response optionsInvokationCount() { + return opt(); + } + + @RolesAllowed(ADMIN) + @Override + public int runCount() { + return runDB.countRuns(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsRunCount() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getRegistryHost() { + return factory.getRegistryHost(); + } + + @RolesAllowed(ADMIN) + @Override + public String setRegistryHost(String newValue) { + factory.setRegistryHost(newValue); + return factory.getRegistryHost(); + } + + @Override + public Response optionsRegistryHost() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int getRegistryPort() { + return factory.getRegistryPort(); + } + + @RolesAllowed(ADMIN) + @Override + public int setRegistryPort(int newValue) { + factory.setRegistryPort(newValue); + return factory.getRegistryPort(); + } + + @Override + public Response optionsRegistryPort() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getRegistryJar() { + return factory.getRmiRegistryJar(); + } + + @RolesAllowed(ADMIN) + @Override + public String setRegistryJar(String registryJar) { + factory.setRmiRegistryJar(registryJar); + return factory.getRmiRegistryJar(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsRegistryJar() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int getRunLimit() { + return factory.getMaxRuns(); + } + + @RolesAllowed(ADMIN) + @Override + public int setRunLimit(int newValue) { + factory.setMaxRuns(newValue); + return factory.getMaxRuns(); + } + + @Override + public Response optionsRunLimit() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int getDefaultLifetime() { + return factory.getDefaultLifetime(); + } + + @RolesAllowed(ADMIN) + @Override + public int setDefaultLifetime(int newValue) { + factory.setDefaultLifetime(newValue); + return factory.getDefaultLifetime(); + } + + @Override + public Response optionsDefaultLifetime() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public StringList currentRuns() { + StringList result = new StringList(); + result.string = runDB.listRunNames(); + return result; + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsCurrentRuns() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getJavaBinary() { + return factory.getJavaBinary(); + } + + @RolesAllowed(ADMIN) + @Override + public String setJavaBinary(String newValue) { + factory.setJavaBinary(newValue); + return factory.getJavaBinary(); + } + + @Override + public Response optionsJavaBinary() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public StringList getExtraArguments() { + String[] xargs = factory.getExtraArguments(); + StringList result = new StringList(); + result.string = asList(xargs == null ? new String[0] : xargs); + return result; + } + + @RolesAllowed(ADMIN) + @Override + public StringList setExtraArguments(StringList newValue) { + if (newValue == null || newValue.string == null) + factory.setExtraArguments(new String[0]); + else + factory.setExtraArguments(newValue.string + .toArray(new String[newValue.string.size()])); + StringList result = new StringList(); + result.string = asList(factory.getExtraArguments()); + return result; + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsExtraArguments() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getServerWorkerJar() { + return factory.getServerWorkerJar(); + } + + @RolesAllowed(ADMIN) + @Override + public String setServerWorkerJar(String newValue) { + factory.setServerWorkerJar(newValue); + return factory.getServerWorkerJar(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsServerWorkerJar() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getExecuteWorkflowScript() { + return factory.getExecuteWorkflowScript(); + } + + @RolesAllowed(ADMIN) + @Override + public String setExecuteWorkflowScript(String newValue) { + factory.setExecuteWorkflowScript(newValue); + return factory.getExecuteWorkflowScript(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsExecuteWorkflowScript() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int getRegistrationWaitSeconds() { + return factory.getWaitSeconds(); + } + + @RolesAllowed(ADMIN) + @Override + public int setRegistrationWaitSeconds(int newValue) { + factory.setWaitSeconds(newValue); + return factory.getWaitSeconds(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsRegistrationWaitSeconds() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int getRegistrationPollMillis() { + return factory.getSleepTime(); + } + + @RolesAllowed(ADMIN) + @Override + public int setRegistrationPollMillis(int newValue) { + factory.setSleepTime(newValue); + return factory.getSleepTime(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsRegistrationPollMillis() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getRunasPasswordFile() { + return factory.getPasswordFile(); + } + + @RolesAllowed(ADMIN) + @Override + public String setRunasPasswordFile(String newValue) { + factory.setPasswordFile(newValue); + return factory.getPasswordFile(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsRunasPasswordFile() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getServerForkerJar() { + return factory.getServerForkerJar(); + } + + @RolesAllowed(ADMIN) + @Override + public String setServerForkerJar(String newValue) { + factory.setServerForkerJar(newValue); + return factory.getServerForkerJar(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsServerForkerJar() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int startupTime() { + return factory.getLastStartupCheckCount(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsStartupTime() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public Integer lastExitCode() { + return factory.getLastExitCode(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsLastExitCode() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public StringList factoryProcessMapping() { + StringList result = new StringList(); + result.string = asList(factory.getFactoryProcessMapping()); + return result; + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsFactoryProcessMapping() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public URList usageRecords() { + URList result = new URList(); + result.usageRecord = usageRecords.getUsageRecords(); + return result; + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsUsageRecords() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public UserList users(UriInfo ui) { + UserList ul = new UserList(); + UriBuilder ub = secure(ui).path("{id}"); + for (String user : userStore.getUserNames()) + ul.user.add(ub.build(user)); + return ul; + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsUsers() { + return opt("POST"); + } + + @RolesAllowed(ADMIN) + @Override + public UserDesc user(String username) { + UserDesc desc = new UserDesc(); + User u = userStore.getUser(username); + desc.username = u.getUsername(); + desc.localUserId = u.getLocalUsername(); + desc.admin = u.isAdmin(); + desc.enabled = u.isEnabled(); + return desc; + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsUser(String username) { + return opt("PUT", "DELETE"); + } + + @RolesAllowed(ADMIN) + @Override + public Response useradd(UserDesc userdesc, UriInfo ui) { + if (userdesc.username == null) + throw new IllegalArgumentException("no user name supplied"); + if (userdesc.password == null) + userdesc.password = randomUUID().toString(); + userStore.addUser(userdesc.username, userdesc.password, false); + if (userdesc.localUserId != null) + userStore.setUserLocalUser(userdesc.username, userdesc.localUserId); + if (userdesc.admin != null && userdesc.admin) + userStore.setUserAdmin(userdesc.username, true); + if (userdesc.enabled != null && userdesc.enabled) + userStore.setUserEnabled(userdesc.username, true); + return created(secure(ui).path("{id}").build(userdesc.username)) + .build(); + } + + @RolesAllowed(ADMIN) + @Override + public UserDesc userset(String username, UserDesc userdesc) { + if (userdesc.password != null) + userStore.setUserPassword(username, userdesc.password); + if (userdesc.localUserId != null) + userStore.setUserLocalUser(username, userdesc.localUserId); + if (userdesc.admin != null) + userStore.setUserAdmin(username, userdesc.admin); + if (userdesc.enabled != null) + userStore.setUserEnabled(username, userdesc.enabled); + userdesc = null; // Stop reuse! + + UserDesc desc = new UserDesc(); + User u = userStore.getUser(username); + desc.username = u.getUsername(); + desc.localUserId = u.getLocalUsername(); + desc.admin = u.isAdmin(); + desc.enabled = u.isEnabled(); + return desc; + } + + @RolesAllowed(ADMIN) + @Override + public Response userdel(String username) { + userStore.deleteUser(username); + return noContent().build(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int operatingCount() { + try { + return factory.getOperatingCount(); + } catch (Exception e) { + throw new GeneralFailureException(e); + } + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsOperatingCount() { + return opt(); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public int getOperatingLimit() { + return factory.getOperatingLimit(); + } + + @RolesAllowed(ADMIN) + @Override + public int setOperatingLimit(int operatingLimit) { + factory.setOperatingLimit(operatingLimit); + return factory.getOperatingLimit(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsOperatingLimit() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + @RolesAllowed(ADMIN) + @Override + public StringList getPermittedWorkflowURIs() { + StringList sl = new StringList(); + List<URI> uris = localWorker.getPermittedWorkflowURIs(); + if (uris != null) + for (URI uri : uris) + sl.string.add(uri.toString()); + return sl; + } + + private static final URI myExp = URI.create("http://www.myexperment.org/"); + + @RolesAllowed(ADMIN) + @Override + public StringList setPermittedWorkflowURIs(StringList permitted) { + List<URI> uris = new ArrayList<>(); + for (String uri : permitted.string) + try { + uris.add(myExp.resolve(uri)); + } catch (Exception e) { + // Ignore + } + localWorker.setPermittedWorkflowURIs(uris); + return getPermittedWorkflowURIs(); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsPermittedWorkflowURIs() { + return opt("PUT"); + } + + // ///////////////////////////////////////////////////// + + @RolesAllowed(ADMIN) + @Override + public String getGenerateProvenance() { + return Boolean.toString(localWorker.getGenerateProvenance()); + } + + @RolesAllowed(ADMIN) + @Override + public String setGenerateProvenance(String newValue) { + boolean b = Boolean.parseBoolean(newValue); + localWorker.setGenerateProvenance(b); + return Boolean.toString(localWorker.getGenerateProvenance()); + } + + @RolesAllowed(ADMIN) + @Override + public Response optionsGenerateProvenance() { + 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/admin/package-info.java ---------------------------------------------------------------------- diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/package-info.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/package-info.java new file mode 100644 index 0000000..0a4174e --- /dev/null +++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/admin/package-info.java @@ -0,0 +1,46 @@ +/* + */ +/** + * This package contains the RESTful administration interface to Taverna Server. + * @author Donal Fellows + */ +@XmlSchema(namespace = ADMIN, elementFormDefault = QUALIFIED, attributeFormDefault = QUALIFIED, xmlns = { + @XmlNs(prefix = "xlink", namespaceURI = XLINK), + @XmlNs(prefix = "ts", namespaceURI = SERVER), + @XmlNs(prefix = "ts-rest", namespaceURI = SERVER_REST), + @XmlNs(prefix = "ts-soap", namespaceURI = SERVER_SOAP), + @XmlNs(prefix = "feed", namespaceURI = FEED), + @XmlNs(prefix = "admin", namespaceURI = ADMIN), + @XmlNs(prefix = "ur", namespaceURI = UR), + @XmlNs(prefix = "ds", namespaceURI = XSIG) }) +package org.taverna.server.master.admin; +/* + * 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.xml.bind.annotation.XmlNsForm.QUALIFIED; +import static org.taverna.server.master.common.Namespaces.ADMIN; +import static org.taverna.server.master.common.Namespaces.FEED; +import static org.taverna.server.master.common.Namespaces.SERVER; +import static org.taverna.server.master.common.Namespaces.SERVER_REST; +import static org.taverna.server.master.common.Namespaces.SERVER_SOAP; +import static org.taverna.server.master.common.Namespaces.UR; +import static org.taverna.server.master.common.Namespaces.XLINK; +import static org.taverna.server.master.common.Namespaces.XSIG; + +import javax.xml.bind.annotation.XmlNs; +import javax.xml.bind.annotation.XmlSchema; +
