You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2018/01/09 23:30:50 UTC
[34/42] incubator-taverna-server git commit: package org.taverna ->
org.apache.taverna
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java
deleted file mode 100644
index bc91f0e..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteRunFactory.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- */
-package org.taverna.server.localworker.remote;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.util.UUID;
-
-import org.taverna.server.localworker.server.UsageRecordReceiver;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * The main RMI-enabled interface for creating runs.
- *
- * @author Donal Fellows
- */
-public interface RemoteRunFactory extends Remote {
- /**
- * Makes a workflow run that will process a particular workflow document.
- *
- * @param workflow
- * The (serialised) workflow to instantiate as a run.
- * @param creator
- * Who is this run created for?
- * @param usageRecordReceiver
- * Where to write any usage records. May be <tt>null</tt> to
- * cause them to not be written.
- * @param masterID
- * The UUID of the run to use, or <tt>null</tt> if the execution
- * engine is to manufacture a new one for itself.
- * @return A remote handle for the run.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- RemoteSingleRun make(@Nonnull byte[] workflow, @Nonnull String creator,
- @Nullable UsageRecordReceiver usageRecordReceiver,
- @Nullable UUID masterID) throws RemoteException;
-
- /**
- * Asks this factory to unregister itself from the registry and cease
- * operation.
- *
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- void shutdown() throws RemoteException;
-
- /**
- * Configures the details to use when setting up the workflow run's
- * connnection to the interaction feed.
- *
- * @param host
- * The host where the feed is located.
- * @param port
- * The port where the feed is located.
- * @param webdavPath
- * The path used for pushing web pages into the feed.
- * @param feedPath
- * The path used for reading and writing notifications on the
- * feed.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- void setInteractionServiceDetails(@Nonnull String host,
- @Nonnull String port, @Nonnull String webdavPath,
- @Nonnull String feedPath) throws RemoteException;
-
- /**
- * Gets a count of the number of {@linkplain RemoteSingleRun workflow runs}
- * that this factor knows about that are in the
- * {@link RemoteStatus#Operating Operating} state.
- *
- * @return A count of "running" workflow runs.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- int countOperatingRuns() throws RemoteException;
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSecurityContext.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSecurityContext.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSecurityContext.java
deleted file mode 100644
index fc4baff..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSecurityContext.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- */
-package org.taverna.server.localworker.remote;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.net.URI;
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-/**
- * Outline of the security context for a workflow run.
- *
- * @author Donal Fellows
- */
-public interface RemoteSecurityContext extends Remote {
- void setKeystore(@Nonnull byte[] keystore) throws RemoteException,
- ImplementationException;
-
- void setPassword(@Nonnull char[] password) throws RemoteException,
- ImplementationException;
-
- void setTruststore(@Nonnull byte[] truststore) throws RemoteException,
- ImplementationException;
-
- void setUriToAliasMap(@Nonnull Map<URI, String> uriToAliasMap)
- throws RemoteException;
-
- void setHelioToken(@Nonnull String helioToken) throws RemoteException;
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSingleRun.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSingleRun.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSingleRun.java
deleted file mode 100644
index e2519f8..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteSingleRun.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- */
-package org.taverna.server.localworker.remote;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.net.URL;
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-import java.util.Date;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public interface RemoteSingleRun extends Remote {
- /**
- * @return The name of the Baclava file to use for all inputs, or
- * <tt>null</tt> if no Baclava file is set.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nullable
- public String getInputBaclavaFile() throws RemoteException;
-
- /**
- * Sets the Baclava file to use for all inputs. This overrides the use of
- * individual inputs.
- *
- * @param filename
- * The filename to use. Must not start with a <tt>/</tt> or
- * contain any <tt>..</tt> segments. Will be interpreted relative
- * to the run's working directory.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- public void setInputBaclavaFile(@Nonnull String filename)
- throws RemoteException;
-
- /**
- * @return The list of input assignments.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public List<RemoteInput> getInputs() throws RemoteException;
-
- /**
- * Create an input assignment.
- *
- * @param name
- * The name of the port that this will be an input for.
- * @return The assignment reference.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public RemoteInput makeInput(@Nonnull String name) throws RemoteException;
-
- /**
- * @return The file (relative to the working directory) to write the outputs
- * of the run to as a Baclava document, or <tt>null</tt> if they are
- * to be written to non-Baclava files in a directory called
- * <tt>out</tt>.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nullable
- public String getOutputBaclavaFile() throws RemoteException;
-
- /**
- * Sets where the output of the run is to be written to. This will cause the
- * output to be generated as a Baclava document, rather than a collection of
- * individual non-Baclava files in the subdirectory of the working directory
- * called <tt>out</tt>.
- *
- * @param filename
- * Where to write the Baclava file (or <tt>null</tt> to cause the
- * output to be written to individual files); overwrites any
- * previous setting of this value.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- public void setOutputBaclavaFile(@Nullable String filename)
- throws RemoteException;
-
- /**
- * @return The current status of the run.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public RemoteStatus getStatus() throws RemoteException;
-
- /**
- * Set the status of the run, which should cause it to move into the given
- * state. This may cause some significant changes.
- *
- * @param s
- * The state to try to change to.
- * @throws IllegalStateTransitionException
- * If the requested state change is impossible. (Note that it is
- * always legal to set the status to the current status.)
- * @throws RemoteException
- * If anything goes wrong with the communication.
- * @throws ImplementationException
- * If something goes horribly wrong on the back end.
- * @throws StillWorkingOnItException
- * If the startup time of the workflow implementation exceeds a
- * built-in threshold.
- */
- public void setStatus(@Nonnull RemoteStatus s)
- throws IllegalStateTransitionException, RemoteException,
- ImplementationException, StillWorkingOnItException;
-
- /**
- * @return When this workflow run was found to have finished, or
- * <tt>null</tt> if it has never finished (either still running or
- * never started).
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nullable
- public Date getFinishTimestamp() throws RemoteException;
-
- /**
- * @return When this workflow run was started, or <tt>null</tt> if it has
- * never been started.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nullable
- public Date getStartTimestamp() throws RemoteException;
-
- /**
- * @return Handle to the main working directory of the run.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public RemoteDirectory getWorkingDirectory() throws RemoteException;
-
- /**
- * @return The list of listener instances attached to the run.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public List<RemoteListener> getListeners() throws RemoteException;
-
- /**
- * Add a listener to the run.
- *
- * @param listener
- * The listener to add.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- * @throws ImplementationException
- * If something goes wrong when adding the listener.
- */
- public void addListener(@Nonnull RemoteListener listener)
- throws RemoteException, ImplementationException;
-
- /**
- * @return The security context structure for this run.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- * @throws ImplementationException
- * If something goes wrong when getting the context.
- */
- @Nonnull
- public RemoteSecurityContext getSecurityContext() throws RemoteException,
- ImplementationException;
-
- /**
- * Kill off this run, removing all resources which it consumes.
- *
- * @throws RemoteException
- * If anything goes wrong with the communication.
- * @throws ImplementationException
- * If something goes horribly wrong when destroying the run.
- */
- public void destroy() throws RemoteException, ImplementationException;
-
- /**
- * Get the types of listener supported by this run.
- *
- * @return A list of listener type names.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public List<String> getListenerTypes() throws RemoteException;
-
- /**
- * Create a listener that can be attached to this run.
- *
- * @param type
- * The type name of the listener to create; it must be one of the
- * names returned by the {@link #getListenerTypes()} operation.
- * @param configuration
- * The configuration document for this listener. The nature of
- * the contents of this are determined by the type.
- * @return A handle for the listener.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- @Nonnull
- public RemoteListener makeListener(@Nonnull String type,
- @Nonnull String configuration) throws RemoteException;
-
- /**
- * Configures the details to use when setting up the workflow run's
- * connnection to the interaction feed.
- *
- * @param interactionFeed
- * The location of the interaction feed. If <tt>null</tt>,
- * defaults from the factory will be used instead.
- * @param webdavPath
- * The location used for pushing web pages to support the feed.
- * If <tt>null</tt>, a default from the factory will be used
- * instead.
- * @param publishUrlBase
- * Where to <i>actually</i> publish to, if this needs to be
- * different from the location presented in the published HTML
- * and Feed entries. Necessary in complex network scenarios.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- void setInteractionServiceDetails(@Nonnull URL interactionFeed,
- @Nonnull URL webdavPath, @Nullable URL publishUrlBase) throws RemoteException;
-
- /**
- * A do-nothing method, used to check the general reachability of the
- * workflow run.
- *
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- void ping() throws RemoteException;
-
- /**
- * Sets whether we should generate provenance information from a run.
- *
- * @param generateProvenance
- * Boolean flag, true for do the generation. Must be set before
- * starting the run for this to have an effect.
- * @throws RemoteException
- * If anything goes wrong with the communication.
- */
- void setGenerateProvenance(boolean generateProvenance)
- throws RemoteException;
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteStatus.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteStatus.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteStatus.java
deleted file mode 100644
index 89a7cff..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/RemoteStatus.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- */
-package org.taverna.server.localworker.remote;
-/*
- * 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.
- */
-
-/**
- * States of a workflow run. They are {@link RemoteStatus#Initialized
- * Initialized}, {@link RemoteStatus#Operating Operating},
- * {@link RemoteStatus#Stopped Stopped}, and {@link RemoteStatus#Finished
- * Finished}. Conceptually, there is also a <tt>Destroyed</tt> state, but the
- * workflow run does not exist (and hence can't have its state queried or set)
- * in that case.
- *
- * @author Donal Fellows
- */
-public enum RemoteStatus {
- /**
- * The workflow run has been created, but is not yet running. The run will
- * need to be manually moved to {@link #Operating} when ready.
- */
- Initialized,
- /**
- * The workflow run is going, reading input, generating output, etc. Will
- * eventually either move automatically to {@link #Finished} or can be moved
- * manually to {@link #Stopped} (where supported).
- */
- Operating,
- /**
- * The workflow run is paused, and will need to be moved back to
- * {@link #Operating} manually.
- */
- Stopped,
- /**
- * The workflow run has ceased; data files will continue to exist until the
- * run is destroyed (which may be manual or automatic).
- */
- Finished
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/StillWorkingOnItException.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/StillWorkingOnItException.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/StillWorkingOnItException.java
deleted file mode 100644
index a7af96b..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/StillWorkingOnItException.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- */
-package org.taverna.server.localworker.remote;
-/*
- * 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.
- */
-
-/**
- * Exception that indicates that the implementation is still working on
- * processing the operation. Note that though this is an exception, it is <i>not
- * a failure</i>.
- *
- * @author Donal Fellows
- */
-@SuppressWarnings("serial")
-public class StillWorkingOnItException extends Exception {
- public StillWorkingOnItException(String string) {
- super(string);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/package-info.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/package-info.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/package-info.java
deleted file mode 100644
index 6466e2f..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/remote/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- */
-/**
- * Interfaces exported by worker classes to the server.
- */
-package org.taverna.server.localworker.remote;
-/*
- * 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.
- */
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/UsageRecordReceiver.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/UsageRecordReceiver.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/UsageRecordReceiver.java
deleted file mode 100644
index cc0127e..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/UsageRecordReceiver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- */
-package org.taverna.server.localworker.server;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-
-/**
- * Interface exported by (part of) the webapp to allow processes it creates to
- * push in usage records.
- *
- * @author Donal Fellows
- */
-public interface UsageRecordReceiver extends Remote {
- /**
- * Called to push in a usage record. Note that it is assumed that the usage
- * record already contains all the information required to locate and
- * process the job; there is no separate handle.
- *
- * @param usageRecord
- * The serialised XML of the usage record.
- * @throws RemoteException
- * if anything goes wrong.
- */
- void acceptUsageRecord(String usageRecord) throws RemoteException;
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/package-info.java
----------------------------------------------------------------------
diff --git a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/package-info.java b/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/package-info.java
deleted file mode 100644
index 584f1ed..0000000
--- a/taverna-server-runinterface/src/main/java/org/taverna/server/localworker/server/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- */
-/**
- * Interfaces exported by the server to worker classes.
- */
-package org.taverna.server.localworker.server;
-/*
- * 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.
- */
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-unix-forker/src/main/java/org/apache/taverna/server/unixforker/Forker.java
----------------------------------------------------------------------
diff --git a/taverna-server-unix-forker/src/main/java/org/apache/taverna/server/unixforker/Forker.java b/taverna-server-unix-forker/src/main/java/org/apache/taverna/server/unixforker/Forker.java
new file mode 100644
index 0000000..ebe73f4
--- /dev/null
+++ b/taverna-server-unix-forker/src/main/java/org/apache/taverna/server/unixforker/Forker.java
@@ -0,0 +1,221 @@
+/*
+ */
+package org.taverna.server.unixforker;
+/*
+ * 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.System.err;
+import static java.lang.System.getProperty;
+import static java.lang.System.in;
+import static java.lang.System.out;
+import static java.util.Arrays.asList;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A simple class that forks off processes when asked to over its standard
+ * input. The one complication is that it forks them off as other users, through
+ * the use of the <tt>sudo</tt> utility. It is Unix-specific.
+ *
+ * @author Donal Fellows
+ */
+public class Forker extends Thread {
+ private static String password;
+ private static BufferedReader br;
+
+ /**
+ * Helper to make reading a password from a file clearer. The password must
+ * be the first line of the file.
+ *
+ * @param passwordFile
+ * The file to load from.
+ * @throws IOException
+ * If anything goes wrong.
+ */
+ private static void loadPassword(@Nonnull File passwordFile)
+ throws IOException {
+ try {
+ err.println("attempting to load password from " + passwordFile);
+ try (FileReader fr = new FileReader(passwordFile)) {
+ password = new BufferedReader(fr).readLine();
+ }
+ } catch (IOException e) {
+ err.println("failed to read password from file " + passwordFile
+ + "described in password.file property");
+ throw e;
+ }
+ }
+
+ /**
+ * Initialization code, which runs before the main loop starts processing.
+ *
+ * @param args
+ * The arguments to the program.
+ * @throws Exception
+ * If anything goes wrong.
+ */
+ public static void init(String[] args) throws Exception {
+ if (args.length < 1)
+ throw new IllegalArgumentException(
+ "wrong # args: must be \"program ?argument ...?\"");
+ if (getProperty("password.file") != null)
+ loadPassword(new File(getProperty("password.file")));
+ if (password == null)
+ err.println("no password.file property or empty file; "
+ + "assuming password-less sudo is configured");
+ else
+ err.println("password is of length " + password.length());
+ br = new BufferedReader(new InputStreamReader(in));
+ }
+
+ /**
+ * The body of the main loop of this program.
+ *
+ * @param args
+ * The arguments to use when running the other program.
+ * @return Whether to repeat the loop.
+ * @throws Exception
+ * If anything goes wrong. Note that the loop is repeated if an
+ * exception occurs in it.
+ */
+ public static boolean mainLoopBody(String[] args) throws Exception {
+ String line = br.readLine();
+ if (line == null)
+ return false;
+ List<String> vals = asList(line.split("[ \t]+"));
+ if (vals.size() != 2) {
+ out.println("wrong # values: must be \"username UUID\"");
+ return true;
+ }
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command()
+ .addAll(asList("sudo", "-u", vals.get(0), "-S", "-H", "--"));
+ pb.command().addAll(asList(args));
+ pb.command().add(vals.get(1));
+ Forker f = new Forker(pb);
+ f.setDaemon(true);
+ f.start();
+ return true;
+ }
+
+ /**
+ * The main code for this class, which turns this into an executable
+ * program. Runs the initialisation and then the main loop, in both cases
+ * with appropriate error handling.
+ *
+ * @param args
+ * Arguments to this program.
+ */
+ public static void main(String... args) {
+ try {
+ init(args);
+ while (true) {
+ try {
+ if (!mainLoopBody(args))
+ break;
+ } catch (Exception e) {
+ e.printStackTrace(err);
+ out.println(e.getClass().getName() + ": " + e.getMessage());
+ }
+ }
+ System.exit(0);
+ } catch (Exception e) {
+ e.printStackTrace(err);
+ System.exit(1);
+ }
+ }
+
+ // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+ public Forker(ProcessBuilder pb) throws IOException {
+ out.println("Starting subprocess: " + pb.command());
+ final Process p = pb.start();
+ abstract class ProcessAttachedDaemon extends Thread {
+ public ProcessAttachedDaemon() {
+ setDaemon(true);
+ start();
+ }
+
+ abstract void act() throws Exception;
+
+ @Override
+ public final void run() {
+ try {
+ act();
+ p.waitFor();
+ } catch (InterruptedException e) {
+ // Just drop
+ } catch (Exception e) {
+ p.destroy();
+ e.printStackTrace(err);
+ }
+ }
+ }
+ new ProcessAttachedDaemon() {
+ @Override
+ void act() throws Exception {
+ copyFromSudo("Subprocess(out):", p.getInputStream());
+ }
+ };
+ new ProcessAttachedDaemon() {
+ @Override
+ void act() throws Exception {
+ copyFromSudo("Subprocess(err):", p.getErrorStream());
+ }
+ };
+ new ProcessAttachedDaemon() {
+ @Override
+ void act() throws Exception {
+ interactWithSudo(p.getOutputStream());
+ }
+ };
+ }
+
+ protected void interactWithSudo(OutputStream os) throws Exception {
+ if (password != null) {
+ OutputStreamWriter osw = new OutputStreamWriter(os);
+ osw.write(password + "\n");
+ osw.flush();
+ }
+ os.close();
+ }
+
+ protected void copyFromSudo(String header, InputStream sudoStream)
+ throws Exception {
+ int b = '\n';
+ while (true) {
+ if (b == '\n')
+ out.print(header);
+ b = sudoStream.read();
+ if (b == -1)
+ break;
+ out.write(b);
+ out.flush();
+ }
+ sudoStream.close();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-unix-forker/src/main/java/org/taverna/server/unixforker/Forker.java
----------------------------------------------------------------------
diff --git a/taverna-server-unix-forker/src/main/java/org/taverna/server/unixforker/Forker.java b/taverna-server-unix-forker/src/main/java/org/taverna/server/unixforker/Forker.java
deleted file mode 100644
index ebe73f4..0000000
--- a/taverna-server-unix-forker/src/main/java/org/taverna/server/unixforker/Forker.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- */
-package org.taverna.server.unixforker;
-/*
- * 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.System.err;
-import static java.lang.System.getProperty;
-import static java.lang.System.in;
-import static java.lang.System.out;
-import static java.util.Arrays.asList;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-/**
- * A simple class that forks off processes when asked to over its standard
- * input. The one complication is that it forks them off as other users, through
- * the use of the <tt>sudo</tt> utility. It is Unix-specific.
- *
- * @author Donal Fellows
- */
-public class Forker extends Thread {
- private static String password;
- private static BufferedReader br;
-
- /**
- * Helper to make reading a password from a file clearer. The password must
- * be the first line of the file.
- *
- * @param passwordFile
- * The file to load from.
- * @throws IOException
- * If anything goes wrong.
- */
- private static void loadPassword(@Nonnull File passwordFile)
- throws IOException {
- try {
- err.println("attempting to load password from " + passwordFile);
- try (FileReader fr = new FileReader(passwordFile)) {
- password = new BufferedReader(fr).readLine();
- }
- } catch (IOException e) {
- err.println("failed to read password from file " + passwordFile
- + "described in password.file property");
- throw e;
- }
- }
-
- /**
- * Initialization code, which runs before the main loop starts processing.
- *
- * @param args
- * The arguments to the program.
- * @throws Exception
- * If anything goes wrong.
- */
- public static void init(String[] args) throws Exception {
- if (args.length < 1)
- throw new IllegalArgumentException(
- "wrong # args: must be \"program ?argument ...?\"");
- if (getProperty("password.file") != null)
- loadPassword(new File(getProperty("password.file")));
- if (password == null)
- err.println("no password.file property or empty file; "
- + "assuming password-less sudo is configured");
- else
- err.println("password is of length " + password.length());
- br = new BufferedReader(new InputStreamReader(in));
- }
-
- /**
- * The body of the main loop of this program.
- *
- * @param args
- * The arguments to use when running the other program.
- * @return Whether to repeat the loop.
- * @throws Exception
- * If anything goes wrong. Note that the loop is repeated if an
- * exception occurs in it.
- */
- public static boolean mainLoopBody(String[] args) throws Exception {
- String line = br.readLine();
- if (line == null)
- return false;
- List<String> vals = asList(line.split("[ \t]+"));
- if (vals.size() != 2) {
- out.println("wrong # values: must be \"username UUID\"");
- return true;
- }
- ProcessBuilder pb = new ProcessBuilder();
- pb.command()
- .addAll(asList("sudo", "-u", vals.get(0), "-S", "-H", "--"));
- pb.command().addAll(asList(args));
- pb.command().add(vals.get(1));
- Forker f = new Forker(pb);
- f.setDaemon(true);
- f.start();
- return true;
- }
-
- /**
- * The main code for this class, which turns this into an executable
- * program. Runs the initialisation and then the main loop, in both cases
- * with appropriate error handling.
- *
- * @param args
- * Arguments to this program.
- */
- public static void main(String... args) {
- try {
- init(args);
- while (true) {
- try {
- if (!mainLoopBody(args))
- break;
- } catch (Exception e) {
- e.printStackTrace(err);
- out.println(e.getClass().getName() + ": " + e.getMessage());
- }
- }
- System.exit(0);
- } catch (Exception e) {
- e.printStackTrace(err);
- System.exit(1);
- }
- }
-
- // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
- public Forker(ProcessBuilder pb) throws IOException {
- out.println("Starting subprocess: " + pb.command());
- final Process p = pb.start();
- abstract class ProcessAttachedDaemon extends Thread {
- public ProcessAttachedDaemon() {
- setDaemon(true);
- start();
- }
-
- abstract void act() throws Exception;
-
- @Override
- public final void run() {
- try {
- act();
- p.waitFor();
- } catch (InterruptedException e) {
- // Just drop
- } catch (Exception e) {
- p.destroy();
- e.printStackTrace(err);
- }
- }
- }
- new ProcessAttachedDaemon() {
- @Override
- void act() throws Exception {
- copyFromSudo("Subprocess(out):", p.getInputStream());
- }
- };
- new ProcessAttachedDaemon() {
- @Override
- void act() throws Exception {
- copyFromSudo("Subprocess(err):", p.getErrorStream());
- }
- };
- new ProcessAttachedDaemon() {
- @Override
- void act() throws Exception {
- interactWithSudo(p.getOutputStream());
- }
- };
- }
-
- protected void interactWithSudo(OutputStream os) throws Exception {
- if (password != null) {
- OutputStreamWriter osw = new OutputStreamWriter(os);
- osw.write(password + "\n");
- osw.flush();
- }
- os.close();
- }
-
- protected void copyFromSudo(String header, InputStream sudoStream)
- throws Exception {
- int b = '\n';
- while (true) {
- if (b == '\n')
- out.print(header);
- b = sudoStream.read();
- if (b == -1)
- break;
- out.write(b);
- out.flush();
- }
- sudoStream.close();
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ContentsDescriptorBuilder.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ContentsDescriptorBuilder.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ContentsDescriptorBuilder.java
new file mode 100644
index 0000000..051a037
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ContentsDescriptorBuilder.java
@@ -0,0 +1,305 @@
+/*
+ */
+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.getMimeType;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
+import static javax.ws.rs.core.UriBuilder.fromUri;
+import static org.apache.commons.logging.LogFactory.getLog;
+import static org.taverna.server.master.common.Uri.secure;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.commons.logging.Log;
+import org.springframework.beans.factory.annotation.Required;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.exceptions.NoDirectoryEntryException;
+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.TavernaRun;
+import org.taverna.server.master.interfaces.UriBuilderFactory;
+import org.taverna.server.master.utils.FilenameUtils;
+import org.taverna.server.port_description.AbsentValue;
+import org.taverna.server.port_description.AbstractPortDescription;
+import org.taverna.server.port_description.AbstractValue;
+import org.taverna.server.port_description.ErrorValue;
+import org.taverna.server.port_description.InputDescription;
+import org.taverna.server.port_description.InputDescription.InputPort;
+import org.taverna.server.port_description.LeafValue;
+import org.taverna.server.port_description.ListValue;
+import org.taverna.server.port_description.OutputDescription;
+import org.taverna.server.port_description.OutputDescription.OutputPort;
+
+import org.apache.taverna.scufl2.api.container.WorkflowBundle;
+import org.apache.taverna.scufl2.api.core.Workflow;
+import org.apache.taverna.scufl2.api.port.InputWorkflowPort;
+import org.apache.taverna.scufl2.api.port.OutputWorkflowPort;
+
+/**
+ * A class that is used to build descriptions of the contents of a workflow
+ * run's filesystem.
+ *
+ * @author Donal Fellows
+ */
+public class ContentsDescriptorBuilder {
+ private Log log = getLog("Taverna.Server.Webapp");
+ private FilenameUtils fileUtils;
+ private UriBuilderFactory uriBuilderFactory;
+
+ @Required
+ public void setUriBuilderFactory(UriBuilderFactory uriBuilderFactory) {
+ this.uriBuilderFactory = uriBuilderFactory;
+ }
+
+ @Required
+ public void setFileUtils(FilenameUtils fileUtils) {
+ this.fileUtils = fileUtils;
+ }
+
+ // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+ private Workflow fillInFromWorkflow(TavernaRun run, UriBuilder ub,
+ AbstractPortDescription portDesc) throws IOException {
+ WorkflowBundle bundle = run.getWorkflow().getScufl2Workflow();
+ bundle.getMainWorkflow().getInputPorts();
+ portDesc.fillInBaseData(bundle.getMainWorkflow()
+ .getIdentifier().toString(), run.getId(), ub.build());
+ return bundle.getMainWorkflow();
+ }
+
+ /**
+ * Computes the depth of value in a descriptor.
+ *
+ * @param value
+ * The value description to characterise.
+ * @return Its depth (i.e., the depth of the port outputting the value) or
+ * <tt>null</tt> if that is impossible to determine.
+ */
+ private Integer computeDepth(AbstractValue value) {
+ if (value instanceof ListValue) {
+ int mv = 1;
+ for (AbstractValue v : ((ListValue) value).contents) {
+ Integer d = computeDepth(v);
+ if (d != null && mv <= d)
+ mv = d + 1;
+ }
+ return mv;
+ } else if (value instanceof LeafValue || value instanceof ErrorValue)
+ return 0;
+ else
+ return null;
+ }
+
+ /**
+ * Build a description of a leaf value.
+ *
+ * @param file
+ * The file representing the value.
+ * @return A value descriptor.
+ * @throws FilesystemAccessException
+ * If anything goes wrong.
+ */
+ private LeafValue constructLeafValue(File file)
+ throws FilesystemAccessException {
+ LeafValue v = new LeafValue();
+ v.fileName = file.getFullName();
+ v.byteLength = file.getSize();
+ try {
+ byte[] head = file.getContents(0, 1024);
+ v.contentType = getMimeType(new ByteArrayInputStream(head));
+ } catch (Exception e) {
+ v.contentType = APPLICATION_OCTET_STREAM_TYPE.toString();
+ }
+ return v;
+ }
+
+ /**
+ * Build a description of an error value.
+ *
+ * @param file
+ * The file representing the error.
+ * @return A value descriptor.
+ * @throws FilesystemAccessException
+ * If anything goes wrong.
+ */
+ private ErrorValue constructErrorValue(File file)
+ throws FilesystemAccessException {
+ ErrorValue v = new ErrorValue();
+ v.fileName = file.getFullName();
+ v.byteLength = file.getSize();
+ return v;
+ }
+
+ /**
+ * Build a description of a list value.
+ *
+ * @param dir
+ * The directory representing the list.
+ * @param ub
+ * The factory for URIs.
+ * @return A value descriptor.
+ * @throws FilesystemAccessException
+ * If anything goes wrong.
+ */
+ private ListValue constructListValue(Directory dir, UriBuilder ub)
+ throws FilesystemAccessException {
+ ListValue v = new ListValue();
+ v.length = 0;
+ Set<DirectoryEntry> contents = new HashSet<>(dir.getContents());
+ Iterator<DirectoryEntry> it = contents.iterator();
+ while (it.hasNext())
+ if (!it.next().getName().matches("^[0-9]+([.].*)?$"))
+ it.remove();
+ for (int i = 1; !contents.isEmpty(); i++) {
+ String exact = Integer.toString(i);
+ AbstractValue subval = constructValue(contents, ub, exact);
+ v.contents.add(subval);
+ if (!(subval instanceof AbsentValue)) {
+ v.length = i;
+ String pfx = i + ".";
+ for (DirectoryEntry de : contents)
+ if (de.getName().equals(exact)
+ || de.getName().startsWith(pfx)) {
+ contents.remove(de);
+ break;
+ }
+ }
+ }
+ return v;
+ }
+
+ /**
+ * Build a value description.
+ *
+ * @param parentContents
+ * The contents of the parent directory.
+ * @param ub
+ * The factory for URIs.
+ * @param name
+ * The name of the value's file/directory representative.
+ * @return A value descriptor.
+ * @throws FilesystemAccessException
+ * If anything goes wrong.
+ */
+ private AbstractValue constructValue(
+ Collection<DirectoryEntry> parentContents, UriBuilder ub,
+ String name) throws FilesystemAccessException {
+ String error = name + ".error";
+ String prefix = name + ".";
+ for (DirectoryEntry entry : parentContents) {
+ AbstractValue av;
+ if (entry.getName().equals(error) && entry instanceof File) {
+ av = constructErrorValue((File) entry);
+ } else if (!entry.getName().equals(name)
+ && !entry.getName().startsWith(prefix))
+ continue;
+ else if (entry instanceof File)
+ av = constructLeafValue((File) entry);
+ else
+ av = constructListValue((Directory) entry, ub);
+ String fullPath = entry.getFullName().replaceFirst("^/", "");
+ av.href = ub.clone().path(fullPath).build();
+ return av;
+ }
+ return new AbsentValue();
+ }
+
+ /**
+ * Construct a description of the outputs of a workflow run.
+ *
+ * @param run
+ * The workflow run whose outputs are to be described.
+ * @param ui
+ * The origin for URIs.
+ * @return The description, which can be serialized to XML.
+ * @throws FilesystemAccessException
+ * If something goes wrong reading the directories.
+ * @throws NoDirectoryEntryException
+ * If something goes wrong reading the directories.
+ */
+ public OutputDescription makeOutputDescriptor(TavernaRun run, UriInfo ui)
+ throws FilesystemAccessException, NoDirectoryEntryException {
+ OutputDescription descriptor = new OutputDescription();
+ try {
+ UriBuilder ub = getRunUriBuilder(run, ui);
+ Workflow dataflow = fillInFromWorkflow(run, ub, descriptor);
+ Collection<DirectoryEntry> outs = null;
+ ub = ub.path("wd/{path}");
+ for (OutputWorkflowPort output : dataflow.getOutputPorts()) {
+ OutputPort p = descriptor.addPort(output.getName());
+ if (run.getOutputBaclavaFile() == null) {
+ if (outs == null)
+ outs = fileUtils.getDirectory(run, "out").getContents();
+ p.output = constructValue(outs, ub, p.name);
+ p.depth = computeDepth(p.output);
+ }
+ }
+ } catch (IOException e) {
+ log.info("failure in conversion to .scufl2", e);
+ }
+ return descriptor;
+ }
+
+ private UriBuilder getRunUriBuilder(TavernaRun run, UriInfo ui) {
+ if (ui == null)
+ return secure(uriBuilderFactory.getRunUriBuilder(run));
+ else
+ return secure(fromUri(ui.getAbsolutePath().toString()
+ .replaceAll("/(out|in)put/?$", "")));
+ }
+
+ /**
+ * Constructs input descriptions.
+ *
+ * @param run
+ * The run to build for.
+ * @param ui
+ * The mechanism for building URIs.
+ * @return The description of the <i>expected</i> inputs of the run.
+ */
+ public InputDescription makeInputDescriptor(TavernaRun run, UriInfo ui) {
+ InputDescription desc = new InputDescription();
+ try {
+ UriBuilder ub = getRunUriBuilder(run, ui);
+ Workflow workflow = fillInFromWorkflow(run, ub, desc);
+ ub = ub.path("input/{name}");
+ for (InputWorkflowPort port : workflow.getInputPorts()) {
+ InputPort in = desc.addPort(port.getName());
+ in.href = ub.build(in.name);
+ try {
+ in.depth = port.getDepth();
+ } catch (NumberFormatException ex) {
+ in.depth = null;
+ }
+ }
+ } catch (IOException e) {
+ log.info("failure in conversion to .scufl2", e);
+ }
+ return desc;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/DirectoryREST.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/DirectoryREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/DirectoryREST.java
new file mode 100644
index 0000000..b800c1c
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/DirectoryREST.java
@@ -0,0 +1,388 @@
+/*
+ */
+package org.taverna.server.master;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.ok;
+import static javax.ws.rs.core.Response.seeOther;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.commons.logging.LogFactory.getLog;
+import static org.taverna.server.master.api.ContentTypes.APPLICATION_ZIP_TYPE;
+import static org.taverna.server.master.api.ContentTypes.DIRECTORY_VARIANTS;
+import static org.taverna.server.master.api.ContentTypes.INITIAL_FILE_VARIANTS;
+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.Uri.secure;
+import static org.taverna.server.master.utils.RestUtils.opt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.core.Variant;
+import javax.xml.ws.Holder;
+
+import org.apache.commons.logging.Log;
+import org.springframework.beans.factory.annotation.Required;
+import org.taverna.server.master.api.DirectoryBean;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.exceptions.NoDirectoryEntryException;
+import org.taverna.server.master.exceptions.NoUpdateException;
+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.TavernaRun;
+import org.taverna.server.master.rest.DirectoryContents;
+import org.taverna.server.master.rest.FileSegment;
+import org.taverna.server.master.rest.MakeOrUpdateDirEntry;
+import org.taverna.server.master.rest.MakeOrUpdateDirEntry.MakeDirectory;
+import org.taverna.server.master.rest.TavernaServerDirectoryREST;
+import org.taverna.server.master.utils.FilenameUtils;
+import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;
+import org.taverna.server.master.utils.InvocationCounter.CallCounted;
+
+/**
+ * RESTful access to the filesystem.
+ *
+ * @author Donal Fellows
+ */
+class DirectoryREST implements TavernaServerDirectoryREST, DirectoryBean {
+ private Log log = getLog("Taverna.Server.Webapp");
+ private TavernaServerSupport support;
+ private TavernaRun run;
+ private FilenameUtils fileUtils;
+
+ @Override
+ public void setSupport(TavernaServerSupport support) {
+ this.support = support;
+ }
+
+ @Override
+ @Required
+ public void setFileUtils(FilenameUtils fileUtils) {
+ this.fileUtils = fileUtils;
+ }
+
+ @Override
+ public DirectoryREST connect(TavernaRun run) {
+ this.run = run;
+ return this;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Response destroyDirectoryEntry(List<PathSegment> path)
+ throws NoUpdateException, FilesystemAccessException,
+ NoDirectoryEntryException {
+ support.permitUpdate(run);
+ fileUtils.getDirEntry(run, path).destroy();
+ return noContent().build();
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public DirectoryContents getDescription(UriInfo ui)
+ throws FilesystemAccessException {
+ return new DirectoryContents(ui, run.getWorkingDirectory()
+ .getContents());
+ }
+
+ @Override
+ @CallCounted
+ public Response options(List<PathSegment> path) {
+ return opt("PUT", "POST", "DELETE");
+ }
+
+ /*
+ * // Nasty! This can have several different responses...
+ *
+ * @Override @CallCounted private Response
+ * getDirectoryOrFileContents(List<PathSegment> path, UriInfo ui, Request
+ * req) throws FilesystemAccessException, NoDirectoryEntryException {
+ *
+ * DirectoryEntry de = fileUtils.getDirEntry(run, path);
+ *
+ * // How did the user want the result?
+ *
+ * List<Variant> variants = getVariants(de); Variant v =
+ * req.selectVariant(variants); if (v == null) return
+ * notAcceptable(variants).type(TEXT_PLAIN)
+ * .entity("Do not know what type of response to produce.") .build();
+ *
+ * // Produce the content to deliver up
+ *
+ * Object result; if
+ * (v.getMediaType().equals(APPLICATION_OCTET_STREAM_TYPE))
+ *
+ * // Only for files...
+ *
+ * result = de; else if (v.getMediaType().equals(APPLICATION_ZIP_TYPE))
+ *
+ * // Only for directories...
+ *
+ * result = ((Directory) de).getContentsAsZip(); else
+ *
+ * // Only for directories... // XML or JSON; let CXF pick what to do
+ *
+ * result = new DirectoryContents(ui, ((Directory) de).getContents());
+ * return ok(result).type(v.getMediaType()).build();
+ *
+ * }
+ */
+
+ private boolean matchType(MediaType a, MediaType b) {
+ if (log.isDebugEnabled())
+ log.debug("comparing " + a.getType() + "/" + a.getSubtype()
+ + " and " + b.getType() + "/" + b.getSubtype());
+ return (a.isWildcardType() || b.isWildcardType() || a.getType().equals(
+ b.getType()))
+ && (a.isWildcardSubtype() || b.isWildcardSubtype() || a
+ .getSubtype().equals(b.getSubtype()));
+ }
+
+ /**
+ * What are we willing to serve up a directory or file as?
+ *
+ * @param de
+ * The reference to the object to serve.
+ * @return The variants we can serve it as.
+ * @throws FilesystemAccessException
+ * If we fail to read data necessary to detection of its media
+ * type.
+ */
+ private List<Variant> getVariants(DirectoryEntry de)
+ throws FilesystemAccessException {
+ if (de instanceof Directory)
+ return DIRECTORY_VARIANTS;
+ else if (!(de instanceof File))
+ throw new FilesystemAccessException("not a directory or file!");
+ File f = (File) de;
+ List<Variant> variants = new ArrayList<>(INITIAL_FILE_VARIANTS);
+ String contentType = support.getEstimatedContentType(f);
+ if (!contentType.equals(APPLICATION_OCTET_STREAM)) {
+ String[] ct = contentType.split("/");
+ variants.add(0,
+ new Variant(new MediaType(ct[0], ct[1]), (String) null, null));
+ }
+ return variants;
+ }
+
+ /** How did the user want the result? */
+ private MediaType pickType(HttpHeaders headers, DirectoryEntry de)
+ throws FilesystemAccessException, NegotiationFailedException {
+ List<Variant> variants = getVariants(de);
+ // Manual content negotiation!!! Ugh!
+ for (MediaType mt : headers.getAcceptableMediaTypes())
+ for (Variant v : variants)
+ if (matchType(mt, v.getMediaType()))
+ return v.getMediaType();
+ throw new NegotiationFailedException(
+ "Do not know what type of response to produce.", variants);
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Response getDirectoryOrFileContents(List<PathSegment> path,
+ UriInfo ui, HttpHeaders headers) throws FilesystemAccessException,
+ NoDirectoryEntryException, NegotiationFailedException {
+ DirectoryEntry de = fileUtils.getDirEntry(run, path);
+
+ // How did the user want the result?
+ MediaType wanted = pickType(headers, de);
+
+ log.info("producing content of type " + wanted);
+ // Produce the content to deliver up
+ Object result;
+ if (de instanceof File) {
+ // Only for files...
+ result = de;
+ List<String> range = headers.getRequestHeader("Range");
+ if (range != null && range.size() == 1)
+ return new FileSegment((File) de, range.get(0))
+ .toResponse(wanted);
+ } else {
+ // Only for directories...
+ Directory d = (Directory) de;
+ if (wanted.getType().equals(APPLICATION_ZIP_TYPE.getType())
+ && wanted.getSubtype().equals(
+ APPLICATION_ZIP_TYPE.getSubtype()))
+ result = d.getContentsAsZip();
+ else
+ // XML or JSON; let CXF pick what to do
+ result = new DirectoryContents(ui, d.getContents());
+ }
+ return ok(result).type(wanted).build();
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Response makeDirectoryOrUpdateFile(List<PathSegment> parent,
+ MakeOrUpdateDirEntry op, UriInfo ui) throws NoUpdateException,
+ FilesystemAccessException, NoDirectoryEntryException {
+ support.permitUpdate(run);
+ DirectoryEntry container = fileUtils.getDirEntry(run, parent);
+ if (!(container instanceof Directory))
+ throw new FilesystemAccessException("You may not "
+ + ((op instanceof MakeDirectory) ? "make a subdirectory of"
+ : "place a file in") + " a file.");
+ if (op.name == null || op.name.length() == 0)
+ throw new FilesystemAccessException("missing name attribute");
+ Directory d = (Directory) container;
+ UriBuilder ub = secure(ui).path("{name}");
+
+ // Make a directory in the context directory
+
+ if (op instanceof MakeDirectory) {
+ Directory target = d.makeSubdirectory(support.getPrincipal(),
+ op.name);
+ return created(ub.build(target.getName())).build();
+ }
+
+ // Make or set the contents of a file
+
+ File f = null;
+ for (DirectoryEntry e : d.getContents()) {
+ if (e.getName().equals(op.name)) {
+ if (e instanceof Directory)
+ throw new FilesystemAccessException(
+ "You may not overwrite a directory with a file.");
+ f = (File) e;
+ break;
+ }
+ }
+ if (f == null) {
+ f = d.makeEmptyFile(support.getPrincipal(), op.name);
+ f.setContents(op.contents);
+ return created(ub.build(f.getName())).build();
+ }
+ f.setContents(op.contents);
+ return seeOther(ub.build(f.getName())).build();
+ }
+
+ private File getFileForWrite(List<PathSegment> filePath,
+ Holder<Boolean> isNew) throws FilesystemAccessException,
+ NoDirectoryEntryException, NoUpdateException {
+ support.permitUpdate(run);
+ if (filePath == null || filePath.size() == 0)
+ throw new FilesystemAccessException(
+ "Cannot create a file that is not in a directory.");
+
+ List<PathSegment> dirPath = new ArrayList<>(filePath);
+ String name = dirPath.remove(dirPath.size() - 1).getPath();
+ DirectoryEntry de = fileUtils.getDirEntry(run, dirPath);
+ if (!(de instanceof Directory)) {
+ throw new FilesystemAccessException(
+ "Cannot create a file that is not in a directory.");
+ }
+ Directory d = (Directory) de;
+
+ File f = null;
+ isNew.value = false;
+ for (DirectoryEntry e : d.getContents())
+ if (e.getName().equals(name)) {
+ if (e instanceof File) {
+ f = (File) e;
+ break;
+ }
+ throw new FilesystemAccessException(
+ "Cannot create a file that is not in a directory.");
+ }
+
+ if (f == null) {
+ f = d.makeEmptyFile(support.getPrincipal(), name);
+ isNew.value = true;
+ } else
+ f.setContents(new byte[0]);
+ return f;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Response setFileContents(List<PathSegment> filePath,
+ InputStream contents, UriInfo ui) throws NoDirectoryEntryException,
+ NoUpdateException, FilesystemAccessException {
+ Holder<Boolean> isNew = new Holder<>(true);
+ support.copyStreamToFile(contents, getFileForWrite(filePath, isNew));
+
+ if (isNew.value)
+ return created(ui.getAbsolutePath()).build();
+ else
+ return noContent().build();
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed(USER)
+ public Response setFileContentsFromURL(List<PathSegment> filePath,
+ List<URI> referenceList, UriInfo ui)
+ throws NoDirectoryEntryException, NoUpdateException,
+ FilesystemAccessException {
+ support.permitUpdate(run);
+ if (referenceList.isEmpty() || referenceList.size() > 1)
+ return status(422).entity("URI list must have single URI in it")
+ .build();
+ URI uri = referenceList.get(0);
+ try {
+ uri.toURL();
+ } catch (MalformedURLException e) {
+ return status(422).entity("URI list must have value URL in it")
+ .build();
+ }
+ Holder<Boolean> isNew = new Holder<>(true);
+ File f = getFileForWrite(filePath, isNew);
+
+ try {
+ support.copyDataToFile(uri, f);
+ } catch (MalformedURLException ex) {
+ // Should not happen; called uri.toURL() successfully above
+ throw new NoUpdateException("failed to parse URI", ex);
+ } catch (IOException ex) {
+ throw new FilesystemAccessException(
+ "failed to transfer data from URI", ex);
+ }
+
+ if (isNew.value)
+ return created(ui.getAbsolutePath()).build();
+ else
+ return noContent().build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/FileConcatenation.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/FileConcatenation.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/FileConcatenation.java
new file mode 100644
index 0000000..4ccb033
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/FileConcatenation.java
@@ -0,0 +1,84 @@
+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 java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.interfaces.File;
+
+/**
+ * Simple concatenation of files.
+ *
+ * @author Donal Fellows
+ */
+public class FileConcatenation implements Iterable<File> {
+ private List<File> files = new ArrayList<>();
+
+ public void add(File f) {
+ files.add(f);
+ }
+
+ public boolean isEmpty() {
+ return files.isEmpty();
+ }
+
+ /**
+ * @return The total length of the files, or -1 if this cannot be
+ * determined.
+ */
+ public long size() {
+ long size = 0;
+ for (File f : files)
+ try {
+ size += f.getSize();
+ } catch (FilesystemAccessException e) {
+ // Ignore; shouldn't happen but can't guarantee
+ }
+ return (size == 0 && !files.isEmpty() ? -1 : size);
+ }
+
+ /**
+ * Get the concatenated files.
+ *
+ * @param encoding
+ * The encoding to use.
+ * @return The concatenated files.
+ * @throws UnsupportedEncodingException
+ * If the encoding doesn't exist.
+ */
+ public String get(String encoding) throws UnsupportedEncodingException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (File f : files)
+ try {
+ baos.write(f.getContents(0, -1));
+ } catch (FilesystemAccessException | IOException e) {
+ continue;
+ }
+ return baos.toString(encoding);
+ }
+
+ @Override
+ public Iterator<File> iterator() {
+ return files.iterator();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InputREST.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InputREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InputREST.java
new file mode 100644
index 0000000..b9c8f55
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InputREST.java
@@ -0,0 +1,265 @@
+/*
+ */
+package org.taverna.server.master;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static java.util.UUID.randomUUID;
+import static org.taverna.server.master.utils.RestUtils.opt;
+
+import java.util.Date;
+
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.model.URITemplate;
+import org.springframework.beans.factory.annotation.Required;
+import org.taverna.server.master.api.InputBean;
+import org.taverna.server.master.common.DirEntryReference;
+import org.taverna.server.master.exceptions.BadInputPortNameException;
+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.NoDirectoryEntryException;
+import org.taverna.server.master.exceptions.NoUpdateException;
+import org.taverna.server.master.exceptions.UnknownRunException;
+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.TavernaRun;
+import org.taverna.server.master.rest.TavernaServerInputREST;
+import org.taverna.server.master.rest.TavernaServerInputREST.InDesc.AbstractContents;
+import org.taverna.server.master.rest.TavernaServerInputREST.InDesc.Reference;
+import org.taverna.server.master.utils.FilenameUtils;
+import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;
+import org.taverna.server.master.utils.InvocationCounter.CallCounted;
+import org.taverna.server.port_description.InputDescription;
+
+/**
+ * RESTful interface to the input descriptor of a single workflow run.
+ *
+ * @author Donal Fellows
+ */
+class InputREST implements TavernaServerInputREST, InputBean {
+ private UriInfo ui;
+ private TavernaServerSupport support;
+ private TavernaRun run;
+ private ContentsDescriptorBuilder cdBuilder;
+ private FilenameUtils fileUtils;
+
+ @Override
+ public void setSupport(TavernaServerSupport support) {
+ this.support = support;
+ }
+
+ @Override
+ @Required
+ public void setCdBuilder(ContentsDescriptorBuilder cdBuilder) {
+ this.cdBuilder = cdBuilder;
+ }
+
+ @Override
+ @Required
+ public void setFileUtils(FilenameUtils fileUtils) {
+ this.fileUtils = fileUtils;
+ }
+
+ @Override
+ public InputREST connect(TavernaRun run, UriInfo ui) {
+ this.run = run;
+ this.ui = ui;
+ return this;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public InputsDescriptor get() {
+ return new InputsDescriptor(ui, run);
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public InputDescription getExpected() {
+ return cdBuilder.makeInputDescriptor(run, ui);
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public String getBaclavaFile() {
+ String i = run.getInputBaclavaFile();
+ return i == null ? "" : i;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public InDesc getInput(String name, UriInfo ui) throws BadInputPortNameException {
+ Input i = support.getInput(run, name);
+ if (i == null)
+ throw new BadInputPortNameException("unknown input port name");
+ return new InDesc(i, ui);
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public String setBaclavaFile(String filename) throws NoUpdateException,
+ BadStateChangeException, FilesystemAccessException {
+ support.permitUpdate(run);
+ run.setInputBaclavaFile(filename);
+ String i = run.getInputBaclavaFile();
+ return i == null ? "" : i;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public InDesc setInput(String name, InDesc inputDescriptor, UriInfo ui)
+ throws NoUpdateException, BadStateChangeException,
+ FilesystemAccessException, BadInputPortNameException,
+ BadPropertyValueException {
+ inputDescriptor.descriptorRef = null;
+ AbstractContents ac = inputDescriptor.assignment;
+ if (name == null || name.isEmpty())
+ throw new BadInputPortNameException("bad input name");
+ if (ac == null)
+ throw new BadPropertyValueException("no content!");
+ if (inputDescriptor.delimiter != null
+ && inputDescriptor.delimiter.isEmpty())
+ inputDescriptor.delimiter = null;
+ if (ac instanceof InDesc.Reference)
+ return setRemoteInput(name, (InDesc.Reference) ac,
+ inputDescriptor.delimiter, ui);
+ if (!(ac instanceof InDesc.File || ac instanceof InDesc.Value))
+ throw new BadPropertyValueException("unknown content type");
+ support.permitUpdate(run);
+ Input i = support.getInput(run, name);
+ if (i == null)
+ i = run.makeInput(name);
+ if (ac instanceof InDesc.File)
+ i.setFile(ac.contents);
+ else
+ i.setValue(ac.contents);
+ i.setDelimiter(inputDescriptor.delimiter);
+ return new InDesc(i, ui);
+ }
+
+ private InDesc setRemoteInput(String name, Reference ref, String delimiter,
+ UriInfo ui) throws BadStateChangeException,
+ BadPropertyValueException, FilesystemAccessException {
+ URITemplate tmpl = new URITemplate(ui.getBaseUri()
+ + "/runs/{runName}/wd/{path:.+}");
+ MultivaluedMap<String, String> mvm = new MetadataMap<>();
+ if (!tmpl.match(ref.contents, mvm)) {
+ throw new BadPropertyValueException(
+ "URI in reference does not refer to local disk resource");
+ }
+ try {
+ File from = fileUtils.getFile(
+ support.getRun(mvm.get("runName").get(0)),
+ SyntheticDirectoryEntry.make(mvm.get("path").get(0)));
+ File to = run.getWorkingDirectory().makeEmptyFile(
+ support.getPrincipal(), randomUUID().toString());
+
+ to.copy(from);
+
+ Input i = support.getInput(run, name);
+ if (i == null)
+ i = run.makeInput(name);
+ i.setFile(to.getFullName());
+ i.setDelimiter(delimiter);
+ return new InDesc(i, ui);
+ } catch (UnknownRunException e) {
+ throw new BadStateChangeException("may not copy from that run", e);
+ } catch (NoDirectoryEntryException e) {
+ throw new BadStateChangeException("source does not exist", e);
+ }
+ }
+
+ @Override
+ @CallCounted
+ public Response options() {
+ return opt();
+ }
+
+ @Override
+ @CallCounted
+ public Response expectedOptions() {
+ return opt();
+ }
+
+ @Override
+ @CallCounted
+ public Response baclavaOptions() {
+ return opt("PUT");
+ }
+
+ @Override
+ @CallCounted
+ public Response inputOptions(@PathParam("name") String name) {
+ return opt("PUT");
+ }
+}
+
+/**
+ * A way to create synthetic directory entries, used during deletion.
+ *
+ * @author Donal Fellows
+ */
+class SyntheticDirectoryEntry implements DirectoryEntry {
+ public static DirEntryReference make(String path) {
+ return DirEntryReference.newInstance(new SyntheticDirectoryEntry(path));
+ }
+
+ private SyntheticDirectoryEntry(String p) {
+ this.p = p;
+ this.d = new Date();
+ }
+
+ private String p;
+ private Date d;
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public String getFullName() {
+ return p;
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public int compareTo(DirectoryEntry o) {
+ return p.compareTo(o.getFullName());
+ }
+
+ @Override
+ public Date getModificationDate() {
+ return d;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InteractionFeed.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InteractionFeed.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InteractionFeed.java
new file mode 100644
index 0000000..6dfa3e2
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/InteractionFeed.java
@@ -0,0 +1,120 @@
+/*
+ */
+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 org.taverna.server.master.common.Roles.SELF;
+import static org.taverna.server.master.common.Roles.USER;
+import static org.taverna.server.master.utils.RestUtils.opt;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.core.Response;
+
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.taverna.server.master.api.FeedBean;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.exceptions.NoDirectoryEntryException;
+import org.taverna.server.master.exceptions.NoUpdateException;
+import org.taverna.server.master.interaction.InteractionFeedSupport;
+import org.taverna.server.master.interfaces.TavernaRun;
+import org.taverna.server.master.rest.InteractionFeedREST;
+import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;
+import org.taverna.server.master.utils.InvocationCounter.CallCounted;
+
+/**
+ * How to connect an interaction feed to the webapp.
+ *
+ * @author Donal Fellows
+ */
+public class InteractionFeed implements InteractionFeedREST, FeedBean {
+ private InteractionFeedSupport interactionFeed;
+ private TavernaRun run;
+
+ @Override
+ public void setInteractionFeedSupport(InteractionFeedSupport feed) {
+ this.interactionFeed = feed;
+ }
+
+ InteractionFeed connect(TavernaRun run) {
+ this.run = run;
+ return this;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Feed getFeed() throws FilesystemAccessException,
+ NoDirectoryEntryException {
+ return interactionFeed.getRunFeed(run);
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Response addEntry(Entry entry) throws MalformedURLException,
+ FilesystemAccessException, NoDirectoryEntryException,
+ NoUpdateException {
+ Entry realEntry = interactionFeed.addRunFeedEntry(run, entry);
+ URI location;
+ try {
+ location = realEntry.getSelfLink().getHref().toURI();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("failed to make URI from link?!", e);
+ }
+ return Response.created(location).entity(realEntry)
+ .type("application/atom+xml;type=entry").build();
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public Entry getEntry(String id) throws FilesystemAccessException,
+ NoDirectoryEntryException {
+ return interactionFeed.getRunFeedEntry(run, id);
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ @RolesAllowed({ USER, SELF })
+ public String deleteEntry(String id) throws FilesystemAccessException,
+ NoDirectoryEntryException, NoUpdateException {
+ interactionFeed.removeRunFeedEntry(run, id);
+ return "entry successfully deleted";
+ }
+
+ @Override
+ @CallCounted
+ public Response feedOptions() {
+ return opt("POST");
+ }
+
+ @Override
+ @CallCounted
+ public Response entryOptions(String id) {
+ return opt("DELETE");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/00397eff/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenerPropertyREST.java
----------------------------------------------------------------------
diff --git a/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenerPropertyREST.java b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenerPropertyREST.java
new file mode 100644
index 0000000..26a8982
--- /dev/null
+++ b/taverna-server-webapp/src/main/java/org/apache/taverna/server/master/ListenerPropertyREST.java
@@ -0,0 +1,91 @@
+/*
+ */
+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 org.apache.commons.logging.LogFactory.getLog;
+import static org.taverna.server.master.utils.RestUtils.opt;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.logging.Log;
+import org.taverna.server.master.api.ListenerPropertyBean;
+import org.taverna.server.master.exceptions.NoListenerException;
+import org.taverna.server.master.exceptions.NoUpdateException;
+import org.taverna.server.master.interfaces.Listener;
+import org.taverna.server.master.interfaces.TavernaRun;
+import org.taverna.server.master.rest.TavernaServerListenersREST;
+import org.taverna.server.master.utils.CallTimeLogger.PerfLogged;
+import org.taverna.server.master.utils.InvocationCounter.CallCounted;
+
+/**
+ * RESTful interface to a single property of a workflow run.
+ *
+ * @author Donal Fellows
+ */
+class ListenerPropertyREST implements TavernaServerListenersREST.Property,
+ ListenerPropertyBean {
+ private Log log = getLog("Taverna.Server.Webapp");
+ private TavernaServerSupport support;
+ private Listener listen;
+ private String propertyName;
+ private TavernaRun run;
+
+ @Override
+ public void setSupport(TavernaServerSupport support) {
+ this.support = support;
+ }
+
+ @Override
+ public ListenerPropertyREST connect(Listener listen, TavernaRun run,
+ String propertyName) {
+ this.listen = listen;
+ this.propertyName = propertyName;
+ this.run = run;
+ return this;
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public String getValue() {
+ try {
+ return listen.getProperty(propertyName);
+ } catch (NoListenerException e) {
+ log.error("unexpected exception; property \"" + propertyName
+ + "\" should exist", e);
+ return null;
+ }
+ }
+
+ @Override
+ @CallCounted
+ @PerfLogged
+ public String setValue(String value) throws NoUpdateException,
+ NoListenerException {
+ support.permitUpdate(run);
+ listen.setProperty(propertyName, value);
+ return listen.getProperty(propertyName);
+ }
+
+ @Override
+ @CallCounted
+ public Response options() {
+ return opt("PUT");
+ }
+}
\ No newline at end of file