http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
new file mode 100644
index 0000000..346d920
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
@@ -0,0 +1,345 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.taverna.activities.interaction.atom.AtomUtils;
+import org.apache.taverna.activities.interaction.jetty.InteractionJetty;
+import 
org.apache.taverna.activities.interaction.preference.InteractionPreference;
+import org.apache.taverna.activities.interaction.velocity.InteractionVelocity;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.i18n.text.Normalizer;
+import org.apache.abdera.i18n.text.Sanitizer;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.parser.stax.FOMElement;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+
+public final class InteractionActivityRunnable implements Runnable {
+
+       private static final Logger logger = Logger
+                       .getLogger(InteractionActivityRunnable.class);
+
+       private static final Abdera ABDERA = Abdera.getInstance();
+
+       private final Template presentationTemplate;
+
+       private final InteractionRequestor requestor;
+
+       private CredentialManager credentialManager;
+
+       private InteractionRecorder interactionRecorder;
+       
+       private InteractionUtils interactionUtils;
+
+       private InteractionJetty interactionJetty;
+
+       private InteractionPreference interactionPreference;
+
+       private ResponseFeedListener responseFeedListener;
+
+       private InteractionVelocity interactionVelocity;
+
+       public InteractionActivityRunnable(final InteractionRequestor requestor,
+                       final Template presentationTemplate,
+                       final CredentialManager credentialManager,
+                       final InteractionRecorder interactionRecorder,
+                       final InteractionUtils interactionUtils,
+                       final InteractionJetty interactionJetty,
+                       final InteractionPreference interactionPreference,
+                       final ResponseFeedListener responseFeedListener,
+                       final InteractionVelocity interactionVelocity) {
+               this.requestor = requestor;
+               this.presentationTemplate = presentationTemplate;
+               this.credentialManager = credentialManager;
+               this.interactionRecorder = interactionRecorder;
+               this.interactionUtils = interactionUtils;
+               this.interactionJetty = interactionJetty;
+               this.interactionPreference = interactionPreference;
+               this.responseFeedListener = responseFeedListener;
+               this.interactionVelocity = interactionVelocity;
+       }
+
+       @Override
+       public void run() {
+               /*
+                * InvocationContext context = callback.getContext();
+                */
+               final String runId = 
InteractionUtils.getUsedRunId(this.requestor
+                               .getRunId());
+
+               final String id = 
Sanitizer.sanitize(UUID.randomUUID().toString(), "",
+                               true, Normalizer.Form.D);
+
+               final Map<String, Object> inputData = 
this.requestor.getInputData();
+
+               if (interactionPreference.getUseJetty()) {
+                       
interactionJetty.startJettyIfNecessary(credentialManager);
+               }
+               interactionJetty.startListenersIfNecessary();
+               try {
+                       interactionUtils.copyFixedFile("pmrpc.js");
+                       interactionUtils.copyFixedFile("interaction.css");
+               } catch (final IOException e1) {
+                       logger.error(e1);
+                       this.requestor.fail("Unable to copy necessary fixed 
file");
+                       return;
+               }
+               synchronized (ABDERA) {
+                       final Entry interactionNotificationMessage = this
+                                       .createBasicInteractionMessage(id, 
runId);
+
+                       for (final String key : inputData.keySet()) {
+                               final Object value = inputData.get(key);
+                               if (value instanceof byte[]) {
+                                       final String replacementUrl = 
interactionPreference
+                                                       
.getPublicationUrlString(id, key);
+                                       final ByteArrayInputStream bais = new 
ByteArrayInputStream(
+                                                       (byte[]) value);
+                                       try {
+                                               
interactionUtils.publishFile(replacementUrl, bais,
+                                                               runId, id);
+                                               bais.close();
+                                               inputData.put(key, 
replacementUrl);
+                                       } catch (final IOException e) {
+                                               logger.error(e);
+                                               this.requestor.fail("Unable to 
publish to " + replacementUrl);
+                                               return;
+                                       }
+                               }
+                       }
+
+                       final String inputDataString = 
this.createInputDataJson(inputData);
+                       if (inputDataString == null) {
+                               return;
+                       }
+                       final String inputDataUrl = interactionPreference
+                                       .getInputDataUrlString(id);
+                       try {
+                               interactionUtils.publishFile(inputDataUrl, 
inputDataString,
+                                               runId, id);
+                       } catch (final IOException e) {
+                               logger.error(e);
+                               this.requestor.fail("Unable to publish to " + 
inputDataUrl);
+                               return;
+                       }
+
+                       String outputDataUrl = null;
+
+                       if (!this.requestor.getInteractionType().equals(
+                                       InteractionType.Notification)) {
+                               outputDataUrl = interactionPreference
+                                               .getOutputDataUrlString(id);
+                       }
+                       final String interactionUrlString = 
this.generateHtml(inputDataUrl,
+                                       outputDataUrl, inputData, runId, id);
+
+                       try {
+                               this.postInteractionMessage(id, 
interactionNotificationMessage,
+                                               interactionUrlString, runId);
+                       } catch (IOException e) {
+                               logger.error(e);
+                               this.requestor.fail("Unable to post message");
+                               return;
+                       }
+                       if (!this.requestor.getInteractionType().equals(
+                                       InteractionType.Notification)) {
+                               responseFeedListener.registerInteraction(
+                                               interactionNotificationMessage, 
this.requestor);
+                       } else {
+                               this.requestor.carryOn();
+
+                       }
+               }
+       }
+
+       private String createInputDataJson(final Map<String, Object> inputData) 
{
+               try {
+                       return InteractionUtils.objectToJson(inputData);
+               } catch (final IOException e) {
+                       logger.error(e);
+                       this.requestor.fail("Unable to generate JSON");
+               }
+               return null;
+       }
+
+       private Entry createBasicInteractionMessage(final String id,
+                       final String runId) {
+               final Entry interactionNotificationMessage = ABDERA.newEntry();
+
+               interactionNotificationMessage.setId(id);
+               final Date timestamp = new Date();
+               interactionNotificationMessage.setPublished(timestamp);
+               interactionNotificationMessage.setUpdated(timestamp);
+
+               interactionNotificationMessage.addAuthor("Taverna");
+               interactionNotificationMessage.setTitle("Interaction from 
Taverna for "
+                               + this.requestor.generateId());
+
+               final Element runIdElement = interactionNotificationMessage
+                               .addExtension(AtomUtils.getRunIdQName());
+               runIdElement.setText(StringEscapeUtils.escapeJavaScript(runId));
+               
+               final Element pathIdElement = 
interactionNotificationMessage.addExtension(AtomUtils.getPathIdQName());
+               
pathIdElement.setText(StringEscapeUtils.escapeJavaScript(this.requestor.getPath()));
+               
+               final Element countElement = 
interactionNotificationMessage.addExtension(AtomUtils.getCountQName());
+               
countElement.setText(StringEscapeUtils.escapeJavaScript(this.requestor.getInvocationCount().toString()));
+               
+               if (this.requestor.getInteractionType().equals(
+                               InteractionType.Notification)) {
+                       interactionNotificationMessage.addExtension(AtomUtils
+                                       .getProgressQName());
+               }
+               final Element idElement = interactionNotificationMessage
+                               .addExtension(AtomUtils.getIdQName());
+               idElement.setText(id);
+
+               return interactionNotificationMessage;
+       }
+
+       private void postInteractionMessage(final String id, final Entry entry,
+                       final String interactionUrlString, final String runId) 
throws IOException {
+
+               entry.addLink(StringEscapeUtils.escapeXml(interactionUrlString),
+                               "presentation");
+               entry.setContentAsXhtml("<p><a href=\""
+                               + 
StringEscapeUtils.escapeXml(interactionUrlString)
+                               + "\">Open: "
+                               + 
StringEscapeUtils.escapeXml(interactionUrlString)
+                               + "</a></p>");
+
+               URL feedUrl;
+
+                       feedUrl = new URL(interactionPreference
+                                       .getFeedUrlString());
+                       final String entryContent = ((FOMElement) entry)
+                                       .toFormattedString();
+                       final HttpURLConnection httpCon = (HttpURLConnection) 
feedUrl
+                                       .openConnection();
+                       httpCon.setDoOutput(true);
+                       httpCon.setRequestProperty("Content-Type",
+                                       
"application/atom+xml;type=entry;charset=UTF-8");
+                       httpCon.setRequestProperty("Content-Length",
+                                       "" + entryContent.length());
+                       httpCon.setRequestProperty("Slug", id);
+                       httpCon.setRequestMethod("POST");
+                       httpCon.setConnectTimeout(5000);
+                       final OutputStream outputStream = 
httpCon.getOutputStream();
+                       IOUtils.write(entryContent, outputStream, "UTF-8");
+                       outputStream.close();
+                       final int response = httpCon.getResponseCode();
+                       if ((response < 0) || (response >= 400)) {
+                               logger.error("Received response code" + 
response);
+                               throw (new IOException ("Received response code 
" + response));
+                       }
+                       if (response == HttpURLConnection.HTTP_CREATED) {
+                               interactionRecorder.addResource(runId, id,
+                                               
httpCon.getHeaderField("Location"));
+                       }
+       }
+
+       String generateHtml(final String inputDataUrl, final String 
outputDataUrl,
+                       final Map<String, Object> inputData, final String runId,
+                       final String id) {
+
+               final VelocityContext velocityContext = new VelocityContext();
+
+               for (final String inputName : inputData.keySet()) {
+                       final Object input = inputData.get(inputName);
+                       velocityContext.put(inputName, input);
+               }
+
+               velocityContext.put("feed", interactionPreference
+                               .getFeedUrlString());
+               velocityContext.put("runId", runId);
+               velocityContext.put("entryId", id);
+               final String pmrpcUrl = interactionPreference
+                               .getLocationUrl() + "/pmrpc.js";
+               velocityContext.put("pmrpcUrl", pmrpcUrl);
+               velocityContext.put("inputDataUrl", inputDataUrl);
+               velocityContext.put("outputDataUrl", outputDataUrl);
+               final String interactionUrl = interactionPreference
+                               .getInteractionUrlString(id);
+
+               velocityContext.put("interactionUrl", interactionUrl);
+
+               String presentationUrl = "";
+               final String authorizeUrl = "";
+               try {
+                       if (this.requestor.getPresentationType().equals(
+                                       
InteractionActivityType.VelocityTemplate)) {
+
+                               presentationUrl = interactionPreference
+                                               .getPresentationUrlString(id);
+
+                               final String presentationString = 
this.processTemplate(
+                                               this.presentationTemplate, 
velocityContext);
+                               interactionUtils.publishFile(presentationUrl,
+                                               presentationString, runId, id);
+
+                       } else if (this.requestor.getPresentationType().equals(
+                                       
InteractionActivityType.LocallyPresentedHtml)) {
+                               presentationUrl = 
this.requestor.getPresentationOrigin();
+                       }
+
+                       velocityContext.put("presentationUrl", presentationUrl);
+
+                       final String interactionString = this.processTemplate(
+                                       
interactionVelocity.getInteractionTemplate(),
+                                       velocityContext);
+                       interactionUtils.publishFile(interactionUrl, 
interactionString,
+                                       runId, id);
+
+                       if (!authorizeUrl.isEmpty()) {
+                               return authorizeUrl;
+                       }
+                       return interactionUrl;
+               } catch (final IOException e) {
+                       logger.error(e);
+                       this.requestor.fail("Unable to generate HTML");
+                       return null;
+               }
+       }
+
+       private String processTemplate(final Template template,
+                       final VelocityContext context) throws IOException {
+               final StringWriter resultWriter = new StringWriter();
+               template.merge(context, resultWriter);
+               resultWriter.close();
+               return resultWriter.toString();
+       }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
new file mode 100644
index 0000000..11422b3
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
@@ -0,0 +1,31 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+/**
+ * @author alanrw
+ * 
+ *         Should be renamed something like presentation type
+ */
+public enum InteractionActivityType {
+
+       VelocityTemplate, LocallyPresentedHtml
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
new file mode 100644
index 0000000..3c8c83b
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
@@ -0,0 +1,209 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.reference.ReferenceService;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.reference.WorkflowRunIdEntity;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+import 
net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivityCallback;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class InteractionCallbackRequestor implements InteractionRequestor {
+
+       private final AsynchronousActivityCallback callback;
+
+       private final Map<String, T2Reference> inputs;
+
+       private final InteractionActivity activity;
+
+       private boolean answered = false;
+
+       private String path;
+
+       private Integer count;
+       
+       private static Map<String, Integer> invocationCount = new 
HashMap<String, Integer> ();
+
+       public InteractionCallbackRequestor(final InteractionActivity activity,
+                       final AsynchronousActivityCallback callback,
+                       final Map<String, T2Reference> inputs) {
+               this.activity = activity;
+               this.callback = callback;
+               this.inputs = inputs;
+               this.path = calculatePath();
+               this.count = calculateInvocationCount(path);
+       }
+
+       @Override
+       public String getRunId() {
+               return this.callback.getContext()
+                               .getEntities(WorkflowRunIdEntity.class).get(0)
+                               .getWorkflowRunId();
+       }
+
+       @Override
+       public Map<String, Object> getInputData() {
+               final Map<String, Object> inputData = new HashMap<String, 
Object>();
+
+               final InvocationContext context = this.callback.getContext();
+               final ReferenceService referenceService = 
context.getReferenceService();
+               for (final String inputName : this.inputs.keySet()) {
+                       final Object input = 
referenceService.renderIdentifier(this.inputs
+                                       .get(inputName), 
this.getInputPort(inputName)
+                                       .getTranslatedElementClass(), 
this.callback.getContext());
+                       inputData.put(inputName, input);
+               }
+               return inputData;
+       }
+
+       public ActivityInputPort getInputPort(final String name) {
+               for (final ActivityInputPort port : 
this.activity.getInputPorts()) {
+                       if (port.getName().equals(name)) {
+                               return port;
+                       }
+               }
+               return null;
+       }
+
+       @Override
+       public void fail(final String string) {
+               if (this.answered) {
+                       return;
+               }
+               this.callback.fail(string);
+               this.answered = true;
+       }
+
+       @Override
+       public void carryOn() {
+               if (this.answered) {
+                       return;
+               }
+               this.callback.receiveResult(new HashMap<String, T2Reference>(),
+                               new int[0]);
+               this.answered = true;
+       }
+
+       @Override
+       public String generateId() {
+               final String workflowRunId = getRunId();
+               final String parentProcessIdentifier = this.callback
+                               .getParentProcessIdentifier();
+               return (workflowRunId + ":" + parentProcessIdentifier);
+       }
+
+       @Override
+       public InteractionType getInteractionType() {
+               if (this.activity.isProgressNotification()) {
+                       return InteractionType.Notification;
+               }
+               return InteractionType.DataRequest;
+       }
+
+       @Override
+       public InteractionActivityType getPresentationType() {
+               return this.activity.getInteractionActivityType();
+       }
+
+       @Override
+       public String getPresentationOrigin() {
+               return this.activity.getPresentationOrigin();
+       }
+
+       @Override
+       public void receiveResult(final Map<String, Object> resultMap) {
+               if (this.answered) {
+                       return;
+               }
+               final Map<String, T2Reference> outputs = new HashMap<String, 
T2Reference>();
+
+               final InvocationContext context = this.callback.getContext();
+               final ReferenceService referenceService = 
context.getReferenceService();
+
+               for (final Object key : resultMap.keySet()) {
+                       final String keyString = (String) key;
+                       final Object value = resultMap.get(key);
+                       final Integer depth = this.findPortDepth(keyString);
+                       if (depth == null) {
+                               this.callback.fail("Data sent for unknown port 
: " + keyString);
+                       }
+                       outputs.put(keyString,
+                                       referenceService.register(value, depth, 
true, context));
+               }
+               this.callback.receiveResult(outputs, new int[0]);
+               this.answered = true;
+       }
+
+       private Integer findPortDepth(final String portName) {
+               final Set<ActivityOutputPort> ports = 
this.activity.getOutputPorts();
+               for (final ActivityOutputPort op : ports) {
+                       if (op.getName().equals(portName)) {
+                               return op.getDepth();
+                       }
+               }
+               return null;
+       }
+
+       private String calculatePath() {
+               final String parentProcessIdentifier = this.callback
+                               .getParentProcessIdentifier();
+               String result = "";
+               String parts[] = parentProcessIdentifier.split(":");
+
+               for (int i = 2; i < parts.length; i += 4) {
+                       if (!result.isEmpty()) {
+                               result += ":";
+                       }
+                       result += parts[i];
+               }
+               return result;
+       }
+
+       @Override
+       public String getPath() {
+               return this.path;
+       }
+       
+       private synchronized static Integer calculateInvocationCount(String 
path) {
+               Integer currentCount = invocationCount.get(path);
+               if (currentCount == null) {
+                       currentCount = Integer.valueOf(0);
+               } else {
+                       currentCount = currentCount + 1;
+               }
+               invocationCount.put(path, currentCount);
+               return currentCount;
+       }
+
+       @Override
+       public Integer getInvocationCount() {
+               return count;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
new file mode 100644
index 0000000..140d209
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
@@ -0,0 +1,179 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * 
+ * This class is used to remember and forget interactions and their associated
+ * ATOM entries and files
+ * 
+ * @author alanrw
+ * 
+ */
+public class InteractionRecorder {
+
+       private static final Logger logger = Logger
+                       .getLogger(InteractionRecorder.class);
+
+       static Map<String, Map<String, Set<String>>> runToInteractionMap = 
Collections
+                       .synchronizedMap(new HashMap<String, Map<String, 
Set<String>>>());
+       
+       private InteractionUtils interactionUtils;
+
+       private InteractionRecorder() {
+               super();
+       }
+
+       public void deleteRun(final String runToDelete) {
+               final Set<String> interactionIds = new HashSet<String>(
+                               getInteractionMap(runToDelete).keySet());
+               for (final String interactionId : interactionIds) {
+                       deleteInteraction(runToDelete, interactionId);
+               }
+               runToInteractionMap.remove(runToDelete);
+       }
+
+       public void deleteInteraction(final String runId,
+                       final String interactionId) {
+               for (final String urlString : getResourceSet(runId, 
interactionId)) {
+                       try {
+                               deleteUrl(urlString);
+                       } catch (final IOException e) {
+                               logger.info("Unable to delete " + urlString, e);
+                       }
+
+               }
+               getInteractionMap(runId).remove(interactionId);
+       }
+
+       private void deleteUrl(final String urlString) throws IOException {
+               logger.info("Deleting resource " + urlString);
+               final URL url = new URL(urlString);
+               final HttpURLConnection httpCon = (HttpURLConnection) url
+                               .openConnection();
+               httpCon.setRequestMethod("DELETE");
+               final int response = httpCon.getResponseCode();
+               if (response >= 400) {
+                       logger.info("Received response code" + response);
+               }
+       }
+
+       public void addResource(final String runId,
+                       final String interactionId, final String resourceId) {
+               if (resourceId == null) {
+                       logger.error("Attempt to add null resource",
+                                       new NullPointerException(""));
+                       return;
+               }
+               logger.info("Adding resource " + resourceId);
+               final Set<String> resourceSet = getResourceSet(runId, 
interactionId);
+
+               resourceSet.add(resourceId);
+       }
+
+       private Set<String> getResourceSet(final String runId,
+                       final String interactionId) {
+               final Map<String, Set<String>> interactionMap = 
getInteractionMap(runId);
+               Set<String> resourceSet = interactionMap.get(interactionId);
+               if (resourceSet == null) {
+                       resourceSet = Collections.synchronizedSet(new 
HashSet<String>());
+                       interactionMap.put(interactionId, resourceSet);
+               }
+               return resourceSet;
+       }
+
+       private Map<String, Set<String>> getInteractionMap(final String runId) {
+               Map<String, Set<String>> interactionMap = 
InteractionRecorder.runToInteractionMap
+                               .get(runId);
+               if (interactionMap == null) {
+                       interactionMap = Collections.synchronizedMap(Collections
+                                       .synchronizedMap(new HashMap<String, 
Set<String>>()));
+                       InteractionRecorder.runToInteractionMap.put(runId, 
interactionMap);
+               }
+               return interactionMap;
+       }
+
+       public void persist() {
+               final File outputFile = getUsageFile();
+               try {
+                       FileUtils.writeStringToFile(outputFile, InteractionUtils
+                                       
.objectToJson(InteractionRecorder.runToInteractionMap));
+               } catch (final IOException e) {
+                       logger.error(e);
+               }
+       }
+
+       private File getUsageFile() {
+               return new 
File(getInteractionUtils().getInteractionServiceDirectory(),
+                               "usage");
+       }
+
+       public void load() {
+               final File inputFile = getUsageFile();
+               try {
+                       final String usageString = 
FileUtils.readFileToString(inputFile);
+                       final ObjectMapper mapper = new ObjectMapper();
+                       @SuppressWarnings("unchecked")
+                       final Map<String, Object> rootAsMap = 
mapper.readValue(usageString,
+                                       Map.class);
+                       InteractionRecorder.runToInteractionMap.clear();
+                       for (final String runId : rootAsMap.keySet()) {
+                               @SuppressWarnings("unchecked")
+                               final Map<String, Object> runMap = (Map<String, 
Object>) rootAsMap
+                                               .get(runId);
+                               for (final String interactionId : 
runMap.keySet()) {
+                                       @SuppressWarnings("unchecked")
+                                       final List<String> urlList = 
(List<String>) runMap
+                                                       .get(interactionId);
+                                       for (final String url : urlList) {
+                                               addResource(runId, 
interactionId, url);
+                                       }
+                               }
+                       }
+               } catch (final IOException e) {
+                       logger.info(e);
+               }
+       }
+
+       public InteractionUtils getInteractionUtils() {
+               return interactionUtils;
+       }
+
+       public void setInteractionUtils(InteractionUtils interactionUtils) {
+               this.interactionUtils = interactionUtils;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
new file mode 100644
index 0000000..7826a59
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
@@ -0,0 +1,54 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.util.Map;
+
+/**
+ * @author alanrw
+ * 
+ */
+public interface InteractionRequestor {
+
+       String getRunId();
+
+       Map<String, Object> getInputData();
+
+       void fail(String string);
+
+       void carryOn();
+
+       String generateId();
+       
+       // The path to whatever requested the interaction
+       String getPath();
+       
+       // The number of times whatever requested the interaction has requested 
one
+       Integer getInvocationCount();
+
+       InteractionActivityType getPresentationType();
+
+       InteractionType getInteractionType();
+
+       String getPresentationOrigin();
+
+       void receiveResult(Map<String, Object> resultMap);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
new file mode 100644
index 0000000..07d8db0
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
@@ -0,0 +1,47 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import net.sf.taverna.t2.workflowmodel.RunDeletionListener;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class InteractionRunDeletionListener implements RunDeletionListener {
+       
+       private InteractionRecorder interactionRecorder;
+
+       @SuppressWarnings("unused")
+       private static final Logger logger = Logger
+                       .getLogger(InteractionRunDeletionListener.class);
+
+       @Override
+       public void deleteRun(final String runToDelete) {
+               interactionRecorder.deleteRun(runToDelete);
+       }
+
+       public void setInteractionRecorder(InteractionRecorder 
interactionRecorder) {
+               this.interactionRecorder = interactionRecorder;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
new file mode 100644
index 0000000..c5549e2
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
@@ -0,0 +1,30 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+/**
+ * @author alanrw
+ * 
+ */
+public enum InteractionType {
+
+       DataRequest, Notification, SecurityRequest, AuthenticationRequest
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
new file mode 100644
index 0000000..7165e76
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
@@ -0,0 +1,143 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+// import net.sf.taverna.raven.appconfig.ApplicationRuntime;
+import 
org.apache.taverna.activities.interaction.preference.InteractionPreference;
+
+import org.apache.commons.io.IOUtils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class InteractionUtils {
+
+       static final Set<String> publishedUrls = Collections
+                       .synchronizedSet(new HashSet<String>());
+       
+       private ApplicationConfiguration appConfig;
+       
+       private InteractionRecorder interactionRecorder;
+
+       private InteractionPreference interactionPreference;
+
+       private InteractionUtils() {
+               super();
+       }
+
+       protected void copyFixedFile(final String fixedFileName)
+                       throws IOException {
+               final String targetUrl = interactionPreference
+                               .getLocationUrl() + "/" + fixedFileName;
+               this.publishFile(
+                               targetUrl,
+                               
InteractionActivity.class.getResourceAsStream("/"
+                                               + fixedFileName), null, null);
+       }
+
+       public void publishFile(final String urlString,
+                       final String contents, final String runId,
+                       final String interactionId) throws IOException {
+               final ByteArrayInputStream byteArrayInputStream = new 
ByteArrayInputStream(
+                               contents.getBytes("UTF-8"));
+               this.publishFile(urlString, byteArrayInputStream, runId,
+                               interactionId);
+       }
+
+       void publishFile(final String urlString, final InputStream is,
+                       final String runId, final String interactionId) throws 
IOException {
+               if (InteractionUtils.publishedUrls.contains(urlString)) {
+                       return;
+               }
+               InteractionUtils.publishedUrls.add(urlString);
+               if (runId != null) {
+                       interactionRecorder.addResource(runId, interactionId, 
urlString);
+               }
+
+               final URL url = new URL(urlString);
+               final HttpURLConnection httpCon = (HttpURLConnection) url
+                               .openConnection();
+               httpCon.setDoOutput(true);
+               httpCon.setRequestMethod("PUT");
+               final OutputStream outputStream = httpCon.getOutputStream();
+               IOUtils.copy(is, outputStream);
+               is.close();
+               outputStream.close();
+               int code = httpCon.getResponseCode();
+               if ((code >= 400) || (code < 0)){
+                       throw new IOException ("Received code " + code);
+               }
+       }
+
+       public static String getUsedRunId(final String engineRunId) {
+               String runId = engineRunId;
+               final String specifiedId = System.getProperty("taverna.runid");
+               if (specifiedId != null) {
+                       runId = specifiedId;
+               }
+               return runId;
+       }
+
+       public File getInteractionServiceDirectory() {
+               final File workingDir = appConfig
+                               .getApplicationHomeDir();
+               final File interactionServiceDirectory = new File(workingDir,
+                               "interactionService");
+               interactionServiceDirectory.mkdirs();
+               return interactionServiceDirectory;
+       }
+
+       public static String objectToJson(final Object o) throws IOException {
+               final ObjectMapper mapper = new ObjectMapper();
+               final StringWriter sw = new StringWriter();
+               mapper.writeValue(sw, o);
+               final String theString = sw.toString();
+               return theString;
+       }
+
+       public void setAppConfig(ApplicationConfiguration appConfig) {
+               this.appConfig = appConfig;
+       }
+
+       public void setInteractionRecorder(InteractionRecorder 
interactionRecorder) {
+               this.interactionRecorder = interactionRecorder;
+       }
+
+       public void setInteractionPreference(InteractionPreference 
interactionPreference) {
+               this.interactionPreference = interactionPreference;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
new file mode 100644
index 0000000..9afd253
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
@@ -0,0 +1,175 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.taverna.activities.interaction.atom.AtomUtils;
+import 
org.apache.taverna.activities.interaction.preference.InteractionPreference;
+
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author alanrw
+ * 
+ */
+public final class ResponseFeedListener extends FeedReader {
+       
+       private InteractionRecorder interactionRecorder;
+
+       private InteractionPreference interactionPreference;
+
+       private static final String STATUS_OK = "OK";
+
+       private static final String DATA_READ_FAILED = "Data read failed";
+
+       private static ResponseFeedListener instance;
+
+       private static final Logger logger = 
Logger.getLogger(ResponseFeedListener.class);
+
+       private static final Map<String, InteractionRequestor> requestorMap = 
new HashMap<String, InteractionRequestor>();
+
+       private ResponseFeedListener() {
+               super("ResponseFeedListener");
+       }
+       
+       @Override
+       protected void considerEntry(final Entry entry) {
+               synchronized (requestorMap) {
+                       final String refString = getReplyTo(entry);
+                       if (refString == null) {
+                               return;
+                       }
+                       final String runId = getRunId(entry);
+
+                       final String entryUrl = interactionPreference
+                                       .getFeedUrlString() + "/" + 
entry.getId().toASCIIString();
+                       interactionRecorder.addResource(runId, refString, 
entryUrl);
+
+                       if (requestorMap.containsKey(refString)) {
+
+                               final InteractionRequestor requestor = 
requestorMap
+                                               .get(refString);
+
+                               final Element statusElement = 
entry.getExtension(AtomUtils
+                                               .getResultStatusQName());
+                               final String statusContent = 
statusElement.getText().trim();
+                               if (!statusContent.equals(STATUS_OK)) {
+                                       cleanup(refString);
+                                       requestor.fail(statusContent);
+                                       return;
+                               }
+                               final String outputDataUrl = 
interactionPreference
+                                               
.getOutputDataUrlString(refString);
+                               // Note that this may not really exist
+                               interactionRecorder
+                                               .addResource(runId, refString, 
outputDataUrl);
+                               String content = null;
+                               InputStream iStream;
+                               try {
+                                       iStream = new 
URL(outputDataUrl).openStream();
+                                       content = IOUtils.toString(iStream);
+                                       iStream.close();
+                               } catch (final MalformedURLException e1) {
+                                       logger.error(e1);
+                                       requestor.fail(DATA_READ_FAILED);
+                                       return;
+                               } catch (final IOException e1) {
+                                       logger.error(e1);
+                                       requestor.fail(DATA_READ_FAILED);
+                                       return;
+                               }
+
+                               try {
+                                       final ObjectMapper mapper = new 
ObjectMapper();
+                                       @SuppressWarnings("unchecked")
+                                       final Map<String, Object> rootAsMap = 
mapper.readValue(
+                                                       content, Map.class);
+                                       requestor.receiveResult(rootAsMap);
+                                       cleanup(refString);
+                                       
interactionRecorder.deleteInteraction(runId, refString);
+
+                               } catch (final JsonParseException e) {
+                                       logger.error(e);
+                               } catch (final IOException e) {
+                                       logger.error(e);
+                               } catch (final Exception e) {
+                                       logger.error(e);
+                               }
+
+                       }
+               }
+       }
+
+       private static void cleanup(final String refString) {
+               requestorMap.remove(refString);
+       }
+
+       private static String getReplyTo(final Entry entry) {
+               final Element replyTo = entry.getFirstChild(AtomUtils
+                               .getInReplyToQName());
+               if (replyTo == null) {
+                       return null;
+               }
+               return replyTo.getText();
+       }
+
+       private static String getRunId(final Entry entry) {
+               final Element runIdElement = entry.getFirstChild(AtomUtils
+                               .getRunIdQName());
+               if (runIdElement == null) {
+                       return null;
+               }
+               return runIdElement.getText();
+       }
+
+       public void registerInteraction(final Entry entry,
+                       final InteractionRequestor requestor) {
+               synchronized (requestorMap) {
+                       final String refString = entry.getId().toString();
+                       requestorMap.put(refString, requestor);
+               }
+       }
+
+       public void setInteractionRecorder(InteractionRecorder 
interactionRecorder) {
+               this.interactionRecorder = interactionRecorder;
+       }
+
+       public void setInteractionPreference(InteractionPreference 
interactionPreference) {
+               this.interactionPreference = interactionPreference;
+       }
+
+       @Override
+       protected InteractionPreference getInteractionPreference() {
+               return this.interactionPreference;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
new file mode 100644
index 0000000..1b8fcf2
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
@@ -0,0 +1,99 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.atom;
+
+import javax.xml.namespace.QName;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class AtomUtils {
+
+       private static QName inputDataQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, 
"input-data",
+                       "interaction");
+       private static QName resultDataQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, 
"result-data",
+                       "interaction");
+       private static QName resultStatusQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, 
"result-status",
+                       "interaction");
+       private static QName idQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, "id", 
"interaction");
+       private static QName pathIdQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, "path",
+                       "interaction");
+       private static QName countQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, "count",
+                       "interaction");
+       private static QName runIdQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, "run-id",
+                       "interaction");
+       private static QName inReplyToQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, 
"in-reply-to",
+                       "interaction");
+       private static QName progressQName = new QName(
+                       "http://ns.taverna.org.uk/2012/interaction";, "progress",
+                       "interaction");
+
+       public static QName getInputDataQName() {
+               return inputDataQName;
+       }
+
+       public static QName getIdQName() {
+               return idQName;
+       }
+
+       public static QName getInReplyToQName() {
+               return inReplyToQName;
+       }
+
+       public static QName getResultDataQName() {
+               return resultDataQName;
+       }
+
+       public static QName getResultStatusQName() {
+               return resultStatusQName;
+       }
+
+       /**
+        * @return the runIdQName
+        */
+       public static QName getRunIdQName() {
+               return runIdQName;
+       }
+
+       /**
+        * @return the progressQName
+        */
+       public static QName getProgressQName() {
+               return progressQName;
+       }
+
+       public static QName getPathIdQName() {
+               return pathIdQName;
+       }
+
+       public static QName getCountQName() {
+               return countQName;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
new file mode 100644
index 0000000..3f2295d
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
@@ -0,0 +1,81 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.feed;
+
+import java.awt.Desktop;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.apache.taverna.activities.interaction.FeedReader;
+import 
org.apache.taverna.activities.interaction.preference.InteractionPreference;
+
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Link;
+import org.apache.log4j.Logger;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class ShowRequestFeedListener extends FeedReader {
+       
+       private static ShowRequestFeedListener instance;
+
+       private static Logger logger = Logger
+                       .getLogger(ShowRequestFeedListener.class);
+       
+       private static final String ignore_requests_property = 
System.getProperty("taverna.interaction.ignore_requests");
+
+       private static boolean operational = (ignore_requests_property == null) 
|| !Boolean.valueOf(ignore_requests_property);
+
+       private InteractionPreference interactionPreference;
+       
+       private ShowRequestFeedListener() {
+               super("ShowRequestFeedListener");
+       }
+       
+                       @Override
+                       protected void considerEntry(final Entry entry) {
+                               if (!operational) {
+                                       return;
+                               }
+                               final Link presentationLink = 
entry.getLink("presentation");
+                               if (presentationLink != null) {
+                                       try {
+                                               Desktop.getDesktop().browse(
+                                                               
presentationLink.getHref().toURI());
+                                       } catch (final IOException e) {
+                                               logger.error("Cannot open 
presentation");
+                                       } catch (final URISyntaxException e) {
+                                               logger.error("Cannot open 
presentation");
+                                       }
+                               }
+                       }
+
+                       @Override
+                       protected InteractionPreference 
getInteractionPreference() {
+                               return this.interactionPreference;
+                       }
+
+                       public void 
setInteractionPreference(InteractionPreference interactionPreference) {
+                               this.interactionPreference = 
interactionPreference;
+                       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
new file mode 100644
index 0000000..0729b23
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
@@ -0,0 +1,264 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.jetty;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.i18n.templates.Template;
+import org.apache.abdera.i18n.text.Normalizer;
+import org.apache.abdera.i18n.text.Sanitizer;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.model.Link;
+import org.apache.abdera.protocol.server.ProviderHelper;
+import org.apache.abdera.protocol.server.RequestContext;
+import org.apache.abdera.protocol.server.ResponseContext;
+import org.apache.abdera.protocol.server.Target;
+import org.apache.abdera.protocol.server.provider.managed.FeedConfiguration;
+import 
org.apache.abdera.protocol.server.provider.managed.ManagedCollectionAdapter;
+
+/**
+ * Simple Filesystem Adapter that uses a local directory to store Atompub
+ * collection entries. As an extension of the ManagedCollectionAdapter class,
+ * the Adapter is intended to be used with implementations of the
+ * ManagedProvider and are configured using /abdera/adapter/*.properties files.
+ * The *.properties file MUST specify the fs.root property to specify the root
+ * directory used by the Adapter.
+ */
+public class HackedFilesystemAdapter extends ManagedCollectionAdapter {
+       
+       private InteractionJetty interactionJetty;
+
+       private final File root;
+       private final static FileSorter sorter = new FileSorter();
+       private final static Template paging_template = new Template(
+                       "?{-join|&|count,page}");
+
+       public HackedFilesystemAdapter(final Abdera abdera,
+                       final FeedConfiguration config) {
+               super(abdera, config);
+               this.root = this.getRoot();
+       }
+
+       private File getRoot() {
+               return interactionJetty.getFeedDirectory();
+       }
+
+       private Entry getEntry(final File entryFile) {
+               if (!entryFile.exists() || !entryFile.isFile()) {
+                       throw new RuntimeException();
+               }
+               try {
+                       final FileInputStream fis = new 
FileInputStream(entryFile);
+                       final Document<Entry> doc = 
this.abdera.getParser().parse(fis);
+                       final Entry entry = doc.getRoot();
+                       return entry;
+               } catch (final Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       private void addPagingLinks(final RequestContext request, final Feed 
feed,
+                       final int currentpage, final int count) {
+               final Map<String, Object> params = new HashMap<String, 
Object>();
+               params.put("count", count);
+               params.put("page", currentpage + 1);
+               String next = paging_template.expand(params);
+               next = request.getResolvedUri().resolve(next).toString();
+               feed.addLink(next, "next");
+               if (currentpage > 0) {
+                       params.put("page", currentpage - 1);
+                       String prev = paging_template.expand(params);
+                       prev = 
request.getResolvedUri().resolve(prev).toString();
+                       feed.addLink(prev, "previous");
+               }
+               params.put("page", 0);
+               String current = paging_template.expand(params);
+               current = request.getResolvedUri().resolve(current).toString();
+               feed.addLink(current, "current");
+       }
+
+       private void getEntries(final RequestContext request, final Feed feed,
+                       final File root) {
+               final File[] files = root.listFiles();
+               Arrays.sort(files, sorter);
+               final int length = ProviderHelper.getPageSize(request, "count", 
25);
+               final int offset = ProviderHelper.getOffset(request, "page", 
length);
+               final String _page = request.getParameter("page");
+               final int page = (_page != null) ? Integer.parseInt(_page) : 0;
+               this.addPagingLinks(request, feed, page, length);
+               if (offset > files.length) {
+                       return;
+               }
+               for (int n = offset; (n < (offset + length)) && (n < 
files.length); n++) {
+                       final File file = files[n];
+                       try {
+                               final Entry entry = this.getEntry(file);
+                               feed.addEntry((Entry) entry.clone());
+                       } catch (final Exception e) {
+                               // Do nothing
+                       }
+               }
+       }
+
+       @Override
+       public ResponseContext getFeed(final RequestContext request) {
+               final Feed feed = this.abdera.newFeed();
+               feed.setId(this.config.getServerConfiguration().getServerUri() 
+ "/"
+                               + this.config.getFeedId());
+               feed.setTitle(this.config.getFeedTitle());
+               feed.addAuthor(this.config.getFeedAuthor());
+               feed.addLink(this.config.getFeedUri());
+               feed.addLink(this.config.getFeedUri(), "self");
+               feed.setUpdated(new Date());
+               this.getEntries(request, feed, this.root);
+               return ProviderHelper.returnBase(feed.getDocument(), 200, null);
+       }
+
+       @Override
+       public ResponseContext deleteEntry(final RequestContext request) {
+               final Target target = request.getTarget();
+               final String key = target.getParameter("entry");
+               final File file = this.getFile(key, false);
+               if (file.exists()) {
+                       file.delete();
+               }
+               return ProviderHelper.nocontent();
+       }
+
+       @Override
+       public ResponseContext getEntry(final RequestContext request) {
+               final Target target = request.getTarget();
+               final String key = target.getParameter("entry");
+               final File file = this.getFile(key, false);
+               final Entry entry = this.getEntry(file);
+               if (entry != null) {
+                       return ProviderHelper.returnBase(entry.getDocument(), 
200, null);
+               } else {
+                       return ProviderHelper.notfound(request);
+               }
+       }
+
+       @Override
+       public ResponseContext postEntry(final RequestContext request) {
+               if (request.isAtom()) {
+                       try {
+                               final Entry entry = (Entry) 
request.getDocument().getRoot()
+                                               .clone();
+                               final String key = this.createKey(request);
+                               this.setEditDetail(request, entry, key);
+                               final File file = this.getFile(key);
+                               final FileOutputStream out = new 
FileOutputStream(file);
+                               entry.writeTo(out);
+                               final String edit = 
entry.getEditLinkResolvedHref().toString();
+                               return ProviderHelper
+                                               
.returnBase(entry.getDocument(), 201, null)
+                                               .setLocation(edit);
+                       } catch (final Exception e) {
+                               return ProviderHelper.badrequest(request);
+                       }
+               } else {
+                       return ProviderHelper.notsupported(request);
+               }
+       }
+
+       private void setEditDetail(final RequestContext request, final Entry 
entry,
+                       final String key) throws IOException {
+               final Target target = request.getTarget();
+               final String feed = target.getParameter("feed");
+               final String id = key;
+               entry.setEdited(new Date());
+               final Link link = entry.getEditLink();
+               final Map<String, Object> params = new HashMap<String, 
Object>();
+               params.put("feed", feed);
+               params.put("entry", id);
+               final String href = request.absoluteUrlFor("entry", params);
+               if (link == null) {
+                       entry.addLink(href, "edit");
+               } else {
+                       link.setHref(href);
+               }
+       }
+
+       private File getFile(final String key) {
+               return this.getFile(key, true);
+       }
+
+       private File getFile(final String key, final boolean post) {
+               final File file = new File(this.root, key);
+               if (post && file.exists()) {
+                       throw new RuntimeException("File exists");
+               }
+               return file;
+       }
+
+       private String createKey(final RequestContext request) throws 
IOException {
+               String slug = request.getSlug();
+               if (slug == null) {
+                       slug = ((Entry) 
request.getDocument().getRoot()).getTitle();
+               }
+               return Sanitizer.sanitize(slug, "", true, Normalizer.Form.D);
+       }
+
+       @Override
+       public ResponseContext putEntry(final RequestContext request) {
+               if (request.isAtom()) {
+                       try {
+                               final Entry entry = (Entry) 
request.getDocument().getRoot()
+                                               .clone();
+                               final String key = 
request.getTarget().getParameter("entry");
+                               this.setEditDetail(request, entry, key);
+                               final File file = this.getFile(key, false);
+                               final FileOutputStream out = new 
FileOutputStream(file);
+                               entry.writeTo(out);
+                               final String edit = 
entry.getEditLinkResolvedHref().toString();
+                               return ProviderHelper
+                                               
.returnBase(entry.getDocument(), 200, null)
+                                               .setLocation(edit);
+                       } catch (final Exception e) {
+                               return ProviderHelper.badrequest(request);
+                       }
+               } else {
+                       return ProviderHelper.notsupported(request);
+               }
+       }
+
+       private static class FileSorter implements Comparator<File> {
+               @Override
+               public int compare(final File o1, final File o2) {
+                       return o1.lastModified() > o2.lastModified() ? -1 : o1
+                                       .lastModified() < o2.lastModified() ? 1 
: 0;
+               }
+       }
+
+       public void setInteractionJetty(InteractionJetty interactionJetty) {
+               this.interactionJetty = interactionJetty;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
new file mode 100644
index 0000000..c5827ac
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
@@ -0,0 +1,219 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.jetty;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.taverna.activities.interaction.FeedReader;
+import org.apache.taverna.activities.interaction.InteractionUtils;
+import org.apache.taverna.activities.interaction.ResponseFeedListener;
+import org.apache.taverna.activities.interaction.feed.ShowRequestFeedListener;
+import 
org.apache.taverna.activities.interaction.preference.InteractionPreference;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
+//import net.sf.taverna.t2.spi.SPIRegistry;
+import net.sf.webdav.WebdavServlet;
+
+import org.apache.abdera.protocol.server.ServiceManager;
+import org.apache.abdera.protocol.server.provider.basic.BasicProvider;
+import org.apache.abdera.protocol.server.servlet.AbderaServlet;
+import org.apache.log4j.Logger;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.HandlerList;
+import org.mortbay.jetty.security.Constraint;
+import org.mortbay.jetty.security.ConstraintMapping;
+import org.mortbay.jetty.security.HashUserRealm;
+import org.mortbay.jetty.security.SecurityHandler;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class InteractionJetty {
+
+       private static Logger logger = Logger.getLogger(InteractionJetty.class);
+       
+       private InteractionUtils interactionUtils;
+       
+       private ShowRequestFeedListener showRequestFeedListener;
+       private ResponseFeedListener responseFeedListener;
+
+       private InteractionPreference interactionPreference;
+
+       private static Server server;
+
+       private static String REALM_NAME = "TavernaInteraction";
+       
+       private static boolean listenersStarted = false;
+
+       public synchronized void startJettyIfNecessary(CredentialManager 
credentialManager) {
+               if (server != null) {
+                       return;
+               }
+               
+//             final ClassLoader previousContextClassLoader = 
Thread.currentThread()
+//                             .getContextClassLoader();
+//             Thread.currentThread().setContextClassLoader(
+//                             InteractionJetty.class.getClassLoader());
+
+               final String port = interactionPreference.getPort();
+
+               server = new Server(Integer.parseInt(port));
+               server.setStopAtShutdown(true);
+
+               final WebdavServlet interactionServlet = new WebdavServlet();
+
+               final ServletHolder interactionHolder = new ServletHolder();
+               interactionHolder.setServlet(interactionServlet);
+
+               try {
+
+                       interactionHolder.setInitParameter("rootpath",
+                                       
getInteractionDirectory().getCanonicalPath());
+               } catch (final IOException e1) {
+                       logger.error("Unable to set root of interaction", e1);
+               }
+
+               final HandlerList handlers = new HandlerList();
+               final Context overallContext = new Context(handlers, "/",
+                               Context.SESSIONS);
+               overallContext.setContextPath("/");
+               server.setHandler(overallContext);
+
+               final AbderaServlet abderaServlet = new AbderaServlet();
+               final ServletHolder abderaHolder = new 
ServletHolder(abderaServlet);
+               abderaHolder.setInitParameter(ServiceManager.PROVIDER,
+                               BasicProvider.class.getName());
+
+               overallContext.addServlet(abderaHolder, "/*");
+               overallContext.addServlet(interactionHolder, "/interaction/*");
+
+               if (interactionPreference.getUseUsername()) {
+                       final Constraint constraint = new Constraint();
+                       constraint.setName(Constraint.__BASIC_AUTH);
+
+                       constraint.setRoles(new String[] { "user", "admin", 
"moderator" });
+                       constraint.setAuthenticate(true);
+
+                       final ConstraintMapping cm = new ConstraintMapping();
+                       cm.setConstraint(constraint);
+                       cm.setPathSpec("/*");
+
+                       final SecurityHandler sh = new SecurityHandler();
+                       try {
+                               final HashUserRealm realm = new 
HashUserRealm(REALM_NAME);
+                               final URI serviceURI = createServiceURI(port);
+                               final UsernamePassword up = credentialManager
+                                               
.getUsernameAndPasswordForService(serviceURI, true,
+                                                               "Please specify 
the username and password to secure your interactions");
+                               if (up != null) {
+                                       final String username = 
up.getUsername();
+                                       realm.put(username, 
up.getPasswordAsString());
+                                       realm.addUserToRole(username, "user");
+                               }
+                               sh.setUserRealm(realm);
+                       } catch (final CMException e) {
+                               logger.error(e);
+                       } catch (final URISyntaxException e) {
+                               logger.error(e);
+                       }
+                       sh.setConstraintMappings(new ConstraintMapping[] { cm 
});
+                       overallContext.addHandler(sh);
+
+               }
+
+               getFeedDirectory();
+
+               try {
+                       server.start();
+                       while (!server.isRunning()) {
+                               Thread.sleep(5000);
+                       }
+               } catch (final Exception e) {
+                       logger.error("Unable to start Jetty");
+               }
+//             Thread.currentThread()
+//                             
.setContextClassLoader(previousContextClassLoader);
+       }
+
+       public static URI createServiceURI(final String port)
+                       throws URISyntaxException {
+               return new URI("http://localhost:"; + port + "/#" + REALM_NAME);
+       }
+
+       public File getJettySubdirectory(final String subdirectoryName) {
+               final File workingDir = interactionUtils
+                               .getInteractionServiceDirectory();
+               final File subDir = new File(workingDir, "jetty/" + 
subdirectoryName);
+               subDir.mkdirs();
+               return subDir;
+       }
+
+       public File getFeedDirectory() {
+               return getJettySubdirectory("feed");
+       }
+
+       public File getInteractionDirectory() {
+               return getJettySubdirectory("interaction");
+       }
+       
+       public synchronized void startListenersIfNecessary() {
+               if (listenersStarted) {
+                       return;
+               }
+               listenersStarted = true;
+               startListener(this.responseFeedListener);
+               startListener(showRequestFeedListener);
+
+       }
+
+       private void startListener(FeedReader fr) {
+               try {
+                       fr.start();
+               }
+               catch (Exception e) {
+                       logger.error("Failed to start " + 
fr.getClass().getCanonicalName(), e);
+               }
+       }
+       
+       public void setInteractionUtils(InteractionUtils interactionUtils) {
+               this.interactionUtils = interactionUtils;
+       }
+
+       public void setShowRequestFeedListener(
+                       ShowRequestFeedListener showRequestFeedListener) {
+               this.showRequestFeedListener = showRequestFeedListener;
+       }
+
+       public void setResponseFeedListener(ResponseFeedListener 
responseFeedListener) {
+               this.responseFeedListener = responseFeedListener;
+       }
+
+       public void setInteractionPreference(InteractionPreference 
interactionPreference) {
+               this.interactionPreference = interactionPreference;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
new file mode 100644
index 0000000..6b47368
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
@@ -0,0 +1,284 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.preference;
+
+import java.awt.GraphicsEnvironment;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class InteractionPreference {
+       
+       private ApplicationConfiguration appConfig;
+
+       private static final String USE_JETTY = "useJetty";
+
+       private static final String DEFAULT_USE_JETTY = "true";
+
+       private static final String PORT = "port";
+
+       private static final String DEFAULT_PORT = "8080";
+
+       private static final String HOST = "host";
+
+       private static final String DEFAULT_HOST = "http://localhost";;
+
+       private static final String WEBDAV_PATH = "webdavPath";
+
+       private static final String DEFAULT_WEBDAV_PATH = "/interaction";
+
+       private static final String FEED_PATH = "feedPath";
+
+       private static final String DEFAULT_FEED_PATH = "/feed";
+
+       private static final String USE_USERNAME = "Secure with username / 
password";
+
+       private static final String DEFAULT_USE_USERNAME = "false";
+
+       // private static final String USE_HTTPS = "Use HTTPS";
+
+       // private static final String DEFAULT_USE_HTTPS = "false";
+
+       private final Logger logger = 
Logger.getLogger(InteractionPreference.class);
+
+       private final Properties properties;
+
+       private File getConfigFile() {
+               final File home = appConfig
+                               .getApplicationHomeDir();
+               final File config = new File(home, "conf");
+               if (!config.exists()) {
+                       config.mkdir();
+               }
+               final File configFile = new File(config, this.getFilePrefix() + 
"-"
+                               + this.getUUID() + ".config");
+               return configFile;
+       }
+
+       private InteractionPreference(ApplicationConfiguration appConfig) {
+               setAppConfig(appConfig);
+               final File configFile = this.getConfigFile();
+               this.properties = new Properties();
+               if (configFile.exists()) {
+                       try {
+                               final FileReader reader = new 
FileReader(configFile);
+                               this.properties.load(reader);
+                               reader.close();
+                       } catch (final FileNotFoundException e) {
+                               this.logger.error(e);
+                       } catch (final IOException e) {
+                               this.logger.error(e);
+                       }
+               }
+               if (GraphicsEnvironment.isHeadless()
+                               || ((System.getProperty("java.awt.headless") != 
null) && System
+                                               
.getProperty("java.awt.headless").equals("true"))) {
+                       final String definedHost = System
+                                       
.getProperty("taverna.interaction.host");
+                       if (definedHost != null) {
+                               this.properties.setProperty(USE_JETTY, "false");
+                               this.logger.info("USE_JETTY set to false");
+                               this.properties.setProperty(HOST, definedHost);
+                       }
+                       final String definedPort = System
+                                       
.getProperty("taverna.interaction.port");
+                       if (definedPort != null) {
+                               this.properties.setProperty(PORT, definedPort);
+                       }
+                       final String definedWebDavPath = System
+                                       
.getProperty("taverna.interaction.webdav_path");
+                       if (definedWebDavPath != null) {
+                               this.properties.setProperty(WEBDAV_PATH, 
definedWebDavPath);
+                       }
+                       final String definedFeedPath = System
+                                       
.getProperty("taverna.interaction.feed_path");
+                       if (definedFeedPath != null) {
+                               this.properties.setProperty(FEED_PATH, 
definedFeedPath);
+                       }
+               } else {
+                       this.logger.info("Running non-headless");
+               }
+               this.fillDefaultProperties();
+       }
+
+       private void fillDefaultProperties() {
+               if (!this.properties.containsKey(USE_JETTY)) {
+                       this.properties.setProperty(USE_JETTY, 
DEFAULT_USE_JETTY);
+                       this.logger.info("USE_JETTY set to " + 
DEFAULT_USE_JETTY);
+               }
+               if (!this.properties.containsKey(PORT)) {
+                       this.properties.setProperty(PORT, DEFAULT_PORT);
+               }
+               if (!this.properties.containsKey(HOST)) {
+                       this.properties.setProperty(HOST, DEFAULT_HOST);
+               }
+               if (!this.properties.containsKey(WEBDAV_PATH)) {
+                       this.properties.setProperty(WEBDAV_PATH, 
DEFAULT_WEBDAV_PATH);
+               }
+               if (!this.properties.containsKey(FEED_PATH)) {
+                       this.properties.setProperty(FEED_PATH, 
DEFAULT_FEED_PATH);
+               }
+               if (!this.properties.containsKey(USE_USERNAME)) {
+                       this.properties.setProperty(USE_USERNAME, 
DEFAULT_USE_USERNAME);
+               }
+               /*
+                * if (!properties.containsKey(USE_HTTPS)) {
+                * properties.setProperty(USE_HTTPS, DEFAULT_USE_HTTPS); }
+                */
+       }
+
+       public String getFilePrefix() {
+               return "Interaction";
+       }
+
+       public void store() {
+               try {
+                       final FileOutputStream out = new FileOutputStream(
+                                       this.getConfigFile());
+                       this.properties.store(out, "");
+                       out.close();
+               } catch (final FileNotFoundException e) {
+                       this.logger.error(e);
+               } catch (final IOException e) {
+                       this.logger.error(e);
+               }
+       }
+
+       public String getUUID() {
+               return "DA992717-5A46-469D-AE25-883F0E4CD348";
+       }
+
+       public void setPort(final String text) {
+               this.properties.setProperty(PORT, text);
+       }
+
+       public void setHost(final String text) {
+               this.properties.setProperty(HOST, text);
+       }
+
+       public void setUseJetty(final boolean use) {
+               this.properties.setProperty(USE_JETTY, Boolean.toString(use));
+       }
+
+       public void setFeedPath(final String path) {
+               this.properties.setProperty(FEED_PATH, path);
+       }
+
+       public void setWebDavPath(final String path) {
+               this.properties.setProperty(WEBDAV_PATH, path);
+       }
+
+       public String getPort() {
+               return this.properties.getProperty(PORT);
+       }
+
+       public String getHost() {
+               return this.properties.getProperty(HOST);
+       }
+
+       public boolean getUseJetty() {
+               return 
(Boolean.parseBoolean(this.properties.getProperty(USE_JETTY)));
+       }
+
+       public String getFeedPath() {
+               return this.properties.getProperty(FEED_PATH);
+       }
+
+       public String getWebDavPath() {
+               return this.properties.getProperty(WEBDAV_PATH);
+       }
+
+       public String getDefaultHost() {
+               return DEFAULT_HOST;
+       }
+
+       public String getDefaultFeedPath() {
+               return DEFAULT_FEED_PATH;
+       }
+
+       public String getDefaultWebDavPath() {
+               return DEFAULT_WEBDAV_PATH;
+       }
+
+       public String getFeedUrlString() {
+               return this.getHost() + ":" + this.getPort() + 
this.getFeedPath();
+       }
+
+       public String getLocationUrl() {
+               return this.getHost() + ":" + this.getPort() + 
this.getWebDavPath();
+       }
+
+       public boolean getUseUsername() {
+               return 
(Boolean.parseBoolean(this.properties.getProperty(USE_USERNAME)));
+       }
+
+       public void setUseUsername(final boolean useUsername) {
+               this.properties
+                               .setProperty(USE_USERNAME, 
Boolean.toString(useUsername));
+       }
+
+       public String getOutputDataUrlString(final String interactionId) {
+               return this.getLocationUrl()
+                               + "/interaction" + interactionId + 
"OutputData.json";
+       }
+
+       public String getInputDataUrlString(final String interactionId) {
+               return this.getLocationUrl()
+                               + "/interaction" + interactionId + 
"InputData.json";
+       }
+
+       public URL getFeedUrl() throws MalformedURLException {
+               return new URL(this.getFeedUrlString());
+       }
+
+       public String getInteractionUrlString(final String interactionId) {
+               return this.getLocationUrl()
+                               + "/interaction" + interactionId + ".html";
+       }
+
+       public String getPresentationUrlString(final String interactionId) {
+               return this.getLocationUrl()
+                               + "/presentation" + interactionId + ".html";
+       }
+
+       public String getPublicationUrlString(final String interactionId,
+                       final String key) {
+               return this.getLocationUrl()
+                               + "/interaction" + interactionId + "_" + key;
+       }
+
+       public void setAppConfig(ApplicationConfiguration appConfig) {
+               this.appConfig = appConfig;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
new file mode 100644
index 0000000..774060e
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
@@ -0,0 +1,145 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.velocity;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+import org.apache.taverna.activities.interaction.InteractionActivity;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class InteractionVelocity {
+
+       public static Logger logger = 
Logger.getLogger(InteractionVelocity.class);
+
+       private static boolean velocityInitialized = false;
+
+       private static final String TEMPLATE_SUFFIX = ".vm";
+
+       private Template interactionTemplate = null;
+       private static final String INTERACTION_TEMPLATE_NAME = "interaction";
+
+       private ArrayList<String> templateNames = new ArrayList<String>();
+
+       private VelocityEngine ve = new VelocityEngine();
+       
+       @SuppressWarnings("deprecation")
+       public synchronized void checkVelocity() {
+               if (velocityInitialized) { 
+                       return;
+               }
+               velocityInitialized = true;
+               ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "string");
+               ve.setProperty("resource.loader.class",
+                               
"org.apache.velocity.runtime.resource.loader.StringResourceLoader");
+               ve.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
+                               
"org.apache.velocity.runtime.log.Log4JLogChute");
+               ve.setProperty("runtime.log.logsystem.log4j.logger",
+                               
"net.sf.taverna.t2.activities.interaction.velocity.InteractionVelocity");
+               ve.init();
+               ve.loadDirective(RequireDirective.class.getName());
+               ve.loadDirective(ProduceDirective.class.getName());
+               ve.loadDirective(NotifyDirective.class.getName());
+
+               loadTemplates();
+
+               interactionTemplate = ve.getTemplate(INTERACTION_TEMPLATE_NAME);
+               if (interactionTemplate == null) {
+                       logger.error("Could not open interaction template "
+                                       + INTERACTION_TEMPLATE_NAME);
+               }
+       }
+
+       private void loadTemplates() {
+               final InputStream is = InteractionActivity.class
+                               .getResourceAsStream("/index");
+               if (is == null) {
+                       logger.error("Unable to read /index");
+                       return;
+               }
+               final BufferedReader br = new BufferedReader(new 
InputStreamReader(is));
+               try {
+                       for (String line = br.readLine(); line != null; line = 
br
+                                       .readLine()) {
+                               if (line.startsWith("#")) {
+                                       continue;
+                               }
+                               line = line.trim();
+                               if (line.isEmpty()) {
+                                       continue;
+                               }
+                               final String templatePath = line + 
TEMPLATE_SUFFIX;
+                               logger.info("Looking for " + templatePath);
+                               final StringResourceRepository repo = 
StringResourceLoader
+                                               .getRepository();
+                               try {
+                                       repo.putStringResource(line,
+                                                       
getTemplateFromResource(templatePath));
+                               } catch (final IOException e) {
+                                       logger.error(
+                                                       "Failed reading 
template from " + templatePath, e);
+                               }
+                               final Template t = Velocity.getTemplate(line);
+                               if (t == null) {
+                                       logger.error("Registration failed");
+                               }
+                               if (!line.equals(INTERACTION_TEMPLATE_NAME)) {
+                                       templateNames.add(line);
+                               }
+                       }
+               } catch (final IOException e) {
+                       logger.error("Failed reading template index", e);
+               }
+       }
+
+       public Template getInteractionTemplate() {
+               checkVelocity();
+               return interactionTemplate;
+       }
+
+       private String getTemplateFromResource(final String templatePath)
+                       throws IOException {
+               checkVelocity();
+               final InputStream stream = InteractionVelocity.class
+                               .getResourceAsStream("/" + templatePath);
+               final String result = IOUtils.toString(stream, "UTF-8");
+               return result;
+       }
+
+       public ArrayList<String> getTemplateNames() {
+               checkVelocity();
+               return templateNames;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
new file mode 100644
index 0000000..d9c6ec2
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
@@ -0,0 +1,42 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.velocity;
+
+import org.apache.velocity.runtime.parser.node.ASTDirective;
+import org.apache.velocity.runtime.visitor.BaseVisitor;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class NotifyChecker extends BaseVisitor {
+
+       @Override
+       public Object visit(final ASTDirective node, final Object data) {
+               ObjectNode json = (ObjectNode) data;
+               if (node.getDirectiveName().equals("notify")) {
+                       json.put("progressNotification", true);
+               }
+               return null;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
----------------------------------------------------------------------
diff --git 
a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
new file mode 100644
index 0000000..2b314d6
--- /dev/null
+++ 
b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
@@ -0,0 +1,74 @@
+/*
+* 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.
+*/
+
+package org.apache.taverna.activities.interaction.velocity;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * @author alanrw
+ * 
+ */
+public class NotifyDirective extends Directive {
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see org.apache.velocity.runtime.directive.Directive#getName()
+        */
+       @Override
+       public String getName() {
+               return "notify";
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see org.apache.velocity.runtime.directive.Directive#getType()
+        */
+       @Override
+       public int getType() {
+               return LINE;
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see
+        * 
org.apache.velocity.runtime.directive.Directive#render(org.apache.velocity
+        * .context.InternalContextAdapter, java.io.Write\ r,
+        * org.apache.velocity.runtime.parser.node.Node)
+        */
+       @Override
+       public boolean render(final InternalContextAdapter context,
+                       final Writer writer, final Node node) throws 
IOException,
+                       ResourceNotFoundException, ParseErrorException,
+                       MethodInvocationException {
+               return true;
+       }
+
+}

Reply via email to