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;
+ }
+}