You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/02/17 12:37:25 UTC

[65/70] [abbrv] incubator-taverna-common-activities git commit: taverna-interaction-activity/

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/src/main/resources/pmrpc.js
----------------------------------------------------------------------
diff --git a/src/main/resources/pmrpc.js b/src/main/resources/pmrpc.js
deleted file mode 100644
index 0edc8cf..0000000
--- a/src/main/resources/pmrpc.js
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * pmrpc 0.6 - Inter-widget remote procedure call library based on HTML5 
- *             postMessage API and JSON-RPC. https://github.com/izuzak/pmrpc
- *
- * Copyright 2011 Ivan Zuzak, Marko Ivankovic
- *
- * Licensed 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.
- */
-
-pmrpc = self.pmrpc =  function() {
-  // check if JSON library is available
-  if (typeof JSON === "undefined" || typeof JSON.stringify === "undefined" || 
-      typeof JSON.parse === "undefined") {
-    throw "pmrpc requires the JSON library";
-  }
-  
-  // TODO: make "contextType" private variable
-  // check if postMessage APIs are available
-  if (typeof this.postMessage === "undefined" &&  // window or worker
-        typeof this.onconnect === "undefined") {  // shared worker
-      throw "pmrpc requires the HTML5 cross-document messaging and worker APIs";
-  }
-    
-  // Generates a version 4 UUID
-  function generateUUID() {
-    var uuid = [], nineteen = "89AB", hex = "0123456789ABCDEF";
-    for (var i=0; i<36; i++) {
-      uuid[i] = hex[Math.floor(Math.random() * 16)];
-    }
-    uuid[14] = '4';
-    uuid[19] = nineteen[Math.floor(Math.random() * 4)];
-    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
-    return uuid.join('');
-  }
-  
-  // TODO: remove this - make everything a regex?
-  // Converts a wildcard expression into a regular expression
-  function convertWildcardToRegex(wildcardExpression) {
-    var regex = wildcardExpression.replace(
-                  /([\^\$\.\+\?\=\!\:\|\\\/\(\)\[\]\{\}])/g, "\\$1");
-    regex = "^" + regex.replace(/\*/g, ".*") + "$";
-    return regex;
-  }
-  
-  // Checks whether a domain satisfies the access control list. The access 
-  // control list has a whitelist and a blacklist. In order to satisfy the acl, 
-  // the domain must be on the whitelist, and must not be on the blacklist.
-  function checkACL(accessControlList, origin) {
-    var aclWhitelist = accessControlList.whitelist;
-    var aclBlacklist = accessControlList.blacklist;
-      
-    var isWhitelisted = false;
-    var isBlacklisted = false;
-    
-    for (var i=0; i<aclWhitelist.length; ++i) {
-      var aclRegex = convertWildcardToRegex(aclWhitelist[i]);
-      if(origin.match(aclRegex)) {
-        isWhitelisted = true;
-        break;
-      }
-    }
-     
-    for (var j=0; i<aclBlacklist.length; ++j) {
-      var aclRegex = convertWildcardToRegex(aclBlacklist[j]);
-      if(origin.match(aclRegex)) {
-        isBlacklisted = true;
-        break;
-      }
-    }
-    
-    return isWhitelisted && !isBlacklisted;
-  }
-  
-  // Calls a function with either positional or named parameters
-  // In either case, additionalParams will be appended to the end
-  function invokeProcedure(fn, self, params, additionalParams) {
-    if (!(params instanceof Array)) {
-      // get string representation of function
-      var fnDef = fn.toString();
-      
-      // parse the string representation and retrieve order of parameters
-      var argNames = fnDef.substring(fnDef.indexOf("(")+1, fnDef.indexOf(")"));
-      argNames = (argNames === "") ? [] : argNames.split(", ");
-      
-      var argIndexes = {};
-      for (var i=0; i<argNames.length; i++) {
-        argIndexes[argNames[i]] = i;
-      }
-      
-      // construct an array of arguments from a dictionary
-      var callParameters = [];
-      for (var paramName in params) {
-        if (typeof argIndexes[paramName] !== "undefined") {
-          callParameters[argIndexes[paramName]] = params[paramName];
-        } else {
-          throw "No such param!";
-        }
-      }
-      
-      params = callParameters;
-    }
-    
-    // append additional parameters
-    if (typeof additionalParams !== "undefined") {
-      params = params.concat(additionalParams);
-    }
-    
-    // invoke function with specified context and arguments array
-    return fn.apply(self, params);
-  }
-  
-  // JSON encode an object into pmrpc message
-  function encode(obj) {
-    return "pmrpc." + JSON.stringify(obj);
-  }
-  
-  // JSON decode a pmrpc message
-  function decode(str) {
-    return JSON.parse(str.substring("pmrpc.".length));
-  }
-
-  // Creates a base JSON-RPC object, usable for both request and response.
-  // As of JSON-RPC 2.0 it only contains one field "jsonrpc" with value "2.0"
-  function createJSONRpcBaseObject() {
-    var call = {};
-    call.jsonrpc = "2.0";
-    return call;
-  }
-
-  // Creates a JSON-RPC request object for the given method and parameters
-  function createJSONRpcRequestObject(procedureName, parameters, id) {
-    var call = createJSONRpcBaseObject();
-    call.method = procedureName;
-    call.params = parameters;
-    if (typeof id !== "undefined") {
-      call.id = id;
-    }
-    return call;
-  }
-
-  // Creates a JSON-RPC error object complete with message and error code
-  function createJSONRpcErrorObject(errorcode, message, data) {
-    var error = {};
-    error.code = errorcode;
-    error.message = message;
-    error.data = data;
-    return error;
-  }
-
-  // Creates a JSON-RPC response object.
-  function createJSONRpcResponseObject(error, result, id) {
-    var response = createJSONRpcBaseObject();
-    response.id = id;
-    
-    if (typeof error === "undefined" || error === null) {
-      response.result = (result === "undefined") ? null : result;
-    } else {
-      response.error = error;
-    }
-    
-    return response;
-  }
-
-  // dictionary of services registered for remote calls
-  var registeredServices = {};
-  // dictionary of requests being processed on the client side
-  var callQueue = {};
-  
-  var reservedProcedureNames = {};
-  // register a service available for remote calls
-  // if no acl is given, assume that it is available to everyone
-  function register(config) {
-    if (config.publicProcedureName in reservedProcedureNames) {
-      return false;
-    } else {
-      registeredServices[config.publicProcedureName] = {
-        "publicProcedureName" : config.publicProcedureName,
-        "procedure" : config.procedure,
-        "context" : config.procedure.context,
-        "isAsync" : typeof config.isAsynchronous !== "undefined" ?
-                      config.isAsynchronous : false,
-        "acl" : typeof config.acl !== "undefined" ? 
-                  config.acl : {whitelist: ["*"], blacklist: []}};
-      return true;
-    }
-  }
-
-  // unregister a previously registered procedure
-  function unregister(publicProcedureName) {
-    if (publicProcedureName in reservedProcedureNames) {
-      return false;
-    } else {
-      delete registeredServices[publicProcedureName];
-      return true;
-    }
-  }
-
-  // retreive service for a specific procedure name
-  function fetchRegisteredService(publicProcedureName){
-    return registeredServices[publicProcedureName];
-  }
-  
-  // receive and execute a pmrpc call which may be a request or a response
-  function processPmrpcMessage(eventParams) {
-    var serviceCallEvent = eventParams.event;
-    var eventSource = eventParams.source;
-    var isWorkerComm = typeof eventSource !== "undefined" && eventSource !== null;
-    
-    // if the message is not for pmrpc, ignore it.
-    if (serviceCallEvent.data.indexOf("pmrpc.") !== 0) {
-      return;
-    } else {
-      var message = decode(serviceCallEvent.data);
-      //if (typeof console !== "undefined" && console.log !== "undefined" && (typeof this.frames !== "undefined")) { console.log("Received:" + encode(message)); }
-      if (typeof message.method !== "undefined") {
-        // this is a request
-        
-        // ako je 
-        var newServiceCallEvent = {
-          data : serviceCallEvent.data,
-          source : isWorkerComm ? eventSource : serviceCallEvent.source,
-          origin : isWorkerComm ? "*" : serviceCallEvent.origin,
-          shouldCheckACL : !isWorkerComm
-        };
-        
-        response = processJSONRpcRequest(message, newServiceCallEvent);
-        
-        // return the response
-        if (response !== null) {
-          sendPmrpcMessage(
-            newServiceCallEvent.source, response, newServiceCallEvent.origin);
-        }
-      } else {
-        // this is a response
-        processJSONRpcResponse(message);
-      }
-    }
-  }
-  
-  // Process a single JSON-RPC Request
-  function processJSONRpcRequest(request, serviceCallEvent, shouldCheckACL) {
-    if (request.jsonrpc !== "2.0") {
-      // Invalid JSON-RPC request    
-      return createJSONRpcResponseObject(
-        createJSONRpcErrorObject(-32600, "Invalid request.", 
-          "The recived JSON is not a valid JSON-RPC 2.0 request."),
-        null,
-        null);
-    }
-    
-    var id = request.id;
-    var service = fetchRegisteredService(request.method);
-    
-    if (typeof service !== "undefined") {
-      // check the acl rights
-      if (!serviceCallEvent.shouldCheckACL || 
-            checkACL(service.acl, serviceCallEvent.origin)) {
-        try {
-          if (service.isAsync) {
-            // if the service is async, create a callback which the service
-            // must call in order to send a response back
-            var cb = function (returnValue) {
-                       sendPmrpcMessage(
-                         serviceCallEvent.source,
-                         createJSONRpcResponseObject(null, returnValue, id),
-                         serviceCallEvent.origin);
-                     };
-            invokeProcedure(
-              service.procedure, service.context, request.params, [cb, serviceCallEvent]);
-            return null;
-          } else {
-            // if the service is not async, just call it and return the value
-            var returnValue = invokeProcedure(
-                                service.procedure,
-                                service.context, 
-                                request.params, [serviceCallEvent]);
-            return (typeof id === "undefined") ? null : 
-              createJSONRpcResponseObject(null, returnValue, id);
-          }
-        } catch (error) {
-          if (typeof id === "undefined") {
-            // it was a notification nobody cares if it fails
-            return null;
-          }
-          
-          if (error === "No such param!") {
-            return createJSONRpcResponseObject(
-              createJSONRpcErrorObject(
-                -32602, "Invalid params.", error.description),
-              null,
-              id);            
-          }            
-          
-          // the -1 value is "application defined"
-          return createJSONRpcResponseObject(
-            createJSONRpcErrorObject(
-              -1, "Application error.", error.description),
-            null,
-            id);
-        }
-      } else {
-        // access denied
-        return (typeof id === "undefined") ? null : createJSONRpcResponseObject(
-          createJSONRpcErrorObject(
-            -2, "Application error.", "Access denied on server."),
-          null,
-          id);
-      }
-    } else {
-      // No such method
-      return (typeof id === "undefined") ? null : createJSONRpcResponseObject(
-        createJSONRpcErrorObject(
-          -32601,
-          "Method not found.", 
-          "The requestd remote procedure does not exist or is not available."),
-        null,
-        id);
-    }
-  }
-  
-  // internal rpc service that receives responses for rpc calls 
-  function processJSONRpcResponse(response) {
-    var id = response.id;
-    var callObj = callQueue[id];
-    if (typeof callObj === "undefined" || callObj === null) {
-      return;
-    } else {
-      delete callQueue[id];
-    }
-    
-    // check if the call was sucessful or not
-    if (typeof response.error === "undefined") {
-      callObj.onSuccess( { 
-        "destination" : callObj.destination,
-        "publicProcedureName" : callObj.publicProcedureName,
-        "params" : callObj.params,
-        "status" : "success",
-        "returnValue" : response.result} );
-    } else {
-      callObj.onError( { 
-        "destination" : callObj.destination,
-        "publicProcedureName" : callObj.publicProcedureName,
-        "params" : callObj.params,
-        "status" : "error",
-        "description" : response.error.message + " " + response.error.data} );
-    }
-  }
-  
-  // call remote procedure
-  function call(config) {
-    // check that number of retries is not -1, that is a special internal value
-    if (config.retries && config.retries < 0) {
-      throw new Exception("number of retries must be 0 or higher");
-    }
-    
-    var destContexts = [];
-    
-    if (typeof config.destination === "undefined" || config.destination === null || config.destination === "workerParent") {
-      destContexts = [{context : null, type : "workerParent"}];
-    } else if (config.destination === "publish") {
-      destContexts = findAllReachableContexts();
-    } else if (config.destination instanceof Array) {
-      for (var i=0; i<config.destination.length; i++) {
-        if (config.destination[i] === "workerParent") {
-          destContexts.push({context : null, type : "workerParent"});
-        } else if (typeof config.destination[i].frames !== "undefined") {
-          destContexts.push({context : config.destination[i], type : "window"});
-        } else {
-          destContexts.push({context : config.destination[i], type : "worker"});
-        }
-      }
-    } else {
-      if (typeof config.destination.frames !== "undefined") {
-        destContexts.push({context : config.destination, type : "window"});
-      } else {
-        destContexts.push({context : config.destination, type : "worker"});
-      }
-    }
-        
-    for (var i=0; i<destContexts.length; i++) {
-      var callObj = {
-        destination : destContexts[i].context,
-        destinationDomain : typeof config.destinationDomain === "undefined" ? ["*"] : (typeof config.destinationDomain === "string" ? [config.destinationDomain] : config.destinationDomain),
-        publicProcedureName : config.publicProcedureName,
-        onSuccess : typeof config.onSuccess !== "undefined" ? 
-                      config.onSuccess : function (){},
-        onError : typeof config.onError !== "undefined" ? 
-                      config.onError : function (){},
-        retries : typeof config.retries !== "undefined" ? config.retries : 5,
-        timeout : typeof config.timeout !== "undefined" ? config.timeout : 500,
-        status : "requestNotSent"
-      };
-      
-      isNotification = typeof config.onError === "undefined" && typeof config.onSuccess === "undefined";
-      params = (typeof config.params !== "undefined") ? config.params : [];
-      callId = generateUUID();
-      callQueue[callId] = callObj; 
-      
-      if (isNotification) {
-        callObj.message = createJSONRpcRequestObject(
-                    config.publicProcedureName, params);
-      } else {
-        callObj.message = createJSONRpcRequestObject(
-                            config.publicProcedureName, params, callId);
-      }
-      
-      waitAndSendRequest(callId);
-    }
-  }
-  
-  // Use the postMessage API to send a pmrpc message to a destination
-  function sendPmrpcMessage(destination, message, acl) {
-    //if (typeof console !== "undefined" && console.log !== "undefined" && (typeof this.frames !== "undefined")) { console.log("Sending:" + encode(message)); }
-    if (typeof destination === "undefined" || destination === null) {
-      self.postMessage(encode(message));
-    } else if (typeof destination.frames !== "undefined") {
-      return destination.postMessage(encode(message), acl);
-    } else {
-      destination.postMessage(encode(message));
-    }
-  }
-    
-  // Execute a remote call by first pinging the destination and afterwards
-  // sending the request
-  function waitAndSendRequest(callId) {
-    var callObj = callQueue[callId];
-    if (typeof callObj === "undefined") {
-      return;
-    } else if (callObj.retries <= -1) {      
-      processJSONRpcResponse(
-        createJSONRpcResponseObject(
-          createJSONRpcErrorObject(
-          -4, "Application error.", "Destination unavailable."),
-          null,
-          callId));
-    } else if (callObj.status === "requestSent") {
-      return;
-    } else if (callObj.retries === 0 || callObj.status === "available") {
-      callObj.status = "requestSent";
-      callObj.retries = -1;
-      callQueue[callId] = callObj;
-      for (var i=0; i<callObj.destinationDomain.length; i++) {
-        sendPmrpcMessage(
-          callObj.destination, callObj.message, callObj.destinationDomain[i], callObj);
-        self.setTimeout(function() { waitAndSendRequest(callId); }, callObj.timeout);
-      }
-    } else {
-      // if we can ping some more - send a new ping request
-      callObj.status = "pinging";
-      callObj.retries = callObj.retries - 1;
-      
-      call({
-        "destination" : callObj.destination,
-        "publicProcedureName" : "receivePingRequest",
-        "onSuccess" : function (callResult) {
-                        if (callResult.returnValue === true &&
-                            typeof callQueue[callId] !== 'undefined') {
-                          callQueue[callId].status = "available";
-                          waitAndSendRequest(callId);
-                        }
-                      },
-        "params" : [callObj.publicProcedureName], 
-        "retries" : 0,
-        "destinationDomain" : callObj.destinationDomain});
-      callQueue[callId] = callObj;
-      self.setTimeout(function() { waitAndSendRequest(callId); }, callObj.timeout / callObj.retries);
-    }
-  }
-  
-  // attach the pmrpc event listener 
-  function addCrossBrowserEventListerner(obj, eventName, handler, bubble) {
-    if ("addEventListener" in obj) {
-      // FF
-      obj.addEventListener(eventName, handler, bubble);
-    } else {
-      // IE
-      obj.attachEvent("on" + eventName, handler);
-    }
-  }
-  
-  function createHandler(method, source, destinationType) {
-    return function(event) {
-      var params = {event : event, source : source, destinationType : destinationType};
-      method(params);
-    };
-  }
-  
-  if ('window' in this) {
-    // window object - window-to-window comm
-    var handler = createHandler(processPmrpcMessage, null, "window");
-    addCrossBrowserEventListerner(this, "message", handler, false);
-  } else if ('onmessage' in this) {
-    // dedicated worker - parent X to worker comm
-    var handler = createHandler(processPmrpcMessage, this, "worker");
-    addCrossBrowserEventListerner(this, "message", handler, false);
-  } else if ('onconnect' in this) {
-    // shared worker - parent X to shared-worker comm
-    var connectHandler = function(e) {
-      //this.sendPort = e.ports[0];
-      var handler = createHandler(processPmrpcMessage, e.ports[0], "sharedWorker");      
-      addCrossBrowserEventListerner(e.ports[0], "message", handler, false);
-      e.ports[0].start();
-    };
-    addCrossBrowserEventListerner(this, "connect", connectHandler, false);
-  } else {
-    throw "Pmrpc must be loaded within a browser window or web worker.";
-  }
-  
-  // Override Worker and SharedWorker constructors so that pmrpc may relay
-  // messages. For each message received from the worker, call pmrpc processing
-  // method. This is child worker to parent communication.
-    
-  var createDedicatedWorker = this.Worker;
-  this.nonPmrpcWorker = createDedicatedWorker;
-  var createSharedWorker = this.SharedWorker;
-  this.nonPmrpcSharedWorker = createSharedWorker;
-  
-  var allWorkers = [];
-  
-  this.Worker = function(scriptUri) {
-    var newWorker = new createDedicatedWorker(scriptUri);
-    allWorkers.push({context : newWorker, type : 'worker'});   
-    var handler = createHandler(processPmrpcMessage, newWorker, "worker");
-    addCrossBrowserEventListerner(newWorker, "message", handler, false);
-    return newWorker;
-  };
-  
-  this.SharedWorker = function(scriptUri, workerName) {
-    var newWorker = new createSharedWorker(scriptUri, workerName);
-    allWorkers.push({context : newWorker, type : 'sharedWorker'});    
-    var handler = createHandler(processPmrpcMessage, newWorker.port, "sharedWorker");
-    addCrossBrowserEventListerner(newWorker.port, "message", handler, false);
-    newWorker.postMessage = function (msg, portArray) {
-      return newWorker.port.postMessage(msg, portArray);
-    };
-    newWorker.port.start();
-    return newWorker;
-  };
-  
-  // function that receives pings for methods and returns responses 
-  function receivePingRequest(publicProcedureName) {
-    return typeof fetchRegisteredService(publicProcedureName) !== "undefined";
-  }
-  
-  function subscribe(params) {
-    return register(params);
-  }
-  
-  function unsubscribe(params) {
-    return unregister(params);
-  }
-  
-  function findAllWindows() {
-    var allWindowContexts = [];
-    
-    if (typeof window !== 'undefined') {
-      allWindowContexts.push( { context : window.top, type : 'window' } );
-      
-      // walk through all iframes, starting with window.top
-      for (var i=0; typeof allWindowContexts[i] !== 'undefined'; i++) {
-        var currentWindow = allWindowContexts[i];
-        for (var j=0; j<currentWindow.context.frames.length; j++) {
-          allWindowContexts.push({ 
-            context : currentWindow.context.frames[j],
-            type : 'window'
-          });
-        }
-      }
-    } else {
-      allWindowContexts.push( {context : this, type : 'workerParent'} );
-    }
-    
-    return allWindowContexts;
-  }
-  
-  function findAllWorkers() {
-    return allWorkers;
-  }
-  
-  function findAllReachableContexts() {
-    var allWindows = findAllWindows();
-    var allWorkers = findAllWorkers();
-    var allContexts = allWindows.concat(allWorkers);
-    
-    return allContexts;
-  }
-  
-  // register method for receiving and returning pings
-  register({
-    "publicProcedureName" : "receivePingRequest",
-    "procedure" : receivePingRequest});
-  
-  function getRegisteredProcedures() {
-    var regSvcs = [];
-    var origin = typeof this.frames !== "undefined" ? (window.location.protocol + "//" + window.location.host + (window.location.port !== "" ? ":" + window.location.port : "")) : "";
-    for (publicProcedureName in registeredServices) {
-      if (publicProcedureName in reservedProcedureNames) {
-        continue;
-      } else {
-        regSvcs.push( { 
-          "publicProcedureName" : registeredServices[publicProcedureName].publicProcedureName,
-          "acl" : registeredServices[publicProcedureName].acl,
-          "origin" : origin
-        } );
-      }
-    }
-    return regSvcs;
-  }
-  
-  // register method for returning registered procedures
-  register({
-    "publicProcedureName" : "getRegisteredProcedures",
-    "procedure" : getRegisteredProcedures});
-    
-  function discover(params) {
-    var windowsForDiscovery = null;
-    
-    if (typeof params.destination === "undefined") {
-      windowsForDiscovery = findAllReachableContexts();
-      for (var i=0; i<windowsForDiscovery.length; i++) {
-        windowsForDiscovery[i] = windowsForDiscovery[i].context;
-      }
-    } else {
-      windowsForDiscovery = params.destination;
-    }
-    var originRegex = typeof params.origin === "undefined" ?
-      ".*" : params.origin; 
-    var nameRegex = typeof params.publicProcedureName === "undefined" ?
-      ".*" : params.publicProcedureName;
-    
-    var counter = windowsForDiscovery.length;
-    
-    var discoveredMethods = [];
-    function addToDiscoveredMethods(methods, destination) {
-      for (var i=0; i<methods.length; i++) {
-        if (methods[i].origin.match(originRegex) && methods[i].publicProcedureName.match(nameRegex)) {
-          discoveredMethods.push({
-            publicProcedureName : methods[i].publicProcedureName,
-            destination : destination,
-            procedureACL : methods[i].acl,
-            destinationOrigin : methods[i].origin
-          });
-        }
-      }
-    }
-    
-    pmrpc.call({
-      destination : windowsForDiscovery,
-      destinationDomain : "*",
-      publicProcedureName : "getRegisteredProcedures",
-      onSuccess : function (callResult) {
-                    counter--;
-                    addToDiscoveredMethods(callResult.returnValue, callResult.destination);
-                    if (counter === 0) {
-                      params.callback(discoveredMethods);
-                    }
-                  },
-      onError : function (callResult) {
-                  counter--;
-                  if (counter === 0) {
-                    params.callback(discoveredMethods);
-                  }
-                }
-    });
-  }
-  
-  reservedProcedureNames = {"getRegisteredProcedures" : null, "receivePingRequest" : null};
-  
-  // return public methods
-  return {
-    register : register,
-    unregister : unregister,
-    call : call,
-    discover : discover
-  };
-}();

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/src/main/resources/schema.json
----------------------------------------------------------------------
diff --git a/src/main/resources/schema.json b/src/main/resources/schema.json
deleted file mode 100644
index dcde96e..0000000
--- a/src/main/resources/schema.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
-	"$schema": "http://json-schema.org/draft-03/schema#",
-    "id": "http://ns.taverna.org.uk/2010/activity/interaction.schema.json",
-    "title": Interaction activity configuration",
-    "type": "object",
-    "properties": {
-        "@context": {
-            "description": "JSON-LD context for interpreting the configuration as RDF",
-            "required": true,
-            "enum": ["http://ns.taverna.org.uk/2010/activity/interaction.context.json"]
-        },
-        "presentationOrigin": {
-        	"type": "string",
-        	"required": true,
-        	"minLength": 1,
-        	"description": "The URL of the presentation page, or the identifier of the standard template"
-        },
-        "interactionActivityType": {
-        	"type": "string",
-        	"required": true,
-        	"minLength": 1,
-		"enum" : [ "VelocityTemplate", "LocallyPresentedHtml"],
-        	"description": "Indication of the type of the definition for the interaction"
-        },
-        "progressNotification": {
-        	"type": "boolean",
-        	"required": true,
-        	"description": "True if the interaction should not block the workflow run"
-        }
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/src/main/resources/select.vm
----------------------------------------------------------------------
diff --git a/src/main/resources/select.vm b/src/main/resources/select.vm
deleted file mode 100644
index 6fe5764..0000000
--- a/src/main/resources/select.vm
+++ /dev/null
@@ -1,61 +0,0 @@
-#require("valueList",1)
-#require("message")
-#require("title")
-#produce("answer")
-<!doctype html>
-<html>
-  <head>
-      <meta charset="utf-8" />
-      <title></title>
-      <style>
-      </style>
-  </head>
-  <body>
-
-       <script type="text/javascript" src="$pmrpcUrl"></script>
-
-       <script type="text/javascript">
-
-         function reply() {
-           pmrpc.call({
-             destination : "publish",
-             publicProcedureName : "reply",
-             params : ["OK", {"answer" : document.myform.mySelect.options[document.myform.mySelect.selectedIndex].value}],
-             onSuccess : function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Data submitted</h1>';},
-             onFailure: function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Data submission failed</h1>';}
-           });
-	       return true;
-         }
-        
-         function cancel() {
-           pmrpc.call({
-             destination : "publish",
-             publicProcedureName : "reply",
-             params : ["Cancelled", {}],
-             onSuccess : function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Cancelled</h1>';},
-             onFailure: function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Cancellation failed</h1>';}
-           });
-	       return true;
-         }
-         
-         pmrpc.call({
-           destination : "publish",
-           publicProcedureName : "setTitle",
-           params : ["$!title"]});
-
-       </script>
-  
-  <h2>$!message</h2>
-    <form name="myform" onSubmit="reply(); return false;">
-      <select name="mySelect">
-#foreach( $value in $valueList )
-      <option value="$value">$value</option>
-#end
-      </select>
-      <br/>
-      <input type="button" value="Cancel" onClick = "cancel()"/>
-      <input type="button" value="Submit" onClick = "reply()"/>
-    </form> 
-  </body>
-</html>
-

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/src/main/resources/tell.vm
----------------------------------------------------------------------
diff --git a/src/main/resources/tell.vm b/src/main/resources/tell.vm
deleted file mode 100644
index 948c023..0000000
--- a/src/main/resources/tell.vm
+++ /dev/null
@@ -1,54 +0,0 @@
-#require("message")
-#require("title")
-#produce("answer")
-<!doctype html>
-<html>
-  <head>
-      <meta charset="utf-8" />
-      <title></title>
-      <style>
-      </style>
-  </head>
-  <body>
-
-       <script type="text/javascript" src="$pmrpcUrl"></script>
-
-       <script type="text/javascript">
-
-         function reply() {
-           pmrpc.call({
-             destination : "publish",
-             publicProcedureName : "reply",
-             params : ["OK", {"answer" : "answer"}],
-             onSuccess : function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Data submitted</h1>';},
-             onFailure: function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Data submission failed</h1>';}
-           });
-	       return true;
-         }
-         
-         function cancel() {
-           pmrpc.call({
-             destination : "publish",
-             publicProcedureName : "reply",
-             params : ["Cancelled", {}],
-             onSuccess : function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Cancelled</h1>';},
-             onFailure: function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Cancellation failed</h1>';}
-           });
-	       return true;
-         }
-         
-         pmrpc.call({
-           destination : "publish",
-           publicProcedureName : "setTitle",
-           params : ["$!title"]});
-
-       </script>
-  
-  <h2>Message: $!message</h2>
-    <form name="myform" onSubmit="reply(); return false;">
-      <input type="button" value="Cancel" onClick = "cancel()"/>
-      <input type="button" value="OK" onClick = "reply()"/>
-    </form> 
-  </body>
-</html>
-

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/src/main/resources/warn.vm
----------------------------------------------------------------------
diff --git a/src/main/resources/warn.vm b/src/main/resources/warn.vm
deleted file mode 100644
index ae9f573..0000000
--- a/src/main/resources/warn.vm
+++ /dev/null
@@ -1,54 +0,0 @@
-#require("message")
-#require("title")
-#produce("answer")
-<!doctype html>
-<html>
-  <head>
-      <meta charset="UTF-8" />
-      <title></title>
-      <style>
-      </style>
-  </head>
-  <body>
-
-       <script type="text/javascript" src="$pmrpcUrl"></script>
-
-       <script type="text/javascript">
-
-         function reply() {
-           pmrpc.call({
-             destination : "publish",
-             publicProcedureName : "reply",
-             params : ["OK", {"answer" : "answer"}],
-             onSuccess : function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Data submitted</h1>';},
-             onFailure: function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Data submission failed</h1>';}
-           });
-	       return true;
-         }
-         
-         function cancel() {
-           pmrpc.call({
-             destination : "publish",
-             publicProcedureName : "reply",
-             params : ["Cancelled", {}],
-             onSuccess : function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Cancelled</h1>';},
-             onFailure: function() {document.getElementsByTagName('body')[0].innerHTML='<h1>Cancellation failed</h1>';}
-           });
-	       return true;
-         }
-         
-         pmrpc.call({
-           destination : "publish",
-           publicProcedureName : "setTitle",
-           params : ["$!title"]});
-
-       </script>
-  
-  <h2>Warning: $!message</h2>
-    <form name="myform" onSubmit="reply(); return false;">
-      <input type="button" value="Cancel" onClick = "cancel()"/>
-      <input type="button" value="OK" onClick = "reply()"/>
-    </form> 
-  </body>
-</html>
-

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/pom.xml
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/pom.xml b/taverna-interaction-activity/pom.xml
new file mode 100644
index 0000000..de9cbbd
--- /dev/null
+++ b/taverna-interaction-activity/pom.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+                <groupId>net.sf.taverna</groupId>
+                <artifactId>taverna-parent</artifactId>
+                <version>3.0.1-SNAPSHOT</version>
+	</parent>
+	<groupId>net.sf.taverna.t2.activities</groupId>
+	<artifactId>interaction-activity</artifactId>
+	<version>2.0.l-SNAPSHOT</version>
+	<packaging>bundle</packaging>
+	<name>Taverna Interaction activity</name>
+	
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<configuration>
+					<instructions>
+					<!--  Export nothing -->
+						<Export-Package></Export-Package>
+						<Embed-Transitive>true</Embed-Transitive>
+						<Embed-Dependency>webdav-servlet</Embed-Dependency>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	
+	<properties>
+		<jetty.version>6.1.24</jetty.version>
+		<abdera.version>1.1.3</abdera.version>
+		<jackson.version>1.9.13</jackson.version>
+		<velocity.version>1.7</velocity.version>
+	</properties>
+
+	<dependencies>
+	
+			<dependency>
+			<groupId>net.sf.taverna.t2.core</groupId>
+			<artifactId>workflowmodel-api</artifactId>
+			<version>${t2.core.version}</version>
+		</dependency>
+		
+				<dependency>
+		  <groupId>net.sf.taverna.t2.security</groupId>
+		  <artifactId>credential-manager</artifactId>
+		  <version>${t2.core.version}</version>
+		</dependency>
+
+	<dependency>
+		<groupId>net.sf.taverna.t2.activities</groupId>
+		<artifactId>activity-test-utils</artifactId>
+		<version>${t2.activities.version}</version>
+		<scope>test</scope>
+	</dependency>
+
+	<dependency>
+    <groupId>commons-io</groupId>
+		<artifactId>commons-io</artifactId>
+		<version>${commons.io.version}</version>
+	</dependency>
+
+	<dependency>
+    <groupId>commons-codec</groupId>
+    <artifactId>commons-codec</artifactId>
+		<version>${commons.codec.version}</version>
+	</dependency>
+				
+
+		
+		<!--  Mortbay jetty artifacts are bundles -->
+		<dependency>
+			<groupId>org.mortbay.jetty</groupId>
+			<artifactId>jetty</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.mortbay.jetty</groupId>
+			<artifactId>jetty-util</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>servlet-api</artifactId>
+			<version>2.5</version>
+		</dependency>
+
+		<dependency>
+			<groupId>net.sf.webdav-servlet</groupId>
+			<artifactId>webdav-servlet</artifactId>
+			<version>2.0</version>
+		</dependency>	
+		
+<!--  The abdera artifacts are bundles -->
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-core</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+		
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-client</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-server</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-extensions-main</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-extensions-html</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-extensions-json</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-i18n</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.abdera</groupId>
+			<artifactId>abdera-parser</artifactId>
+			<version>${abdera.version}</version>
+		</dependency>
+
+<!--  The velocity artifact is a bundle -->
+		<dependency>
+			<groupId>org.apache.velocity</groupId>
+			<artifactId>velocity</artifactId>
+			<version>${velocity.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+                        <version>${junit.version}</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+        <repositories>
+                <repository>
+                        <releases />
+                        <snapshots>
+                                <enabled>false</enabled>
+                        </snapshots>
+                        <id>mygrid-repository</id>
+                        <name>myGrid Repository</name>
+                        <url>http://www.mygrid.org.uk/maven/repository</url>
+                </repository>
+                <repository>
+                        <releases>
+                                <enabled>false</enabled>
+                        </releases>
+                        <snapshots />
+                        <id>mygrid-snapshot-repository</id>
+                        <name>myGrid Snapshot Repository</name>
+                        <url>http://www.mygrid.org.uk/maven/snapshot-repository</url>
+                </repository>
+        </repositories>
+        <scm>
+                <connection>scm:git:https://github.com/taverna/taverna-interaction-activity.git</connection>
+                <developerConnection>scm:git:ssh://git@github.com/taverna/taverna-interaction-activity.git</developerConnection>
+                <url>https://github.com/taverna/taverna-interaction-activity/</url>
+                <tag>HEAD</tag>
+        </scm>
+	
+	
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/FeedReader.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/FeedReader.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/FeedReader.java
new file mode 100644
index 0000000..7be03df
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/FeedReader.java
@@ -0,0 +1,87 @@
+package net.sf.taverna.t2.activities.interaction;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+
+import net.sf.taverna.t2.activities.interaction.preference.InteractionPreference;
+
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.parser.ParseException;
+import org.apache.abdera.parser.Parser;
+import org.apache.abdera.parser.stax.FOMParser;
+import org.apache.log4j.Logger;
+
+public abstract class FeedReader extends Thread {
+
+	static final Logger logger = Logger.getLogger(FeedReader.class);
+
+	public FeedReader(final String name) {
+		super(name);
+		this.setDaemon(true);
+	}
+
+	protected abstract void considerEntry(Entry entry);
+
+	@Override
+	public void run() {
+		try {
+			final Parser parser = new FOMParser();
+			Date lastCheckedDate = new Date();
+			while (true) {
+				try {
+					sleep(5000);
+				} catch (final InterruptedException e1) {
+					logger.error(e1);
+				}
+				InputStream openStream = null;
+				try {
+					final Date newLastCheckedDate = new Date();
+					final URL url = getInteractionPreference().getFeedUrl();
+					openStream = url.openStream();
+					final Document<Feed> doc = parser.parse(openStream,
+							url.toString());
+					final Feed feed = doc.getRoot().sortEntriesByEdited(true);
+
+					for (final Entry entry : feed.getEntries()) {
+
+						Date d = entry.getEdited();
+						if (d == null) {
+							d = entry.getUpdated();
+						}
+						if (d == null) {
+							d = entry.getPublished();
+						}
+						 if (d.before(lastCheckedDate)) {
+						 break;
+						 }
+						this.considerEntry(entry);
+					}
+					lastCheckedDate = newLastCheckedDate;
+				} catch (final MalformedURLException e) {
+					logger.error(e);
+				} catch (final ParseException e) {
+					logger.error(e);
+				} catch (final IOException e) {
+					logger.error(e);
+				} finally {
+					try {
+						if (openStream != null) {
+							openStream.close();
+						}
+					} catch (final IOException e) {
+						logger.error(e);
+					}
+				}
+			}
+		} catch (final Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected abstract InteractionPreference getInteractionPreference();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivity.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivity.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivity.java
new file mode 100644
index 0000000..cfc2182
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivity.java
@@ -0,0 +1,156 @@
+package net.sf.taverna.t2.activities.interaction;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.sf.taverna.t2.activities.interaction.jetty.InteractionJetty;
+import net.sf.taverna.t2.activities.interaction.preference.InteractionPreference;
+import net.sf.taverna.t2.activities.interaction.velocity.InteractionVelocity;
+import net.sf.taverna.t2.activities.interaction.velocity.NotifyChecker;
+import net.sf.taverna.t2.activities.interaction.velocity.ProduceChecker;
+import net.sf.taverna.t2.activities.interaction.velocity.RequireChecker;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workflowmodel.processor.activity.AbstractAsynchronousActivity;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityConfigurationException;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivity;
+import net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivityCallback;
+import net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityInputPortDefinitionBean;
+import net.sf.taverna.t2.workflowmodel.processor.activity.config.ActivityOutputPortDefinitionBean;
+
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.parser.node.ASTprocess;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public final class InteractionActivity extends
+		AbstractAsynchronousActivity<JsonNode>
+		implements AsynchronousActivity<JsonNode> {
+	
+	public static final String URI = "http://ns.taverna.org.uk/2010/activity/interaction";
+
+	@SuppressWarnings("unused")
+	private static final Logger logger = Logger
+			.getLogger(InteractionActivity.class);
+
+	private Template presentationTemplate;
+
+	private final Map<String, Integer> inputDepths = new HashMap<String, Integer>();
+	private final Map<String, Integer> outputDepths = new HashMap<String, Integer>();
+
+	private CredentialManager credentialManager;
+
+	private InteractionRecorder interactionRecorder;
+
+	private InteractionUtils interactionUtils;
+
+	private InteractionJetty interactionJetty;
+
+	private InteractionPreference interactionPreference;
+
+	private ResponseFeedListener responseFeedListener;
+
+	private JsonNode json;
+
+	private InteractionVelocity interactionVelocity;
+
+	public InteractionActivity(final CredentialManager credentialManager,
+			final InteractionRecorder interactionRecorder,
+			final InteractionUtils interactionUtils,
+			final InteractionJetty interactionJetty,
+			final InteractionPreference interactionPreference,
+			final ResponseFeedListener responseFeedListener,
+			final InteractionVelocity interactionVelocity) {
+		this.credentialManager = credentialManager;
+		this.interactionRecorder = interactionRecorder;
+		this.interactionUtils = interactionUtils;
+		this.interactionJetty = interactionJetty;
+		this.interactionPreference = interactionPreference;
+		this.responseFeedListener = responseFeedListener;
+		this.interactionVelocity = interactionVelocity;
+		this.json = null;
+	}
+
+	@Override
+	public void configure(final JsonNode json)
+			throws ActivityConfigurationException {
+		
+		this.json = json;
+	}
+
+	@Override
+	public void executeAsynch(final Map<String, T2Reference> inputs,
+			final AsynchronousActivityCallback callback) {
+		// Don't execute service directly now, request to be run ask to be run
+		// from thread pool and return asynchronously
+		final InteractionRequestor requestor = new InteractionCallbackRequestor(
+				this, callback, inputs);
+		callback.requestRun(new InteractionActivityRunnable(requestor,
+				this.presentationTemplate,
+				this.credentialManager,
+				this.interactionRecorder,
+				this.interactionUtils,
+				this.interactionJetty,
+				this.interactionPreference,
+				this.responseFeedListener,
+				this.interactionVelocity));
+	}
+
+	@Override
+	public JsonNode getConfiguration() {
+		return this.json;
+	}
+
+	public ActivityInputPort getInputPort(final String name) {
+		for (final ActivityInputPort port : this.getInputPorts()) {
+			if (port.getName().equals(name)) {
+				return port;
+			}
+		}
+		return null;
+	}
+
+	InteractionActivityType getInteractionActivityType() {
+		JsonNode subNode = json.get("interactivityActivityType");
+		if (subNode == null) {
+			return InteractionActivityType.LocallyPresentedHtml;
+		}
+		String textValue = subNode.textValue();
+		if (textValue == null) {
+			return InteractionActivityType.LocallyPresentedHtml;
+		}
+		if ("VelocityTemplate".equals(textValue)) {
+			return InteractionActivityType.VelocityTemplate;
+		}
+		return InteractionActivityType.LocallyPresentedHtml;
+	}
+	
+
+	 String getPresentationOrigin() {
+		JsonNode subNode = json.get("presentationOrigin");
+		if (subNode == null) {
+			return null;
+		}
+		String textValue = subNode.textValue();
+		if (textValue == null) {
+			return null;			
+		}
+		return textValue;
+	}
+
+	public boolean isProgressNotification() {
+		JsonNode subNode = json.get("progressNotification");
+		if (subNode == null) {
+			return false;
+		}
+		return subNode.booleanValue();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityFactory.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityFactory.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityFactory.java
new file mode 100644
index 0000000..911d860
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityFactory.java
@@ -0,0 +1,135 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.activities.interaction;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Set;
+
+import net.sf.taverna.t2.activities.interaction.jetty.InteractionJetty;
+import net.sf.taverna.t2.activities.interaction.preference.InteractionPreference;
+import net.sf.taverna.t2.activities.interaction.velocity.InteractionVelocity;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityConfigurationException;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionActivityFactory implements ActivityFactory {
+	
+	private CredentialManager credentialManager;
+	
+	private InteractionRecorder interactionRecorder;
+	
+	private InteractionUtils interactionUtils;
+
+	private InteractionJetty interactionJetty;
+
+	private InteractionPreference interactionPreference;
+
+	private ResponseFeedListener responseFeedListener;
+
+	private InteractionVelocity interactionVelocity;
+
+	/* (non-Javadoc)
+	 * @see net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory#createActivity()
+	 */
+	@Override
+	public InteractionActivity createActivity() {
+		return new InteractionActivity(this.credentialManager,
+				this.interactionRecorder,
+				this.interactionUtils,
+				this.interactionJetty,
+				this.interactionPreference,
+				this.responseFeedListener,
+				this.interactionVelocity);
+	}
+
+	/* (non-Javadoc)
+	 * @see net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory#getActivityType()
+	 */
+	@Override
+	public URI getActivityType() {
+		return URI.create(InteractionActivity.URI);
+	}
+
+	/* (non-Javadoc)
+	 * @see net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory#getActivityConfigurationSchema()
+	 */
+	@Override
+	public JsonNode getActivityConfigurationSchema() {
+		ObjectMapper objectMapper = new ObjectMapper();
+		try {
+			return objectMapper.readTree(getClass().getResource("/schema.json"));
+		} catch (IOException e) {
+			return objectMapper.createObjectNode();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory#getInputPorts(com.fasterxml.jackson.databind.JsonNode)
+	 */
+	@Override
+	public Set<ActivityInputPort> getInputPorts(JsonNode configuration)
+			throws ActivityConfigurationException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see net.sf.taverna.t2.workflowmodel.processor.activity.ActivityFactory#getOutputPorts(com.fasterxml.jackson.databind.JsonNode)
+	 */
+	@Override
+	public Set<ActivityOutputPort> getOutputPorts(JsonNode configuration)
+			throws ActivityConfigurationException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	/**
+	 * @return the credentialManager
+	 */
+	public CredentialManager getCredentialManager() {
+		return credentialManager;
+	}
+
+	/**
+	 * @param credentialManager the credentialManager to set
+	 */
+	public void setCredentialManager(CredentialManager credentialManager) {
+		this.credentialManager = credentialManager;
+	}
+
+	public void setInteractionRecorder(InteractionRecorder interactionRecorder) {
+		this.interactionRecorder = interactionRecorder;
+	}
+
+	public void setInteractionUtils(InteractionUtils interactionUtils) {
+		this.interactionUtils = interactionUtils;
+	}
+
+	public void setInteractionJetty(InteractionJetty interactionJetty) {
+		this.interactionJetty = interactionJetty;
+	}
+
+	public void setInteractionPreference(InteractionPreference interactionPreference) {
+		this.interactionPreference = interactionPreference;
+	}
+
+	public void setResponseFeedListener(ResponseFeedListener responseFeedListener) {
+		this.responseFeedListener = responseFeedListener;
+	}
+
+	public void setInteractionVelocity(InteractionVelocity interactionVelocity) {
+		this.interactionVelocity = interactionVelocity;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityHealthChecker.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityHealthChecker.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityHealthChecker.java
new file mode 100644
index 0000000..555fe6a
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityHealthChecker.java
@@ -0,0 +1,39 @@
+package net.sf.taverna.t2.activities.interaction;
+
+import java.util.List;
+
+import net.sf.taverna.t2.visit.VisitReport;
+import net.sf.taverna.t2.workflowmodel.health.HealthChecker;
+import net.sf.taverna.t2.workflowmodel.health.RemoteHealthChecker;
+
+/**
+ * Example health checker
+ * 
+ */
+public class InteractionActivityHealthChecker implements
+		HealthChecker<InteractionActivity> {
+
+	@Override
+	public boolean canVisit(final Object o) {
+		return o instanceof InteractionActivity;
+	}
+
+	@Override
+	public boolean isTimeConsuming() {
+		return true;
+	}
+
+	@Override
+	public VisitReport visit(final InteractionActivity activity,
+			final List<Object> ancestry) {
+
+		if (activity.getInteractionActivityType().equals(
+				InteractionActivityType.LocallyPresentedHtml)) {
+			return RemoteHealthChecker.contactEndpoint(activity,
+					activity.getPresentationOrigin());
+		}
+
+		return null;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityRunnable.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityRunnable.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityRunnable.java
new file mode 100644
index 0000000..342eeda
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityRunnable.java
@@ -0,0 +1,329 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.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 net.sf.taverna.t2.activities.interaction.atom.AtomUtils;
+import net.sf.taverna.t2.activities.interaction.jetty.InteractionJetty;
+import net.sf.taverna.t2.activities.interaction.preference.InteractionPreference;
+import net.sf.taverna.t2.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/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityType.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityType.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityType.java
new file mode 100644
index 0000000..63bc001
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionActivityType.java
@@ -0,0 +1,15 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.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/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionCallbackRequestor.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionCallbackRequestor.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionCallbackRequestor.java
new file mode 100644
index 0000000..f7445a8
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionCallbackRequestor.java
@@ -0,0 +1,193 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.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/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRecorder.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRecorder.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRecorder.java
new file mode 100644
index 0000000..66b2d38
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRecorder.java
@@ -0,0 +1,163 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.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/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRequestor.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRequestor.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRequestor.java
new file mode 100644
index 0000000..7eff950
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRequestor.java
@@ -0,0 +1,38 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.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/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRunDeletionListener.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRunDeletionListener.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRunDeletionListener.java
new file mode 100644
index 0000000..880ff82
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionRunDeletionListener.java
@@ -0,0 +1,31 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.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/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionType.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionType.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionType.java
new file mode 100644
index 0000000..1e42b4a
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionType.java
@@ -0,0 +1,14 @@
+/**
+ *
+ */
+package net.sf.taverna.t2.activities.interaction;
+
+/**
+ * @author alanrw
+ * 
+ */
+public enum InteractionType {
+
+	DataRequest, Notification, SecurityRequest, AuthenticationRequest
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/95509a51/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionUtils.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionUtils.java b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionUtils.java
new file mode 100644
index 0000000..149f842
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/net/sf/taverna/t2/activities/interaction/InteractionUtils.java
@@ -0,0 +1,127 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.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 net.sf.taverna.t2.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;
+	}
+}