http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/EntryHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/EntryHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/EntryHandler.java
new file mode 100644
index 0000000..20d9f56
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/EntryHandler.java
@@ -0,0 +1,131 @@
+package org.taverna.server.master.rest.handler;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonMap;
+import static javax.ws.rs.core.Response.notAcceptable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Variant;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.parser.Parser;
+import org.apache.abdera.writer.Writer;
+import org.springframework.beans.factory.annotation.Required;
+
+@Provider
+@Produces({ "application/atom+xml", "application/atom+xml;type=entry" })
+@Consumes({ "application/atom+xml", "application/atom+xml;type=entry" })
+public class EntryHandler implements MessageBodyWriter<Entry>,
+               MessageBodyReader<Entry> {
+       private static final String ENC = "UTF-8";
+       private static final MediaType ENTRY = new MediaType("application",
+                       "atom+xml", singletonMap("type", "entry"));
+       private static final Variant VARIANT = new Variant(ENTRY, (String) null,
+                       ENC);
+       private static final Charset UTF8 = Charset.forName(ENC);
+
+       @Required
+       public void setAbdera(Abdera abdera) {
+               parser = abdera.getParser();
+               writer = abdera.getWriterFactory().getWriter("prettyxml");
+       }
+
+       private Parser parser;
+       private Writer writer;
+
+       @Override
+       public boolean isReadable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (!Entry.class.isAssignableFrom(type))
+                       return false;
+               if (!ENTRY.isCompatible(mediaType))
+                       return false;
+               if (mediaType.getParameters().containsKey("type"))
+                       return 
"entry".equalsIgnoreCase(mediaType.getParameters().get(
+                                       "type"));
+               return true;
+       }
+
+       @Override
+       public Entry readFrom(Class<Entry> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+                       throws IOException, WebApplicationException {
+               Charset cs = UTF8;
+               try {
+                       String charset = 
mediaType.getParameters().get("charset");
+                       if (charset != null)
+                               cs = Charset.forName(charset);
+               } catch (IllegalCharsetNameException e) {
+                       throw new 
WebApplicationException(notAcceptable(asList(VARIANT))
+                                       .entity("bad charset name").build());
+               } catch (UnsupportedCharsetException e) {
+                       throw new 
WebApplicationException(notAcceptable(asList(VARIANT))
+                                       .entity("unsupportd charset 
name").build());
+               }
+               try {
+                       Document<Entry> doc = parser.parse(new 
InputStreamReader(
+                                       entityStream, cs));
+                       if 
(!Entry.class.isAssignableFrom(doc.getRoot().getClass())) {
+                               throw new WebApplicationException(
+                                               
notAcceptable(asList(VARIANT)).entity(
+                                                               "not really a 
feed entry").build());
+                       }
+                       return doc.getRoot();
+               } catch (ClassCastException e) {
+                       throw new 
WebApplicationException(notAcceptable(asList(VARIANT))
+                                       .entity("not really a feed 
entry").build());
+
+               }
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (!Entry.class.isAssignableFrom(type))
+                       return false;
+               if (!ENTRY.isCompatible(mediaType))
+                       return false;
+               if (mediaType.getParameters().containsKey("type"))
+                       return 
"entry".equalsIgnoreCase(mediaType.getParameters().get(
+                                       "type"));
+               return true;
+       }
+
+       @Override
+       public long getSize(Entry t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return -1;
+       }
+
+       @Override
+       public void writeTo(Entry t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               httpHeaders.putSingle("Content-Type", ENTRY.toString() + 
";charset="
+                               + ENC);
+               writer.writeTo(t, new OutputStreamWriter(entityStream, UTF8));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FeedHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FeedHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FeedHandler.java
new file mode 100644
index 0000000..7c17f5c
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FeedHandler.java
@@ -0,0 +1,66 @@
+package org.taverna.server.master.rest.handler;
+
+import static java.util.Collections.singletonMap;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.writer.Writer;
+import org.springframework.beans.factory.annotation.Required;
+
+@Provider
+@Produces({ "application/atom+xml", "application/atom+xml;type=feed" })
+public class FeedHandler implements MessageBodyWriter<Feed> {
+       private static final MediaType FEED = new MediaType("application",
+                       "atom+xml", singletonMap("type", "feed"));
+       private static final String ENC = "UTF-8";
+
+       @Required
+       public void setAbdera(Abdera abdera) {
+               writer = abdera.getWriterFactory().getWriter("prettyxml");
+       }
+
+       private Writer writer;
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (!Feed.class.isAssignableFrom(type))
+                       return false;
+               if (!FEED.isCompatible(mediaType))
+                       return false;
+               if (mediaType.getParameters().containsKey("type"))
+                       return "feed".equalsIgnoreCase(mediaType.getParameters()
+                                       .get("type"));
+               return true;
+       }
+
+       @Override
+       public long getSize(Feed t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return -1;
+       }
+
+       @Override
+       public void writeTo(Feed t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               httpHeaders.putSingle("Content-Type", FEED.toString() + 
";charset="
+                               + ENC);
+               writer.writeTo(t, new OutputStreamWriter(entityStream, ENC));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileConcatenationHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileConcatenationHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileConcatenationHandler.java
new file mode 100644
index 0000000..ab18d73
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileConcatenationHandler.java
@@ -0,0 +1,61 @@
+package org.taverna.server.master.rest.handler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.springframework.beans.factory.annotation.Required;
+import org.taverna.server.master.FileConcatenation;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.interfaces.File;
+
+public class FileConcatenationHandler implements
+               MessageBodyWriter<FileConcatenation> {
+       /** How much to pull from the worker in one read. */
+       private int maxChunkSize;
+
+       /**
+        * @param maxChunkSize
+        *            How much to pull from the worker in one read.
+        */
+       @Required
+       public void setMaxChunkSize(int maxChunkSize) {
+               this.maxChunkSize = maxChunkSize;
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return type.isAssignableFrom(FileConcatenation.class);
+       }
+
+       @Override
+       public long getSize(FileConcatenation fc, Class<?> type, Type 
genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return fc.size();
+       }
+
+       @Override
+       public void writeTo(FileConcatenation fc, Class<?> type, Type 
genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException {
+               for (File f : fc)
+                       try {
+                               byte[] buffer;
+                               for (int off = 0; true ; off += buffer.length) {
+                                       buffer = f.getContents(off, 
maxChunkSize);
+                                       if (buffer == null || buffer.length == 
0)
+                                               break;
+                                       entityStream.write(buffer);
+                               }
+                       } catch (FilesystemAccessException e) {
+                               // Ignore/skip to next file
+                       }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileMessageHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileMessageHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileMessageHandler.java
new file mode 100644
index 0000000..0aeb816
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileMessageHandler.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static org.apache.commons.logging.LogFactory.getLog;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.commons.logging.Log;
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.interfaces.File;
+
+/**
+ * How to write out a File object with JAX-RS.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+public class FileMessageHandler implements MessageBodyWriter<File> {
+       private Log log = getLog("Taverna.Server.Webapp");
+       /** How much to pull from the worker in one read. */
+       private int maxChunkSize;
+
+       /**
+        * @param maxChunkSize
+        *            How much to pull from the worker in one read.
+        */
+       public void setMaxChunkSize(int maxChunkSize) {
+               this.maxChunkSize = maxChunkSize;
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return File.class.isAssignableFrom(type);
+       }
+
+       @Override
+       public long getSize(File t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               try {
+                       return t.getSize(); // Is it really raw bytes?
+               } catch (FilesystemAccessException e) {
+                       log.info("failed to get file length", e);
+                       return -1;
+               }
+       }
+
+       @Override
+       public void writeTo(File t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               try {
+                       int off = 0;
+                       while (true) {
+                               byte[] buffer = t.getContents(off, 
maxChunkSize);
+                               if (buffer == null || buffer.length == 0)
+                                       break;
+                               entityStream.write(buffer);
+                               off += buffer.length;
+                       }
+               } catch (FilesystemAccessException e) {
+                       throw new IOException("problem when reading file", e);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileSegmentHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileSegmentHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileSegmentHandler.java
new file mode 100644
index 0000000..f387cf6
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FileSegmentHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static java.lang.Math.min;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.rest.FileSegment;
+
+/**
+ * How to write out a segment of a file with JAX-RS.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+public class FileSegmentHandler implements MessageBodyWriter<FileSegment> {
+       /** How much to pull from the worker in one read. */
+       private int maxChunkSize;
+
+       /**
+        * @param maxChunkSize
+        *            How much to pull from the worker in one read.
+        */
+       public void setMaxChunkSize(int maxChunkSize) {
+               this.maxChunkSize = maxChunkSize;
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return FileSegment.class.isAssignableFrom(type);
+       }
+
+       @Override
+       public long getSize(FileSegment t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return t.to - t.from;
+       }
+
+       @Override
+       public void writeTo(FileSegment t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               try {
+                       int off = t.from;
+                       while (off < t.to) {
+                               byte[] buffer = t.file.getContents(off,
+                                               min(maxChunkSize, t.to - off));
+                               if (buffer == null || buffer.length == 0)
+                                       break;
+                               entityStream.write(buffer);
+                               off += buffer.length;
+                       }
+               } catch (FilesystemAccessException e) {
+                       throw new IOException("problem when reading file", e);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FilesystemAccessHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FilesystemAccessHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FilesystemAccessHandler.java
new file mode 100644
index 0000000..12c137e
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/FilesystemAccessHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+
+@Provider
+public class FilesystemAccessHandler extends HandlerCore implements
+               ExceptionMapper<FilesystemAccessException> {
+       @Override
+       public Response toResponse(FilesystemAccessException exn) {
+               return respond(FORBIDDEN, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/GeneralFailureHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/GeneralFailureHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/GeneralFailureHandler.java
new file mode 100644
index 0000000..775805b
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/GeneralFailureHandler.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.taverna.server.master.exceptions.GeneralFailureException;
+
+public class GeneralFailureHandler extends HandlerCore implements
+               ExceptionMapper<GeneralFailureException> {
+       @Override
+       public Response toResponse(GeneralFailureException exception) {
+               return respond(INTERNAL_SERVER_ERROR, exception);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/HandlerCore.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/HandlerCore.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/HandlerCore.java
new file mode 100644
index 0000000..0e3fb51
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/HandlerCore.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.commons.logging.LogFactory.getLog;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.logging.Log;
+import org.taverna.server.master.api.ManagementModel;
+
+/**
+ * Base class for handlers that grants Spring-enabled access to the management
+ * model.
+ * 
+ * @author Donal Fellows
+ */
+public class HandlerCore {
+       private Log log = getLog("Taverna.Server.Webapp");
+       private ManagementModel managementModel;
+
+       /**
+        * @param managementModel
+        *            the managementModel to set
+        */
+       public void setManagementModel(ManagementModel managementModel) {
+               this.managementModel = managementModel;
+       }
+
+       /**
+        * Simplified interface for building responses.
+        * 
+        * @param status
+        *            What status code to use?
+        * @param exception
+        *            What exception to report on?
+        * @return The build response.
+        */
+       protected Response respond(Response.Status status, Exception exception) 
{
+               if (managementModel.getLogOutgoingExceptions()
+                               || status.getStatusCode() >= 500)
+                       log.info("converting exception to response", exception);
+               return status(status).type(TEXT_PLAIN_TYPE)
+                               .entity(exception.getMessage()).build();
+       }
+
+       /**
+        * Simplified interface for building responses.
+        * 
+        * @param status
+        *            What status code to use?
+        * @param partialMessage
+        *            The prefix to the message.
+        * @param exception
+        *            What exception to report on?
+        * @return The build response.
+        */
+       protected Response respond(Response.Status status, String 
partialMessage,
+                       Exception exception) {
+               if (managementModel.getLogOutgoingExceptions()
+                               || status.getStatusCode() >= 500)
+                       log.info("converting exception to response", exception);
+               return status(status).type(TEXT_PLAIN_TYPE)
+                               .entity(partialMessage + "\n" + 
exception.getMessage()).build();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/IllegalArgumentHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/IllegalArgumentHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/IllegalArgumentHandler.java
new file mode 100644
index 0000000..ac02015
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/IllegalArgumentHandler.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+public class IllegalArgumentHandler extends HandlerCore implements
+               ExceptionMapper<IllegalArgumentException> {
+       @Override
+       public Response toResponse(IllegalArgumentException exn) {
+               return respond(UNSUPPORTED_MEDIA_TYPE, exn);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ImplementationProblemHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ImplementationProblemHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ImplementationProblemHandler.java
new file mode 100644
index 0000000..1458667
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ImplementationProblemHandler.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.taverna.server.localworker.remote.ImplementationException;
+
+public class ImplementationProblemHandler extends HandlerCore implements
+               ExceptionMapper<ImplementationException> {
+       @Override
+       public Response toResponse(ImplementationException exception) {
+               return respond(INTERNAL_SERVER_ERROR, exception);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InputStreamMessageHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InputStreamMessageHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InputStreamMessageHandler.java
new file mode 100644
index 0000000..2c0c092
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InputStreamMessageHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010-2012 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static java.lang.Long.parseLong;
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static org.apache.commons.logging.LogFactory.getLog;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Maps a stream from a client into a bounded ordinary input stream that the
+ * webapp can work with more easily.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+@Consumes(APPLICATION_OCTET_STREAM)
+public class InputStreamMessageHandler implements
+               MessageBodyReader<InputStream> {
+       @Override
+       public boolean isReadable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return InputStream.class.isAssignableFrom(type);
+       }
+
+       @Override
+       public InputStream readFrom(Class<InputStream> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+                       throws IOException, WebApplicationException {
+               return new TransferStream(entityStream,
+                               httpHeaders.get("Content-Length"));
+       }
+}
+
+/**
+ * The actual transfer thunk.
+ * 
+ * @author Donal Fellows
+ */
+class TransferStream extends InputStream {
+       private Log log = getLog("Taverna.Server.Handlers");
+
+       public TransferStream(InputStream entityStream, List<String> 
contentLength) {
+               this.entityStream = new BufferedInputStream(entityStream);
+               if (contentLength != null && contentLength.size() > 0) {
+                       this.limit = parseLong(contentLength.get(0));
+                       if (log.isDebugEnabled())
+                               log.debug("will attempt to transfer " + 
this.limit + " bytes");
+               } else {
+                       this.limit = -1;
+                       if (log.isDebugEnabled())
+                               log.debug("will attempt to transfer until EOF");
+               }
+       }
+
+       InputStream entityStream;
+       long limit;
+       long doneBytes = 0;
+
+       @Override
+       public int read() throws IOException {
+               if (limit >= 0 && doneBytes >= limit)
+                       return -1;
+               int result = entityStream.read();
+               if (result >= 0)
+                       doneBytes++;
+               return result;
+       }
+
+       @Override
+       public int read(byte[] ary, int off, int len) throws IOException {
+               if (limit >= 0) {
+                       if (doneBytes >= limit)
+                               return -1;
+                       if (doneBytes + len > limit)
+                               len = (int) (limit - doneBytes);
+               }
+               int readBytes = entityStream.read(ary, off, len);
+               if (readBytes >= 0)
+                       doneBytes += readBytes;
+               return readBytes;
+       }
+
+       @Override
+       public void close() throws IOException {
+               entityStream.close();
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InvalidCredentialHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InvalidCredentialHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InvalidCredentialHandler.java
new file mode 100644
index 0000000..fe11de8
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/InvalidCredentialHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.InvalidCredentialException;
+
+@Provider
+public class InvalidCredentialHandler extends HandlerCore implements
+               ExceptionMapper<InvalidCredentialException> {
+       @Override
+       public Response toResponse(InvalidCredentialException exn) {
+               return respond(BAD_REQUEST, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/JAXBExceptionHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/JAXBExceptionHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/JAXBExceptionHandler.java
new file mode 100644
index 0000000..33ac6a0
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/JAXBExceptionHandler.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.JAXBException;
+
+@Provider
+public class JAXBExceptionHandler extends HandlerCore implements
+               ExceptionMapper<JAXBException> {
+       @Override
+       public Response toResponse(JAXBException exn) {
+               return respond(BAD_REQUEST, "APIEpicFail: " + 
exn.getErrorCode(), exn);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NegotiationFailedHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NegotiationFailedHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NegotiationFailedHandler.java
new file mode 100644
index 0000000..47153e7
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NegotiationFailedHandler.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+import static javax.ws.rs.core.Response.notAcceptable;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import 
org.taverna.server.master.rest.TavernaServerDirectoryREST.NegotiationFailedException;
+
+@Provider
+public class NegotiationFailedHandler implements
+               ExceptionMapper<NegotiationFailedException> {
+       @Override
+       public Response toResponse(NegotiationFailedException exn) {
+               return notAcceptable(exn.accepted).type(TEXT_PLAIN)
+                               .entity(exn.getMessage()).build();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCreateHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCreateHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCreateHandler.java
new file mode 100644
index 0000000..e4215a1
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCreateHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.NoCreateException;
+
+@Provider
+public class NoCreateHandler extends HandlerCore implements
+               ExceptionMapper<NoCreateException> {
+       @Override
+       public Response toResponse(NoCreateException exn) {
+               return respond(SERVICE_UNAVAILABLE, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCredentialHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCredentialHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCredentialHandler.java
new file mode 100644
index 0000000..d81f6ba
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoCredentialHandler.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.taverna.server.master.exceptions.NoCredentialException;
+
+public class NoCredentialHandler extends HandlerCore implements
+               ExceptionMapper<NoCredentialException> {
+       @Override
+       public Response toResponse(NoCredentialException exn) {
+               return respond(NOT_FOUND, exn);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDestroyHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDestroyHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDestroyHandler.java
new file mode 100644
index 0000000..927af4b
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDestroyHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.NoDestroyException;
+
+@Provider
+public class NoDestroyHandler extends HandlerCore implements
+               ExceptionMapper<NoDestroyException> {
+       @Override
+       public Response toResponse(NoDestroyException exn) {
+               return respond(FORBIDDEN, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDirectoryEntryHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDirectoryEntryHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDirectoryEntryHandler.java
new file mode 100644
index 0000000..ab2e54d
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoDirectoryEntryHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.NoDirectoryEntryException;
+
+@Provider
+public class NoDirectoryEntryHandler extends HandlerCore implements
+               ExceptionMapper<NoDirectoryEntryException> {
+       @Override
+       public Response toResponse(NoDirectoryEntryException exn) {
+               return respond(NOT_FOUND, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoListenerHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoListenerHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoListenerHandler.java
new file mode 100644
index 0000000..36e3053
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoListenerHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.NoListenerException;
+
+@Provider
+public class NoListenerHandler extends HandlerCore implements
+               ExceptionMapper<NoListenerException> {
+       @Override
+       public Response toResponse(NoListenerException exn) {
+               return respond(BAD_REQUEST, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoUpdateHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoUpdateHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoUpdateHandler.java
new file mode 100644
index 0000000..61a89d4
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NoUpdateHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.NoUpdateException;
+
+@Provider
+public class NoUpdateHandler extends HandlerCore implements
+               ExceptionMapper<NoUpdateException> {
+       @Override
+       public Response toResponse(NoUpdateException exn) {
+               return respond(FORBIDDEN, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NotOwnerHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NotOwnerHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NotOwnerHandler.java
new file mode 100644
index 0000000..44de871
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/NotOwnerHandler.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+import org.taverna.server.master.exceptions.NotOwnerException;
+
+public class NotOwnerHandler extends HandlerCore implements
+               ExceptionMapper<NotOwnerException> {
+       @Override
+       public Response toResponse(NotOwnerException exn) {
+               return respond(FORBIDDEN, exn);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/OverloadedHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/OverloadedHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/OverloadedHandler.java
new file mode 100644
index 0000000..21e5e68
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/OverloadedHandler.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2013 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.OverloadedException;
+
+@Provider
+public class OverloadedHandler extends HandlerCore implements
+               ExceptionMapper<OverloadedException> {
+       @Override
+       public Response toResponse(OverloadedException exn) {
+               return respond(SERVICE_UNAVAILABLE, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/PermissionHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/PermissionHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/PermissionHandler.java
new file mode 100644
index 0000000..03a4dd4
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/PermissionHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.taverna.server.master.common.Permission;
+
+/**
+ * Handler that allows CXF to send and receive {@linkplain Permission
+ * permissions} as plain text directly.
+ * 
+ * @author Donal Fellows
+ */
+public class PermissionHandler implements MessageBodyReader<Permission>,
+               MessageBodyWriter<Permission> {
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return type.isAssignableFrom(Permission.class)
+                               && mediaType.isCompatible(TEXT_PLAIN_TYPE);
+       }
+
+       @Override
+       public long getSize(Permission t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return t.toString().length();
+       }
+
+       @Override
+       public void writeTo(Permission t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               new OutputStreamWriter(entityStream).write(t.toString());
+       }
+
+       @Override
+       public boolean isReadable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return type.isAssignableFrom(Permission.class)
+                               && mediaType.isCompatible(TEXT_PLAIN_TYPE);
+       }
+
+       @Override
+       public Permission readFrom(Class<Permission> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+                       throws IOException, WebApplicationException {
+               char[] cbuf = new char[7];
+               int len = new InputStreamReader(entityStream).read(cbuf);
+               if (len < 0)
+                       throw new IllegalArgumentException("no entity 
supplied");
+               return Permission.valueOf(new String(cbuf, 0, len));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java
new file mode 100644
index 0000000..edeac63
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/Scufl2DocumentHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE.txt" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.common.Workflow;
+
+import uk.org.taverna.scufl2.api.io.ReaderException;
+import uk.org.taverna.scufl2.api.io.WorkflowBundleIO;
+import uk.org.taverna.scufl2.api.io.WriterException;
+
+/**
+ * Handler that allows a .scufl2 document to be read from and written to a REST
+ * message directly.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+public class Scufl2DocumentHandler implements MessageBodyReader<Workflow>,
+               MessageBodyWriter<Workflow> {
+       private static final MediaType SCUFL2_TYPE = new 
MediaType("application",
+                       "vnd.taverna.scufl2.workflow-bundle");
+       public static final String SCUFL2 = 
"application/vnd.taverna.scufl2.workflow-bundle";
+       private WorkflowBundleIO io = new WorkflowBundleIO();
+
+       @Override
+       public boolean isReadable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (type.isAssignableFrom(Workflow.class))
+                       return mediaType.isCompatible(SCUFL2_TYPE);
+               return false;
+       }
+
+       @Override
+       public Workflow readFrom(Class<Workflow> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+                       throws IOException, WebApplicationException {
+               try {
+                       return new Workflow(io.readBundle(entityStream, 
SCUFL2));
+               } catch (ReaderException e) {
+                       throw new WebApplicationException(e, 403);
+               }
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (Workflow.class.isAssignableFrom(type))
+                       return mediaType.isCompatible(SCUFL2_TYPE);
+               return false;
+       }
+
+       @Override
+       public long getSize(Workflow workflow, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return -1;
+       }
+
+       @Override
+       public void writeTo(Workflow workflow, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               try {
+                       io.writeBundle(workflow.getScufl2Workflow(), 
entityStream, SCUFL2);
+               } catch (WriterException e) {
+                       throw new WebApplicationException(e);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
new file mode 100644
index 0000000..4227d80
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/T2FlowDocumentHandler.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.taverna.server.master.common.Workflow;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * Handler that allows a .t2flow document to be read from and written to a REST
+ * message directly.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+public class T2FlowDocumentHandler implements MessageBodyReader<Workflow>,
+               MessageBodyWriter<Workflow> {
+       private static final MediaType T2FLOW_TYPE = new 
MediaType("application",
+                       "vnd.taverna.t2flow+xml");
+       public static final String T2FLOW = 
"application/vnd.taverna.t2flow+xml";
+       public static final String T2FLOW_ROOTNAME = "workflow";
+       public static final String T2FLOW_NS = 
"http://taverna.sf.net/2008/xml/t2flow";;
+       private DocumentBuilderFactory db;
+       private TransformerFactory transformer;
+
+       public T2FlowDocumentHandler() throws ParserConfigurationException,
+                       TransformerConfigurationException {
+               db = DocumentBuilderFactory.newInstance();
+               db.setNamespaceAware(true);
+               transformer = TransformerFactory.newInstance();
+       }
+
+       @Override
+       public boolean isReadable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (type.isAssignableFrom(Workflow.class))
+                       return mediaType.isCompatible(T2FLOW_TYPE);
+               return false;
+       }
+
+       @Override
+       public Workflow readFrom(Class<Workflow> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+                       throws IOException, WebApplicationException {
+               Document doc;
+               try {
+                       doc = db.newDocumentBuilder().parse(entityStream);
+               } catch (SAXException e) {
+                       throw new WebApplicationException(e, 403);
+               } catch (ParserConfigurationException e) {
+                       throw new WebApplicationException(e);
+               }
+               Workflow workflow = new Workflow(doc.getDocumentElement());
+               if (doc.getDocumentElement().getNamespaceURI().equals(T2FLOW_NS)
+                               && doc.getDocumentElement().getNodeName()
+                                               .equals(T2FLOW_ROOTNAME))
+                       return workflow;
+               throw new WebApplicationException(Response.status(403)
+                               .entity("invalid T2flow document; bad root 
element")
+                               .type("text/plain").build());
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               if (Workflow.class.isAssignableFrom(type))
+                       return mediaType.isCompatible(T2FLOW_TYPE);
+               return false;
+       }
+
+       @Override
+       public long getSize(Workflow workflow, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return -1;
+       }
+
+       @Override
+       public void writeTo(Workflow workflow, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               try {
+                       transformer.newTransformer().transform(
+                                       new 
DOMSource(workflow.getT2flowWorkflow()),
+                                       new StreamResult(entityStream));
+               } catch (TransformerException e) {
+                       if (e.getCause() != null && e.getCause() instanceof 
IOException)
+                               throw (IOException) e.getCause();
+                       throw new WebApplicationException(e);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
new file mode 100644
index 0000000..a90a229
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/URIListHandler.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.status;
+import static org.taverna.server.master.rest.handler.URIListHandler.URI_LIST;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Deserialization and serialization engine for the <tt>{@value #URI_LIST}</tt>
+ * content type.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+@Consumes(URI_LIST)
+public class URIListHandler implements MessageBodyReader<List<URI>>,
+               MessageBodyWriter<List<URI>> {
+       /** The content type we handle. */
+       public static final String URI_LIST = "text/uri-list";
+       private static final MediaType URILIST = new MediaType("text", 
"uri-list");
+
+       @Override
+       public boolean isReadable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return type.isAssignableFrom(ArrayList.class)
+                               && genericType instanceof ParameterizedType
+                               && ((Class<?>) ((ParameterizedType) genericType)
+                                               .getActualTypeArguments()[0])
+                                               .isAssignableFrom(URI.class)
+                               && URILIST.isCompatible(mediaType);
+       }
+
+       @Override
+       public List<URI> readFrom(Class<List<URI>> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, String> httpHeaders, InputStream 
entityStream)
+                       throws IOException, WebApplicationException {
+               String enc = mediaType.getParameters().get("encoding");
+               Charset c = (enc == null) ? Charset.defaultCharset() : Charset
+                               .forName(enc);
+               BufferedReader br = new BufferedReader(new InputStreamReader(
+                               entityStream, c));
+               ArrayList<URI> uris = new ArrayList<>();
+               String line;
+               while ((line = br.readLine()) != null) {
+                       if (line.startsWith("#"))
+                               continue;
+                       try {
+                               uris.add(new URI(line));
+                       } catch (URISyntaxException e) {
+                               throw new WebApplicationException(e, 
status(422).entity(
+                                               "ill-formed URI").build());
+                       }
+               }
+               return uris;
+       }
+
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return List.class.isAssignableFrom(type)
+                               && genericType instanceof ParameterizedType
+                               && ((ParameterizedType) 
genericType).getActualTypeArguments()[0] == URI.class
+                               && URILIST.isCompatible(mediaType);
+       }
+
+       @Override
+       public long getSize(List<URI> list, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return -1;
+       }
+
+       private static final String PREFERRED_ENCODING = "UTF-8";
+
+       @Override
+       public void writeTo(List<URI> list, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException {
+               String encoding = mediaType.getParameters().get("encoding");
+               if (encoding == null) {
+                       encoding = PREFERRED_ENCODING;
+                       httpHeaders.putSingle("Content-Type", URI_LIST + 
";encoding="
+                                       + encoding);
+               }
+               BufferedWriter w = new BufferedWriter(new OutputStreamWriter(
+                               entityStream, encoding));
+               for (URI uri : list) {
+                       w.write(uri.toString());
+                       w.newLine();
+               }
+               w.flush();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/UnknownRunHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/UnknownRunHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/UnknownRunHandler.java
new file mode 100644
index 0000000..4542237
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/UnknownRunHandler.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010-2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.exceptions.UnknownRunException;
+
+@Provider
+public class UnknownRunHandler extends HandlerCore implements
+               ExceptionMapper<UnknownRunException> {
+       @Override
+       public Response toResponse(UnknownRunException exn) {
+               return respond(NOT_FOUND, exn);
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ZipStreamHandler.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ZipStreamHandler.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ZipStreamHandler.java
new file mode 100644
index 0000000..2241220
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/ZipStreamHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.rest.handler;
+
+import static org.apache.commons.io.IOUtils.copy;
+import static org.taverna.server.master.api.ContentTypes.APPLICATION_ZIP_TYPE;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+import org.taverna.server.master.interfaces.Directory.ZipStream;
+
+/**
+ * How to write a ZIP file as the result entity of a request.
+ * 
+ * @author Donal Fellows
+ */
+@Provider
+@Produces("application/zip")
+public class ZipStreamHandler implements MessageBodyWriter<ZipStream> {
+       @Override
+       public boolean isWriteable(Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return ZipStream.class.isAssignableFrom(type)
+                               && mediaType.equals(APPLICATION_ZIP_TYPE);
+       }
+
+       @Override
+       public long getSize(ZipStream t, Class<?> type, Type genericType,
+                       Annotation[] annotations, MediaType mediaType) {
+               return -1;
+       }
+
+       @Override
+       public void writeTo(ZipStream zipStream, Class<?> type, Type 
genericType,
+                       Annotation[] annotations, MediaType mediaType,
+                       MultivaluedMap<String, Object> httpHeaders,
+                       OutputStream entityStream) throws IOException,
+                       WebApplicationException {
+               copy(zipStream, entityStream);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/package-info.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/package-info.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/package-info.java
new file mode 100644
index 0000000..e72af22
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/handler/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+/**
+ * This package contains type handlers for the RESTful interface to Taverna 
Server.
+ * @author Donal Fellows
+ */
+@XmlSchema(namespace = SERVER_REST, 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 = "port", namespaceURI = DATA),
+               @XmlNs(prefix = "feed", namespaceURI = FEED),
+               @XmlNs(prefix = "admin", namespaceURI = ADMIN) })
+package org.taverna.server.master.rest.handler;
+
+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.XLINK;
+import static org.taverna.server.port_description.Namespaces.DATA;
+
+import javax.xml.bind.annotation.XmlNs;
+import javax.xml.bind.annotation.XmlSchema;
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/package-info.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/package-info.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/package-info.java
new file mode 100644
index 0000000..0a0b069
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/rest/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+/**
+ * This package contains the RESTful interface to Taverna Server.
+ * @author Donal Fellows
+ */
+@XmlSchema(namespace = SERVER_REST, 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 = "port", namespaceURI = DATA),
+               @XmlNs(prefix = "feed", namespaceURI = FEED),
+               @XmlNs(prefix = "admin", namespaceURI = ADMIN) })
+package org.taverna.server.master.rest;
+
+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.XLINK;
+import static org.taverna.server.port_description.Namespaces.DATA;
+
+import javax.xml.bind.annotation.XmlNs;
+import javax.xml.bind.annotation.XmlSchema;
+

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/DirEntry.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/DirEntry.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/DirEntry.java
new file mode 100644
index 0000000..bb8d73f
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/DirEntry.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.soap;
+
+import static org.taverna.server.master.common.Namespaces.XLINK;
+
+import java.net.URI;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlType;
+
+import org.taverna.server.master.common.DirEntryReference;
+
+/**
+ * A more Taverna-friendly version of the directory entry descriptor classes.
+ * 
+ * @author Donal Fellows
+ */
+@XmlType(name = "DirectoryEntry")
+@XmlRootElement(name = "entry")
+@XmlSeeAlso({ DirEntry.File.class, DirEntry.Directory.class })
+public class DirEntry {
+       /** A link to the entry. Ignored on input. */
+       @XmlAttribute(name = "href", namespace = XLINK)
+       @XmlSchemaType(name = "anyURI")
+       public URI link;
+       @XmlAttribute
+       public String name;
+       @XmlElement(required = true)
+       public String path;
+
+       /**
+        * A file in a directory.
+        * 
+        * @author Donal Fellows
+        */
+       @XmlType(name = "FileDirEntry")
+       @XmlRootElement(name = "file")
+       public static class File extends DirEntry {
+       }
+
+       /**
+        * A directory in a directory. That is, a sub-directory.
+        * 
+        * @author Donal Fellows
+        */
+       @XmlType(name = "DirectoryDirEntry")
+       @XmlRootElement(name = "dir")
+       public static class Directory extends DirEntry {
+       }
+
+       /**
+        * Converts from the "common" format to the subclasses of this class.
+        * 
+        * @param deref
+        *            The "common" format handle to convert.
+        * @return The converted handle
+        */
+       public static DirEntry convert(DirEntryReference deref) {
+               DirEntry result;
+               if (deref instanceof DirEntryReference.DirectoryReference)
+                       result = new Directory();
+               else if (deref instanceof DirEntryReference.FileReference)
+                       result = new File();
+               else
+                       result = new DirEntry();
+               result.link = deref.link;
+               result.name = deref.name;
+               result.path = deref.path;
+               return result;
+       }
+
+       /**
+        * Converts to the "common" format from the subclasses of this class.
+        * 
+        * @param deref
+        *            The subclass of this class to convert.
+        * @return The converted reference.
+        */
+       public static DirEntryReference convert(DirEntry de) {
+               DirEntryReference result;
+               if (de instanceof Directory)
+                       result = new DirEntryReference.DirectoryReference();
+               else if (de instanceof File)
+                       result = new DirEntryReference.FileReference();
+               else
+                       result = new DirEntryReference();
+               result.link = de.link;
+               result.name = de.name;
+               result.path = de.path;
+               return result;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/FileContents.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/FileContents.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/FileContents.java
new file mode 100644
index 0000000..7ebc991
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/FileContents.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.soap;
+
+import static java.lang.Math.min;
+import static java.lang.System.arraycopy;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.activation.DataHandler;
+import javax.activation.DataSource;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlMimeType;
+import javax.xml.bind.annotation.XmlType;
+
+import org.taverna.server.master.exceptions.FilesystemAccessException;
+import org.taverna.server.master.interfaces.File;
+
+/**
+ * An MTOM-capable description of how to transfer the contents of a file.
+ * 
+ * @author Donal Fellows
+ */
+@XmlType(name = "FileContents")
+public class FileContents {
+       @XmlElement
+       public String name;
+       @XmlMimeType("application/octet-stream") // JAXB bug: must be this
+       public DataHandler fileData;
+
+       /**
+        * Initialize the contents of this descriptor from the given file and
+        * content type.
+        * 
+        * @param file
+        *            The file that is to be reported.
+        * @param contentType
+        *            The estimated content type of the file.
+        */
+       public void setFile(File file, String contentType) {
+               name = file.getFullName();
+               fileData = new DataHandler(new TavernaFileSource(file, 
contentType));
+       }
+
+       /**
+        * Write the content described by this class to the specified file.
+        * @param file The file to write to; must already exist.
+        * @throws IOException
+        * @throws FilesystemAccessException
+        */
+       public void writeToFile(File file) throws IOException,
+                       FilesystemAccessException {
+               try (InputStream is = fileData.getInputStream()) {
+                       byte[] buf = new byte[65536];
+                       file.setContents(new byte[0]);
+                       while (true) {
+                               int len = is.read(buf);
+                               if (len <= 0)
+                                       return;
+                               if (len == buf.length)
+                                       file.appendContents(buf);
+                               else {
+                                       byte[] shortbuf = new byte[len];
+                                       arraycopy(buf, 0, shortbuf, 0, len);
+                                       file.appendContents(shortbuf);
+                               }
+                       }
+               }
+       }
+}
+
+/**
+ * A data source that knows how to communicate with the Taverna Server 
back-end.
+ * 
+ * @author Donal Fellows
+ */
+class TavernaFileSource implements DataSource {
+       TavernaFileSource(File f, String type) {
+               this.f = f;
+               this.type = type;
+       }
+
+       private final File f;
+       private final String type;
+
+       @Override
+       public String getContentType() {
+               return type;
+       }
+
+       @Override
+       public String getName() {
+               return f.getName();
+       }
+
+       @Override
+       public InputStream getInputStream() throws IOException {
+               final File f = this.f;
+               return new InputStream() {
+                       private int idx;
+
+                       @Override
+                       public int read(byte[] b, int off, int len) throws 
IOException {
+                               byte[] r;
+                               try {
+                                       r = f.getContents(idx, len);
+                               } catch (FilesystemAccessException e) {
+                                       throw new IOException(e);
+                               }
+                               if (r == null)
+                                       return -1;
+                               len = min(len, r.length);
+                               arraycopy(r, 0, b, off, len);
+                               idx += len;
+                               return len;
+                       }
+
+                       @Override
+                       public int read() throws IOException {
+                               byte[] r;
+                               try {
+                                       r = f.getContents(idx, 1);
+                               } catch (FilesystemAccessException e) {
+                                       throw new IOException(e);
+                               }
+                               if (r == null)
+                                       return -1;
+                               idx++;
+                               return r[0];
+                       }
+               };
+       }
+
+       @Override
+       public OutputStream getOutputStream() throws IOException {
+               final File f = this.f;
+               return new OutputStream() {
+                       private boolean append = false;
+
+                       @Override
+                       public void write(int b) throws IOException {
+                               write(new byte[] { (byte) b });
+                       }
+
+                       @Override
+                       public void write(byte[] b) throws IOException {
+                               try {
+                                       if (append)
+                                               f.appendContents(b);
+                                       else
+                                               f.setContents(b);
+                                       append = true;
+                               } catch (FilesystemAccessException e) {
+                                       throw new IOException(e);
+                               }
+                       }
+
+                       @Override
+                       public void write(byte[] b, int off, int len) throws 
IOException {
+                               byte[] ary = new byte[len];
+                               arraycopy(b, off, ary, 0, len);
+                               write(ary);
+                       }
+               };
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-server/blob/2c71f9a9/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/PermissionList.java
----------------------------------------------------------------------
diff --git 
a/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/PermissionList.java
 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/PermissionList.java
new file mode 100644
index 0000000..3ab6d0c
--- /dev/null
+++ 
b/taverna-server-webapp/src/main/java/org/taverna/server/master/soap/PermissionList.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 The University of Manchester
+ * 
+ * See the file "LICENSE" for license terms.
+ */
+package org.taverna.server.master.soap;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.taverna.server.master.common.Permission;
+
+/**
+ * The list of permissions to access a workflow run of users <i>other than the
+ * owner</i>. This class exists to support the JAXB mapping.
+ * 
+ * @author Donal Fellows
+ */
+@XmlType(name = "PermissionList")
+@XmlRootElement(name = "permissionList")
+public class PermissionList {
+       /**
+        * The type of a single mapped permission. This class exists to support 
the
+        * JAXB mapping.
+        * 
+        * @author Donal Fellows
+        */
+       @XmlType(name = "")
+       public static class SinglePermissionMapping {
+               public SinglePermissionMapping() {
+               }
+
+               public SinglePermissionMapping(String user, Permission 
permission) {
+                       this.userName = user;
+                       this.permission = permission;
+               }
+
+               /** The name of the user that this talks about. */
+               public String userName;
+               /** The permission level that the user is granted. */
+               public Permission permission;
+       }
+
+       /** The list of (non-default) permissions granted. */
+       @XmlElement
+       public List<SinglePermissionMapping> permission;
+}
\ No newline at end of file

Reply via email to