You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by re...@apache.org on 2015/03/19 14:43:57 UTC
[29/35] incubator-taverna-common-activities git commit: package names
changed to org.apache.taverna.*
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
new file mode 100644
index 0000000..346d920
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityRunnable.java
@@ -0,0 +1,345 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.taverna.activities.interaction.atom.AtomUtils;
+import org.apache.taverna.activities.interaction.jetty.InteractionJetty;
+import org.apache.taverna.activities.interaction.preference.InteractionPreference;
+import org.apache.taverna.activities.interaction.velocity.InteractionVelocity;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.i18n.text.Normalizer;
+import org.apache.abdera.i18n.text.Sanitizer;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.parser.stax.FOMElement;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+
+public final class InteractionActivityRunnable implements Runnable {
+
+ private static final Logger logger = Logger
+ .getLogger(InteractionActivityRunnable.class);
+
+ private static final Abdera ABDERA = Abdera.getInstance();
+
+ private final Template presentationTemplate;
+
+ private final InteractionRequestor requestor;
+
+ private CredentialManager credentialManager;
+
+ private InteractionRecorder interactionRecorder;
+
+ private InteractionUtils interactionUtils;
+
+ private InteractionJetty interactionJetty;
+
+ private InteractionPreference interactionPreference;
+
+ private ResponseFeedListener responseFeedListener;
+
+ private InteractionVelocity interactionVelocity;
+
+ public InteractionActivityRunnable(final InteractionRequestor requestor,
+ final Template presentationTemplate,
+ final CredentialManager credentialManager,
+ final InteractionRecorder interactionRecorder,
+ final InteractionUtils interactionUtils,
+ final InteractionJetty interactionJetty,
+ final InteractionPreference interactionPreference,
+ final ResponseFeedListener responseFeedListener,
+ final InteractionVelocity interactionVelocity) {
+ this.requestor = requestor;
+ this.presentationTemplate = presentationTemplate;
+ this.credentialManager = credentialManager;
+ this.interactionRecorder = interactionRecorder;
+ this.interactionUtils = interactionUtils;
+ this.interactionJetty = interactionJetty;
+ this.interactionPreference = interactionPreference;
+ this.responseFeedListener = responseFeedListener;
+ this.interactionVelocity = interactionVelocity;
+ }
+
+ @Override
+ public void run() {
+ /*
+ * InvocationContext context = callback.getContext();
+ */
+ final String runId = InteractionUtils.getUsedRunId(this.requestor
+ .getRunId());
+
+ final String id = Sanitizer.sanitize(UUID.randomUUID().toString(), "",
+ true, Normalizer.Form.D);
+
+ final Map<String, Object> inputData = this.requestor.getInputData();
+
+ if (interactionPreference.getUseJetty()) {
+ interactionJetty.startJettyIfNecessary(credentialManager);
+ }
+ interactionJetty.startListenersIfNecessary();
+ try {
+ interactionUtils.copyFixedFile("pmrpc.js");
+ interactionUtils.copyFixedFile("interaction.css");
+ } catch (final IOException e1) {
+ logger.error(e1);
+ this.requestor.fail("Unable to copy necessary fixed file");
+ return;
+ }
+ synchronized (ABDERA) {
+ final Entry interactionNotificationMessage = this
+ .createBasicInteractionMessage(id, runId);
+
+ for (final String key : inputData.keySet()) {
+ final Object value = inputData.get(key);
+ if (value instanceof byte[]) {
+ final String replacementUrl = interactionPreference
+ .getPublicationUrlString(id, key);
+ final ByteArrayInputStream bais = new ByteArrayInputStream(
+ (byte[]) value);
+ try {
+ interactionUtils.publishFile(replacementUrl, bais,
+ runId, id);
+ bais.close();
+ inputData.put(key, replacementUrl);
+ } catch (final IOException e) {
+ logger.error(e);
+ this.requestor.fail("Unable to publish to " + replacementUrl);
+ return;
+ }
+ }
+ }
+
+ final String inputDataString = this.createInputDataJson(inputData);
+ if (inputDataString == null) {
+ return;
+ }
+ final String inputDataUrl = interactionPreference
+ .getInputDataUrlString(id);
+ try {
+ interactionUtils.publishFile(inputDataUrl, inputDataString,
+ runId, id);
+ } catch (final IOException e) {
+ logger.error(e);
+ this.requestor.fail("Unable to publish to " + inputDataUrl);
+ return;
+ }
+
+ String outputDataUrl = null;
+
+ if (!this.requestor.getInteractionType().equals(
+ InteractionType.Notification)) {
+ outputDataUrl = interactionPreference
+ .getOutputDataUrlString(id);
+ }
+ final String interactionUrlString = this.generateHtml(inputDataUrl,
+ outputDataUrl, inputData, runId, id);
+
+ try {
+ this.postInteractionMessage(id, interactionNotificationMessage,
+ interactionUrlString, runId);
+ } catch (IOException e) {
+ logger.error(e);
+ this.requestor.fail("Unable to post message");
+ return;
+ }
+ if (!this.requestor.getInteractionType().equals(
+ InteractionType.Notification)) {
+ responseFeedListener.registerInteraction(
+ interactionNotificationMessage, this.requestor);
+ } else {
+ this.requestor.carryOn();
+
+ }
+ }
+ }
+
+ private String createInputDataJson(final Map<String, Object> inputData) {
+ try {
+ return InteractionUtils.objectToJson(inputData);
+ } catch (final IOException e) {
+ logger.error(e);
+ this.requestor.fail("Unable to generate JSON");
+ }
+ return null;
+ }
+
+ private Entry createBasicInteractionMessage(final String id,
+ final String runId) {
+ final Entry interactionNotificationMessage = ABDERA.newEntry();
+
+ interactionNotificationMessage.setId(id);
+ final Date timestamp = new Date();
+ interactionNotificationMessage.setPublished(timestamp);
+ interactionNotificationMessage.setUpdated(timestamp);
+
+ interactionNotificationMessage.addAuthor("Taverna");
+ interactionNotificationMessage.setTitle("Interaction from Taverna for "
+ + this.requestor.generateId());
+
+ final Element runIdElement = interactionNotificationMessage
+ .addExtension(AtomUtils.getRunIdQName());
+ runIdElement.setText(StringEscapeUtils.escapeJavaScript(runId));
+
+ final Element pathIdElement = interactionNotificationMessage.addExtension(AtomUtils.getPathIdQName());
+ pathIdElement.setText(StringEscapeUtils.escapeJavaScript(this.requestor.getPath()));
+
+ final Element countElement = interactionNotificationMessage.addExtension(AtomUtils.getCountQName());
+ countElement.setText(StringEscapeUtils.escapeJavaScript(this.requestor.getInvocationCount().toString()));
+
+ if (this.requestor.getInteractionType().equals(
+ InteractionType.Notification)) {
+ interactionNotificationMessage.addExtension(AtomUtils
+ .getProgressQName());
+ }
+ final Element idElement = interactionNotificationMessage
+ .addExtension(AtomUtils.getIdQName());
+ idElement.setText(id);
+
+ return interactionNotificationMessage;
+ }
+
+ private void postInteractionMessage(final String id, final Entry entry,
+ final String interactionUrlString, final String runId) throws IOException {
+
+ entry.addLink(StringEscapeUtils.escapeXml(interactionUrlString),
+ "presentation");
+ entry.setContentAsXhtml("<p><a href=\""
+ + StringEscapeUtils.escapeXml(interactionUrlString)
+ + "\">Open: "
+ + StringEscapeUtils.escapeXml(interactionUrlString)
+ + "</a></p>");
+
+ URL feedUrl;
+
+ feedUrl = new URL(interactionPreference
+ .getFeedUrlString());
+ final String entryContent = ((FOMElement) entry)
+ .toFormattedString();
+ final HttpURLConnection httpCon = (HttpURLConnection) feedUrl
+ .openConnection();
+ httpCon.setDoOutput(true);
+ httpCon.setRequestProperty("Content-Type",
+ "application/atom+xml;type=entry;charset=UTF-8");
+ httpCon.setRequestProperty("Content-Length",
+ "" + entryContent.length());
+ httpCon.setRequestProperty("Slug", id);
+ httpCon.setRequestMethod("POST");
+ httpCon.setConnectTimeout(5000);
+ final OutputStream outputStream = httpCon.getOutputStream();
+ IOUtils.write(entryContent, outputStream, "UTF-8");
+ outputStream.close();
+ final int response = httpCon.getResponseCode();
+ if ((response < 0) || (response >= 400)) {
+ logger.error("Received response code" + response);
+ throw (new IOException ("Received response code " + response));
+ }
+ if (response == HttpURLConnection.HTTP_CREATED) {
+ interactionRecorder.addResource(runId, id,
+ httpCon.getHeaderField("Location"));
+ }
+ }
+
+ String generateHtml(final String inputDataUrl, final String outputDataUrl,
+ final Map<String, Object> inputData, final String runId,
+ final String id) {
+
+ final VelocityContext velocityContext = new VelocityContext();
+
+ for (final String inputName : inputData.keySet()) {
+ final Object input = inputData.get(inputName);
+ velocityContext.put(inputName, input);
+ }
+
+ velocityContext.put("feed", interactionPreference
+ .getFeedUrlString());
+ velocityContext.put("runId", runId);
+ velocityContext.put("entryId", id);
+ final String pmrpcUrl = interactionPreference
+ .getLocationUrl() + "/pmrpc.js";
+ velocityContext.put("pmrpcUrl", pmrpcUrl);
+ velocityContext.put("inputDataUrl", inputDataUrl);
+ velocityContext.put("outputDataUrl", outputDataUrl);
+ final String interactionUrl = interactionPreference
+ .getInteractionUrlString(id);
+
+ velocityContext.put("interactionUrl", interactionUrl);
+
+ String presentationUrl = "";
+ final String authorizeUrl = "";
+ try {
+ if (this.requestor.getPresentationType().equals(
+ InteractionActivityType.VelocityTemplate)) {
+
+ presentationUrl = interactionPreference
+ .getPresentationUrlString(id);
+
+ final String presentationString = this.processTemplate(
+ this.presentationTemplate, velocityContext);
+ interactionUtils.publishFile(presentationUrl,
+ presentationString, runId, id);
+
+ } else if (this.requestor.getPresentationType().equals(
+ InteractionActivityType.LocallyPresentedHtml)) {
+ presentationUrl = this.requestor.getPresentationOrigin();
+ }
+
+ velocityContext.put("presentationUrl", presentationUrl);
+
+ final String interactionString = this.processTemplate(
+ interactionVelocity.getInteractionTemplate(),
+ velocityContext);
+ interactionUtils.publishFile(interactionUrl, interactionString,
+ runId, id);
+
+ if (!authorizeUrl.isEmpty()) {
+ return authorizeUrl;
+ }
+ return interactionUrl;
+ } catch (final IOException e) {
+ logger.error(e);
+ this.requestor.fail("Unable to generate HTML");
+ return null;
+ }
+ }
+
+ private String processTemplate(final Template template,
+ final VelocityContext context) throws IOException {
+ final StringWriter resultWriter = new StringWriter();
+ template.merge(context, resultWriter);
+ resultWriter.close();
+ return resultWriter.toString();
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
new file mode 100644
index 0000000..11422b3
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionActivityType.java
@@ -0,0 +1,31 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+/**
+ * @author alanrw
+ *
+ * Should be renamed something like presentation type
+ */
+public enum InteractionActivityType {
+
+ VelocityTemplate, LocallyPresentedHtml
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
new file mode 100644
index 0000000..3c8c83b
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionCallbackRequestor.java
@@ -0,0 +1,209 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import net.sf.taverna.t2.invocation.InvocationContext;
+import net.sf.taverna.t2.reference.ReferenceService;
+import net.sf.taverna.t2.reference.T2Reference;
+import net.sf.taverna.t2.reference.WorkflowRunIdEntity;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityOutputPort;
+import net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivityCallback;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionCallbackRequestor implements InteractionRequestor {
+
+ private final AsynchronousActivityCallback callback;
+
+ private final Map<String, T2Reference> inputs;
+
+ private final InteractionActivity activity;
+
+ private boolean answered = false;
+
+ private String path;
+
+ private Integer count;
+
+ private static Map<String, Integer> invocationCount = new HashMap<String, Integer> ();
+
+ public InteractionCallbackRequestor(final InteractionActivity activity,
+ final AsynchronousActivityCallback callback,
+ final Map<String, T2Reference> inputs) {
+ this.activity = activity;
+ this.callback = callback;
+ this.inputs = inputs;
+ this.path = calculatePath();
+ this.count = calculateInvocationCount(path);
+ }
+
+ @Override
+ public String getRunId() {
+ return this.callback.getContext()
+ .getEntities(WorkflowRunIdEntity.class).get(0)
+ .getWorkflowRunId();
+ }
+
+ @Override
+ public Map<String, Object> getInputData() {
+ final Map<String, Object> inputData = new HashMap<String, Object>();
+
+ final InvocationContext context = this.callback.getContext();
+ final ReferenceService referenceService = context.getReferenceService();
+ for (final String inputName : this.inputs.keySet()) {
+ final Object input = referenceService.renderIdentifier(this.inputs
+ .get(inputName), this.getInputPort(inputName)
+ .getTranslatedElementClass(), this.callback.getContext());
+ inputData.put(inputName, input);
+ }
+ return inputData;
+ }
+
+ public ActivityInputPort getInputPort(final String name) {
+ for (final ActivityInputPort port : this.activity.getInputPorts()) {
+ if (port.getName().equals(name)) {
+ return port;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void fail(final String string) {
+ if (this.answered) {
+ return;
+ }
+ this.callback.fail(string);
+ this.answered = true;
+ }
+
+ @Override
+ public void carryOn() {
+ if (this.answered) {
+ return;
+ }
+ this.callback.receiveResult(new HashMap<String, T2Reference>(),
+ new int[0]);
+ this.answered = true;
+ }
+
+ @Override
+ public String generateId() {
+ final String workflowRunId = getRunId();
+ final String parentProcessIdentifier = this.callback
+ .getParentProcessIdentifier();
+ return (workflowRunId + ":" + parentProcessIdentifier);
+ }
+
+ @Override
+ public InteractionType getInteractionType() {
+ if (this.activity.isProgressNotification()) {
+ return InteractionType.Notification;
+ }
+ return InteractionType.DataRequest;
+ }
+
+ @Override
+ public InteractionActivityType getPresentationType() {
+ return this.activity.getInteractionActivityType();
+ }
+
+ @Override
+ public String getPresentationOrigin() {
+ return this.activity.getPresentationOrigin();
+ }
+
+ @Override
+ public void receiveResult(final Map<String, Object> resultMap) {
+ if (this.answered) {
+ return;
+ }
+ final Map<String, T2Reference> outputs = new HashMap<String, T2Reference>();
+
+ final InvocationContext context = this.callback.getContext();
+ final ReferenceService referenceService = context.getReferenceService();
+
+ for (final Object key : resultMap.keySet()) {
+ final String keyString = (String) key;
+ final Object value = resultMap.get(key);
+ final Integer depth = this.findPortDepth(keyString);
+ if (depth == null) {
+ this.callback.fail("Data sent for unknown port : " + keyString);
+ }
+ outputs.put(keyString,
+ referenceService.register(value, depth, true, context));
+ }
+ this.callback.receiveResult(outputs, new int[0]);
+ this.answered = true;
+ }
+
+ private Integer findPortDepth(final String portName) {
+ final Set<ActivityOutputPort> ports = this.activity.getOutputPorts();
+ for (final ActivityOutputPort op : ports) {
+ if (op.getName().equals(portName)) {
+ return op.getDepth();
+ }
+ }
+ return null;
+ }
+
+ private String calculatePath() {
+ final String parentProcessIdentifier = this.callback
+ .getParentProcessIdentifier();
+ String result = "";
+ String parts[] = parentProcessIdentifier.split(":");
+
+ for (int i = 2; i < parts.length; i += 4) {
+ if (!result.isEmpty()) {
+ result += ":";
+ }
+ result += parts[i];
+ }
+ return result;
+ }
+
+ @Override
+ public String getPath() {
+ return this.path;
+ }
+
+ private synchronized static Integer calculateInvocationCount(String path) {
+ Integer currentCount = invocationCount.get(path);
+ if (currentCount == null) {
+ currentCount = Integer.valueOf(0);
+ } else {
+ currentCount = currentCount + 1;
+ }
+ invocationCount.put(path, currentCount);
+ return currentCount;
+ }
+
+ @Override
+ public Integer getInvocationCount() {
+ return count;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
new file mode 100644
index 0000000..140d209
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRecorder.java
@@ -0,0 +1,179 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ *
+ * This class is used to remember and forget interactions and their associated
+ * ATOM entries and files
+ *
+ * @author alanrw
+ *
+ */
+public class InteractionRecorder {
+
+ private static final Logger logger = Logger
+ .getLogger(InteractionRecorder.class);
+
+ static Map<String, Map<String, Set<String>>> runToInteractionMap = Collections
+ .synchronizedMap(new HashMap<String, Map<String, Set<String>>>());
+
+ private InteractionUtils interactionUtils;
+
+ private InteractionRecorder() {
+ super();
+ }
+
+ public void deleteRun(final String runToDelete) {
+ final Set<String> interactionIds = new HashSet<String>(
+ getInteractionMap(runToDelete).keySet());
+ for (final String interactionId : interactionIds) {
+ deleteInteraction(runToDelete, interactionId);
+ }
+ runToInteractionMap.remove(runToDelete);
+ }
+
+ public void deleteInteraction(final String runId,
+ final String interactionId) {
+ for (final String urlString : getResourceSet(runId, interactionId)) {
+ try {
+ deleteUrl(urlString);
+ } catch (final IOException e) {
+ logger.info("Unable to delete " + urlString, e);
+ }
+
+ }
+ getInteractionMap(runId).remove(interactionId);
+ }
+
+ private void deleteUrl(final String urlString) throws IOException {
+ logger.info("Deleting resource " + urlString);
+ final URL url = new URL(urlString);
+ final HttpURLConnection httpCon = (HttpURLConnection) url
+ .openConnection();
+ httpCon.setRequestMethod("DELETE");
+ final int response = httpCon.getResponseCode();
+ if (response >= 400) {
+ logger.info("Received response code" + response);
+ }
+ }
+
+ public void addResource(final String runId,
+ final String interactionId, final String resourceId) {
+ if (resourceId == null) {
+ logger.error("Attempt to add null resource",
+ new NullPointerException(""));
+ return;
+ }
+ logger.info("Adding resource " + resourceId);
+ final Set<String> resourceSet = getResourceSet(runId, interactionId);
+
+ resourceSet.add(resourceId);
+ }
+
+ private Set<String> getResourceSet(final String runId,
+ final String interactionId) {
+ final Map<String, Set<String>> interactionMap = getInteractionMap(runId);
+ Set<String> resourceSet = interactionMap.get(interactionId);
+ if (resourceSet == null) {
+ resourceSet = Collections.synchronizedSet(new HashSet<String>());
+ interactionMap.put(interactionId, resourceSet);
+ }
+ return resourceSet;
+ }
+
+ private Map<String, Set<String>> getInteractionMap(final String runId) {
+ Map<String, Set<String>> interactionMap = InteractionRecorder.runToInteractionMap
+ .get(runId);
+ if (interactionMap == null) {
+ interactionMap = Collections.synchronizedMap(Collections
+ .synchronizedMap(new HashMap<String, Set<String>>()));
+ InteractionRecorder.runToInteractionMap.put(runId, interactionMap);
+ }
+ return interactionMap;
+ }
+
+ public void persist() {
+ final File outputFile = getUsageFile();
+ try {
+ FileUtils.writeStringToFile(outputFile, InteractionUtils
+ .objectToJson(InteractionRecorder.runToInteractionMap));
+ } catch (final IOException e) {
+ logger.error(e);
+ }
+ }
+
+ private File getUsageFile() {
+ return new File(getInteractionUtils().getInteractionServiceDirectory(),
+ "usage");
+ }
+
+ public void load() {
+ final File inputFile = getUsageFile();
+ try {
+ final String usageString = FileUtils.readFileToString(inputFile);
+ final ObjectMapper mapper = new ObjectMapper();
+ @SuppressWarnings("unchecked")
+ final Map<String, Object> rootAsMap = mapper.readValue(usageString,
+ Map.class);
+ InteractionRecorder.runToInteractionMap.clear();
+ for (final String runId : rootAsMap.keySet()) {
+ @SuppressWarnings("unchecked")
+ final Map<String, Object> runMap = (Map<String, Object>) rootAsMap
+ .get(runId);
+ for (final String interactionId : runMap.keySet()) {
+ @SuppressWarnings("unchecked")
+ final List<String> urlList = (List<String>) runMap
+ .get(interactionId);
+ for (final String url : urlList) {
+ addResource(runId, interactionId, url);
+ }
+ }
+ }
+ } catch (final IOException e) {
+ logger.info(e);
+ }
+ }
+
+ public InteractionUtils getInteractionUtils() {
+ return interactionUtils;
+ }
+
+ public void setInteractionUtils(InteractionUtils interactionUtils) {
+ this.interactionUtils = interactionUtils;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
new file mode 100644
index 0000000..7826a59
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRequestor.java
@@ -0,0 +1,54 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.util.Map;
+
+/**
+ * @author alanrw
+ *
+ */
+public interface InteractionRequestor {
+
+ String getRunId();
+
+ Map<String, Object> getInputData();
+
+ void fail(String string);
+
+ void carryOn();
+
+ String generateId();
+
+ // The path to whatever requested the interaction
+ String getPath();
+
+ // The number of times whatever requested the interaction has requested one
+ Integer getInvocationCount();
+
+ InteractionActivityType getPresentationType();
+
+ InteractionType getInteractionType();
+
+ String getPresentationOrigin();
+
+ void receiveResult(Map<String, Object> resultMap);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
new file mode 100644
index 0000000..07d8db0
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionRunDeletionListener.java
@@ -0,0 +1,47 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import net.sf.taverna.t2.workflowmodel.RunDeletionListener;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionRunDeletionListener implements RunDeletionListener {
+
+ private InteractionRecorder interactionRecorder;
+
+ @SuppressWarnings("unused")
+ private static final Logger logger = Logger
+ .getLogger(InteractionRunDeletionListener.class);
+
+ @Override
+ public void deleteRun(final String runToDelete) {
+ interactionRecorder.deleteRun(runToDelete);
+ }
+
+ public void setInteractionRecorder(InteractionRecorder interactionRecorder) {
+ this.interactionRecorder = interactionRecorder;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
new file mode 100644
index 0000000..c5549e2
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionType.java
@@ -0,0 +1,30 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+/**
+ * @author alanrw
+ *
+ */
+public enum InteractionType {
+
+ DataRequest, Notification, SecurityRequest, AuthenticationRequest
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
new file mode 100644
index 0000000..7165e76
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/InteractionUtils.java
@@ -0,0 +1,143 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+// import net.sf.taverna.raven.appconfig.ApplicationRuntime;
+import org.apache.taverna.activities.interaction.preference.InteractionPreference;
+
+import org.apache.commons.io.IOUtils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionUtils {
+
+ static final Set<String> publishedUrls = Collections
+ .synchronizedSet(new HashSet<String>());
+
+ private ApplicationConfiguration appConfig;
+
+ private InteractionRecorder interactionRecorder;
+
+ private InteractionPreference interactionPreference;
+
+ private InteractionUtils() {
+ super();
+ }
+
+ protected void copyFixedFile(final String fixedFileName)
+ throws IOException {
+ final String targetUrl = interactionPreference
+ .getLocationUrl() + "/" + fixedFileName;
+ this.publishFile(
+ targetUrl,
+ InteractionActivity.class.getResourceAsStream("/"
+ + fixedFileName), null, null);
+ }
+
+ public void publishFile(final String urlString,
+ final String contents, final String runId,
+ final String interactionId) throws IOException {
+ final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ contents.getBytes("UTF-8"));
+ this.publishFile(urlString, byteArrayInputStream, runId,
+ interactionId);
+ }
+
+ void publishFile(final String urlString, final InputStream is,
+ final String runId, final String interactionId) throws IOException {
+ if (InteractionUtils.publishedUrls.contains(urlString)) {
+ return;
+ }
+ InteractionUtils.publishedUrls.add(urlString);
+ if (runId != null) {
+ interactionRecorder.addResource(runId, interactionId, urlString);
+ }
+
+ final URL url = new URL(urlString);
+ final HttpURLConnection httpCon = (HttpURLConnection) url
+ .openConnection();
+ httpCon.setDoOutput(true);
+ httpCon.setRequestMethod("PUT");
+ final OutputStream outputStream = httpCon.getOutputStream();
+ IOUtils.copy(is, outputStream);
+ is.close();
+ outputStream.close();
+ int code = httpCon.getResponseCode();
+ if ((code >= 400) || (code < 0)){
+ throw new IOException ("Received code " + code);
+ }
+ }
+
+ public static String getUsedRunId(final String engineRunId) {
+ String runId = engineRunId;
+ final String specifiedId = System.getProperty("taverna.runid");
+ if (specifiedId != null) {
+ runId = specifiedId;
+ }
+ return runId;
+ }
+
+ public File getInteractionServiceDirectory() {
+ final File workingDir = appConfig
+ .getApplicationHomeDir();
+ final File interactionServiceDirectory = new File(workingDir,
+ "interactionService");
+ interactionServiceDirectory.mkdirs();
+ return interactionServiceDirectory;
+ }
+
+ public static String objectToJson(final Object o) throws IOException {
+ final ObjectMapper mapper = new ObjectMapper();
+ final StringWriter sw = new StringWriter();
+ mapper.writeValue(sw, o);
+ final String theString = sw.toString();
+ return theString;
+ }
+
+ public void setAppConfig(ApplicationConfiguration appConfig) {
+ this.appConfig = appConfig;
+ }
+
+ public void setInteractionRecorder(InteractionRecorder interactionRecorder) {
+ this.interactionRecorder = interactionRecorder;
+ }
+
+ public void setInteractionPreference(InteractionPreference interactionPreference) {
+ this.interactionPreference = interactionPreference;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
new file mode 100644
index 0000000..9afd253
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/ResponseFeedListener.java
@@ -0,0 +1,175 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.taverna.activities.interaction.atom.AtomUtils;
+import org.apache.taverna.activities.interaction.preference.InteractionPreference;
+
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author alanrw
+ *
+ */
+public final class ResponseFeedListener extends FeedReader {
+
+ private InteractionRecorder interactionRecorder;
+
+ private InteractionPreference interactionPreference;
+
+ private static final String STATUS_OK = "OK";
+
+ private static final String DATA_READ_FAILED = "Data read failed";
+
+ private static ResponseFeedListener instance;
+
+ private static final Logger logger = Logger.getLogger(ResponseFeedListener.class);
+
+ private static final Map<String, InteractionRequestor> requestorMap = new HashMap<String, InteractionRequestor>();
+
+ private ResponseFeedListener() {
+ super("ResponseFeedListener");
+ }
+
+ @Override
+ protected void considerEntry(final Entry entry) {
+ synchronized (requestorMap) {
+ final String refString = getReplyTo(entry);
+ if (refString == null) {
+ return;
+ }
+ final String runId = getRunId(entry);
+
+ final String entryUrl = interactionPreference
+ .getFeedUrlString() + "/" + entry.getId().toASCIIString();
+ interactionRecorder.addResource(runId, refString, entryUrl);
+
+ if (requestorMap.containsKey(refString)) {
+
+ final InteractionRequestor requestor = requestorMap
+ .get(refString);
+
+ final Element statusElement = entry.getExtension(AtomUtils
+ .getResultStatusQName());
+ final String statusContent = statusElement.getText().trim();
+ if (!statusContent.equals(STATUS_OK)) {
+ cleanup(refString);
+ requestor.fail(statusContent);
+ return;
+ }
+ final String outputDataUrl = interactionPreference
+ .getOutputDataUrlString(refString);
+ // Note that this may not really exist
+ interactionRecorder
+ .addResource(runId, refString, outputDataUrl);
+ String content = null;
+ InputStream iStream;
+ try {
+ iStream = new URL(outputDataUrl).openStream();
+ content = IOUtils.toString(iStream);
+ iStream.close();
+ } catch (final MalformedURLException e1) {
+ logger.error(e1);
+ requestor.fail(DATA_READ_FAILED);
+ return;
+ } catch (final IOException e1) {
+ logger.error(e1);
+ requestor.fail(DATA_READ_FAILED);
+ return;
+ }
+
+ try {
+ final ObjectMapper mapper = new ObjectMapper();
+ @SuppressWarnings("unchecked")
+ final Map<String, Object> rootAsMap = mapper.readValue(
+ content, Map.class);
+ requestor.receiveResult(rootAsMap);
+ cleanup(refString);
+ interactionRecorder.deleteInteraction(runId, refString);
+
+ } catch (final JsonParseException e) {
+ logger.error(e);
+ } catch (final IOException e) {
+ logger.error(e);
+ } catch (final Exception e) {
+ logger.error(e);
+ }
+
+ }
+ }
+ }
+
+ private static void cleanup(final String refString) {
+ requestorMap.remove(refString);
+ }
+
+ private static String getReplyTo(final Entry entry) {
+ final Element replyTo = entry.getFirstChild(AtomUtils
+ .getInReplyToQName());
+ if (replyTo == null) {
+ return null;
+ }
+ return replyTo.getText();
+ }
+
+ private static String getRunId(final Entry entry) {
+ final Element runIdElement = entry.getFirstChild(AtomUtils
+ .getRunIdQName());
+ if (runIdElement == null) {
+ return null;
+ }
+ return runIdElement.getText();
+ }
+
+ public void registerInteraction(final Entry entry,
+ final InteractionRequestor requestor) {
+ synchronized (requestorMap) {
+ final String refString = entry.getId().toString();
+ requestorMap.put(refString, requestor);
+ }
+ }
+
+ public void setInteractionRecorder(InteractionRecorder interactionRecorder) {
+ this.interactionRecorder = interactionRecorder;
+ }
+
+ public void setInteractionPreference(InteractionPreference interactionPreference) {
+ this.interactionPreference = interactionPreference;
+ }
+
+ @Override
+ protected InteractionPreference getInteractionPreference() {
+ return this.interactionPreference;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
new file mode 100644
index 0000000..1b8fcf2
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/atom/AtomUtils.java
@@ -0,0 +1,99 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.atom;
+
+import javax.xml.namespace.QName;
+
+/**
+ * @author alanrw
+ *
+ */
+public class AtomUtils {
+
+ private static QName inputDataQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "input-data",
+ "interaction");
+ private static QName resultDataQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "result-data",
+ "interaction");
+ private static QName resultStatusQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "result-status",
+ "interaction");
+ private static QName idQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "id", "interaction");
+ private static QName pathIdQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "path",
+ "interaction");
+ private static QName countQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "count",
+ "interaction");
+ private static QName runIdQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "run-id",
+ "interaction");
+ private static QName inReplyToQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "in-reply-to",
+ "interaction");
+ private static QName progressQName = new QName(
+ "http://ns.taverna.org.uk/2012/interaction", "progress",
+ "interaction");
+
+ public static QName getInputDataQName() {
+ return inputDataQName;
+ }
+
+ public static QName getIdQName() {
+ return idQName;
+ }
+
+ public static QName getInReplyToQName() {
+ return inReplyToQName;
+ }
+
+ public static QName getResultDataQName() {
+ return resultDataQName;
+ }
+
+ public static QName getResultStatusQName() {
+ return resultStatusQName;
+ }
+
+ /**
+ * @return the runIdQName
+ */
+ public static QName getRunIdQName() {
+ return runIdQName;
+ }
+
+ /**
+ * @return the progressQName
+ */
+ public static QName getProgressQName() {
+ return progressQName;
+ }
+
+ public static QName getPathIdQName() {
+ return pathIdQName;
+ }
+
+ public static QName getCountQName() {
+ return countQName;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
new file mode 100644
index 0000000..3f2295d
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/feed/ShowRequestFeedListener.java
@@ -0,0 +1,81 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.feed;
+
+import java.awt.Desktop;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.apache.taverna.activities.interaction.FeedReader;
+import org.apache.taverna.activities.interaction.preference.InteractionPreference;
+
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Link;
+import org.apache.log4j.Logger;
+
+/**
+ * @author alanrw
+ *
+ */
+public class ShowRequestFeedListener extends FeedReader {
+
+ private static ShowRequestFeedListener instance;
+
+ private static Logger logger = Logger
+ .getLogger(ShowRequestFeedListener.class);
+
+ private static final String ignore_requests_property = System.getProperty("taverna.interaction.ignore_requests");
+
+ private static boolean operational = (ignore_requests_property == null) || !Boolean.valueOf(ignore_requests_property);
+
+ private InteractionPreference interactionPreference;
+
+ private ShowRequestFeedListener() {
+ super("ShowRequestFeedListener");
+ }
+
+ @Override
+ protected void considerEntry(final Entry entry) {
+ if (!operational) {
+ return;
+ }
+ final Link presentationLink = entry.getLink("presentation");
+ if (presentationLink != null) {
+ try {
+ Desktop.getDesktop().browse(
+ presentationLink.getHref().toURI());
+ } catch (final IOException e) {
+ logger.error("Cannot open presentation");
+ } catch (final URISyntaxException e) {
+ logger.error("Cannot open presentation");
+ }
+ }
+ }
+
+ @Override
+ protected InteractionPreference getInteractionPreference() {
+ return this.interactionPreference;
+ }
+
+ public void setInteractionPreference(InteractionPreference interactionPreference) {
+ this.interactionPreference = interactionPreference;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
new file mode 100644
index 0000000..0729b23
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/HackedFilesystemAdapter.java
@@ -0,0 +1,264 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.jetty;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.i18n.templates.Template;
+import org.apache.abdera.i18n.text.Normalizer;
+import org.apache.abdera.i18n.text.Sanitizer;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.model.Link;
+import org.apache.abdera.protocol.server.ProviderHelper;
+import org.apache.abdera.protocol.server.RequestContext;
+import org.apache.abdera.protocol.server.ResponseContext;
+import org.apache.abdera.protocol.server.Target;
+import org.apache.abdera.protocol.server.provider.managed.FeedConfiguration;
+import org.apache.abdera.protocol.server.provider.managed.ManagedCollectionAdapter;
+
+/**
+ * Simple Filesystem Adapter that uses a local directory to store Atompub
+ * collection entries. As an extension of the ManagedCollectionAdapter class,
+ * the Adapter is intended to be used with implementations of the
+ * ManagedProvider and are configured using /abdera/adapter/*.properties files.
+ * The *.properties file MUST specify the fs.root property to specify the root
+ * directory used by the Adapter.
+ */
+public class HackedFilesystemAdapter extends ManagedCollectionAdapter {
+
+ private InteractionJetty interactionJetty;
+
+ private final File root;
+ private final static FileSorter sorter = new FileSorter();
+ private final static Template paging_template = new Template(
+ "?{-join|&|count,page}");
+
+ public HackedFilesystemAdapter(final Abdera abdera,
+ final FeedConfiguration config) {
+ super(abdera, config);
+ this.root = this.getRoot();
+ }
+
+ private File getRoot() {
+ return interactionJetty.getFeedDirectory();
+ }
+
+ private Entry getEntry(final File entryFile) {
+ if (!entryFile.exists() || !entryFile.isFile()) {
+ throw new RuntimeException();
+ }
+ try {
+ final FileInputStream fis = new FileInputStream(entryFile);
+ final Document<Entry> doc = this.abdera.getParser().parse(fis);
+ final Entry entry = doc.getRoot();
+ return entry;
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void addPagingLinks(final RequestContext request, final Feed feed,
+ final int currentpage, final int count) {
+ final Map<String, Object> params = new HashMap<String, Object>();
+ params.put("count", count);
+ params.put("page", currentpage + 1);
+ String next = paging_template.expand(params);
+ next = request.getResolvedUri().resolve(next).toString();
+ feed.addLink(next, "next");
+ if (currentpage > 0) {
+ params.put("page", currentpage - 1);
+ String prev = paging_template.expand(params);
+ prev = request.getResolvedUri().resolve(prev).toString();
+ feed.addLink(prev, "previous");
+ }
+ params.put("page", 0);
+ String current = paging_template.expand(params);
+ current = request.getResolvedUri().resolve(current).toString();
+ feed.addLink(current, "current");
+ }
+
+ private void getEntries(final RequestContext request, final Feed feed,
+ final File root) {
+ final File[] files = root.listFiles();
+ Arrays.sort(files, sorter);
+ final int length = ProviderHelper.getPageSize(request, "count", 25);
+ final int offset = ProviderHelper.getOffset(request, "page", length);
+ final String _page = request.getParameter("page");
+ final int page = (_page != null) ? Integer.parseInt(_page) : 0;
+ this.addPagingLinks(request, feed, page, length);
+ if (offset > files.length) {
+ return;
+ }
+ for (int n = offset; (n < (offset + length)) && (n < files.length); n++) {
+ final File file = files[n];
+ try {
+ final Entry entry = this.getEntry(file);
+ feed.addEntry((Entry) entry.clone());
+ } catch (final Exception e) {
+ // Do nothing
+ }
+ }
+ }
+
+ @Override
+ public ResponseContext getFeed(final RequestContext request) {
+ final Feed feed = this.abdera.newFeed();
+ feed.setId(this.config.getServerConfiguration().getServerUri() + "/"
+ + this.config.getFeedId());
+ feed.setTitle(this.config.getFeedTitle());
+ feed.addAuthor(this.config.getFeedAuthor());
+ feed.addLink(this.config.getFeedUri());
+ feed.addLink(this.config.getFeedUri(), "self");
+ feed.setUpdated(new Date());
+ this.getEntries(request, feed, this.root);
+ return ProviderHelper.returnBase(feed.getDocument(), 200, null);
+ }
+
+ @Override
+ public ResponseContext deleteEntry(final RequestContext request) {
+ final Target target = request.getTarget();
+ final String key = target.getParameter("entry");
+ final File file = this.getFile(key, false);
+ if (file.exists()) {
+ file.delete();
+ }
+ return ProviderHelper.nocontent();
+ }
+
+ @Override
+ public ResponseContext getEntry(final RequestContext request) {
+ final Target target = request.getTarget();
+ final String key = target.getParameter("entry");
+ final File file = this.getFile(key, false);
+ final Entry entry = this.getEntry(file);
+ if (entry != null) {
+ return ProviderHelper.returnBase(entry.getDocument(), 200, null);
+ } else {
+ return ProviderHelper.notfound(request);
+ }
+ }
+
+ @Override
+ public ResponseContext postEntry(final RequestContext request) {
+ if (request.isAtom()) {
+ try {
+ final Entry entry = (Entry) request.getDocument().getRoot()
+ .clone();
+ final String key = this.createKey(request);
+ this.setEditDetail(request, entry, key);
+ final File file = this.getFile(key);
+ final FileOutputStream out = new FileOutputStream(file);
+ entry.writeTo(out);
+ final String edit = entry.getEditLinkResolvedHref().toString();
+ return ProviderHelper
+ .returnBase(entry.getDocument(), 201, null)
+ .setLocation(edit);
+ } catch (final Exception e) {
+ return ProviderHelper.badrequest(request);
+ }
+ } else {
+ return ProviderHelper.notsupported(request);
+ }
+ }
+
+ private void setEditDetail(final RequestContext request, final Entry entry,
+ final String key) throws IOException {
+ final Target target = request.getTarget();
+ final String feed = target.getParameter("feed");
+ final String id = key;
+ entry.setEdited(new Date());
+ final Link link = entry.getEditLink();
+ final Map<String, Object> params = new HashMap<String, Object>();
+ params.put("feed", feed);
+ params.put("entry", id);
+ final String href = request.absoluteUrlFor("entry", params);
+ if (link == null) {
+ entry.addLink(href, "edit");
+ } else {
+ link.setHref(href);
+ }
+ }
+
+ private File getFile(final String key) {
+ return this.getFile(key, true);
+ }
+
+ private File getFile(final String key, final boolean post) {
+ final File file = new File(this.root, key);
+ if (post && file.exists()) {
+ throw new RuntimeException("File exists");
+ }
+ return file;
+ }
+
+ private String createKey(final RequestContext request) throws IOException {
+ String slug = request.getSlug();
+ if (slug == null) {
+ slug = ((Entry) request.getDocument().getRoot()).getTitle();
+ }
+ return Sanitizer.sanitize(slug, "", true, Normalizer.Form.D);
+ }
+
+ @Override
+ public ResponseContext putEntry(final RequestContext request) {
+ if (request.isAtom()) {
+ try {
+ final Entry entry = (Entry) request.getDocument().getRoot()
+ .clone();
+ final String key = request.getTarget().getParameter("entry");
+ this.setEditDetail(request, entry, key);
+ final File file = this.getFile(key, false);
+ final FileOutputStream out = new FileOutputStream(file);
+ entry.writeTo(out);
+ final String edit = entry.getEditLinkResolvedHref().toString();
+ return ProviderHelper
+ .returnBase(entry.getDocument(), 200, null)
+ .setLocation(edit);
+ } catch (final Exception e) {
+ return ProviderHelper.badrequest(request);
+ }
+ } else {
+ return ProviderHelper.notsupported(request);
+ }
+ }
+
+ private static class FileSorter implements Comparator<File> {
+ @Override
+ public int compare(final File o1, final File o2) {
+ return o1.lastModified() > o2.lastModified() ? -1 : o1
+ .lastModified() < o2.lastModified() ? 1 : 0;
+ }
+ }
+
+ public void setInteractionJetty(InteractionJetty interactionJetty) {
+ this.interactionJetty = interactionJetty;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
new file mode 100644
index 0000000..c5827ac
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/jetty/InteractionJetty.java
@@ -0,0 +1,219 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.jetty;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.taverna.activities.interaction.FeedReader;
+import org.apache.taverna.activities.interaction.InteractionUtils;
+import org.apache.taverna.activities.interaction.ResponseFeedListener;
+import org.apache.taverna.activities.interaction.feed.ShowRequestFeedListener;
+import org.apache.taverna.activities.interaction.preference.InteractionPreference;
+import net.sf.taverna.t2.security.credentialmanager.CMException;
+import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
+import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
+//import net.sf.taverna.t2.spi.SPIRegistry;
+import net.sf.webdav.WebdavServlet;
+
+import org.apache.abdera.protocol.server.ServiceManager;
+import org.apache.abdera.protocol.server.provider.basic.BasicProvider;
+import org.apache.abdera.protocol.server.servlet.AbderaServlet;
+import org.apache.log4j.Logger;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.HandlerList;
+import org.mortbay.jetty.security.Constraint;
+import org.mortbay.jetty.security.ConstraintMapping;
+import org.mortbay.jetty.security.HashUserRealm;
+import org.mortbay.jetty.security.SecurityHandler;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionJetty {
+
+ private static Logger logger = Logger.getLogger(InteractionJetty.class);
+
+ private InteractionUtils interactionUtils;
+
+ private ShowRequestFeedListener showRequestFeedListener;
+ private ResponseFeedListener responseFeedListener;
+
+ private InteractionPreference interactionPreference;
+
+ private static Server server;
+
+ private static String REALM_NAME = "TavernaInteraction";
+
+ private static boolean listenersStarted = false;
+
+ public synchronized void startJettyIfNecessary(CredentialManager credentialManager) {
+ if (server != null) {
+ return;
+ }
+
+// final ClassLoader previousContextClassLoader = Thread.currentThread()
+// .getContextClassLoader();
+// Thread.currentThread().setContextClassLoader(
+// InteractionJetty.class.getClassLoader());
+
+ final String port = interactionPreference.getPort();
+
+ server = new Server(Integer.parseInt(port));
+ server.setStopAtShutdown(true);
+
+ final WebdavServlet interactionServlet = new WebdavServlet();
+
+ final ServletHolder interactionHolder = new ServletHolder();
+ interactionHolder.setServlet(interactionServlet);
+
+ try {
+
+ interactionHolder.setInitParameter("rootpath",
+ getInteractionDirectory().getCanonicalPath());
+ } catch (final IOException e1) {
+ logger.error("Unable to set root of interaction", e1);
+ }
+
+ final HandlerList handlers = new HandlerList();
+ final Context overallContext = new Context(handlers, "/",
+ Context.SESSIONS);
+ overallContext.setContextPath("/");
+ server.setHandler(overallContext);
+
+ final AbderaServlet abderaServlet = new AbderaServlet();
+ final ServletHolder abderaHolder = new ServletHolder(abderaServlet);
+ abderaHolder.setInitParameter(ServiceManager.PROVIDER,
+ BasicProvider.class.getName());
+
+ overallContext.addServlet(abderaHolder, "/*");
+ overallContext.addServlet(interactionHolder, "/interaction/*");
+
+ if (interactionPreference.getUseUsername()) {
+ final Constraint constraint = new Constraint();
+ constraint.setName(Constraint.__BASIC_AUTH);
+
+ constraint.setRoles(new String[] { "user", "admin", "moderator" });
+ constraint.setAuthenticate(true);
+
+ final ConstraintMapping cm = new ConstraintMapping();
+ cm.setConstraint(constraint);
+ cm.setPathSpec("/*");
+
+ final SecurityHandler sh = new SecurityHandler();
+ try {
+ final HashUserRealm realm = new HashUserRealm(REALM_NAME);
+ final URI serviceURI = createServiceURI(port);
+ final UsernamePassword up = credentialManager
+ .getUsernameAndPasswordForService(serviceURI, true,
+ "Please specify the username and password to secure your interactions");
+ if (up != null) {
+ final String username = up.getUsername();
+ realm.put(username, up.getPasswordAsString());
+ realm.addUserToRole(username, "user");
+ }
+ sh.setUserRealm(realm);
+ } catch (final CMException e) {
+ logger.error(e);
+ } catch (final URISyntaxException e) {
+ logger.error(e);
+ }
+ sh.setConstraintMappings(new ConstraintMapping[] { cm });
+ overallContext.addHandler(sh);
+
+ }
+
+ getFeedDirectory();
+
+ try {
+ server.start();
+ while (!server.isRunning()) {
+ Thread.sleep(5000);
+ }
+ } catch (final Exception e) {
+ logger.error("Unable to start Jetty");
+ }
+// Thread.currentThread()
+// .setContextClassLoader(previousContextClassLoader);
+ }
+
+ public static URI createServiceURI(final String port)
+ throws URISyntaxException {
+ return new URI("http://localhost:" + port + "/#" + REALM_NAME);
+ }
+
+ public File getJettySubdirectory(final String subdirectoryName) {
+ final File workingDir = interactionUtils
+ .getInteractionServiceDirectory();
+ final File subDir = new File(workingDir, "jetty/" + subdirectoryName);
+ subDir.mkdirs();
+ return subDir;
+ }
+
+ public File getFeedDirectory() {
+ return getJettySubdirectory("feed");
+ }
+
+ public File getInteractionDirectory() {
+ return getJettySubdirectory("interaction");
+ }
+
+ public synchronized void startListenersIfNecessary() {
+ if (listenersStarted) {
+ return;
+ }
+ listenersStarted = true;
+ startListener(this.responseFeedListener);
+ startListener(showRequestFeedListener);
+
+ }
+
+ private void startListener(FeedReader fr) {
+ try {
+ fr.start();
+ }
+ catch (Exception e) {
+ logger.error("Failed to start " + fr.getClass().getCanonicalName(), e);
+ }
+ }
+
+ public void setInteractionUtils(InteractionUtils interactionUtils) {
+ this.interactionUtils = interactionUtils;
+ }
+
+ public void setShowRequestFeedListener(
+ ShowRequestFeedListener showRequestFeedListener) {
+ this.showRequestFeedListener = showRequestFeedListener;
+ }
+
+ public void setResponseFeedListener(ResponseFeedListener responseFeedListener) {
+ this.responseFeedListener = responseFeedListener;
+ }
+
+ public void setInteractionPreference(InteractionPreference interactionPreference) {
+ this.interactionPreference = interactionPreference;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
new file mode 100644
index 0000000..6b47368
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/preference/InteractionPreference.java
@@ -0,0 +1,284 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.preference;
+
+import java.awt.GraphicsEnvironment;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+
+import uk.org.taverna.configuration.app.ApplicationConfiguration;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionPreference {
+
+ private ApplicationConfiguration appConfig;
+
+ private static final String USE_JETTY = "useJetty";
+
+ private static final String DEFAULT_USE_JETTY = "true";
+
+ private static final String PORT = "port";
+
+ private static final String DEFAULT_PORT = "8080";
+
+ private static final String HOST = "host";
+
+ private static final String DEFAULT_HOST = "http://localhost";
+
+ private static final String WEBDAV_PATH = "webdavPath";
+
+ private static final String DEFAULT_WEBDAV_PATH = "/interaction";
+
+ private static final String FEED_PATH = "feedPath";
+
+ private static final String DEFAULT_FEED_PATH = "/feed";
+
+ private static final String USE_USERNAME = "Secure with username / password";
+
+ private static final String DEFAULT_USE_USERNAME = "false";
+
+ // private static final String USE_HTTPS = "Use HTTPS";
+
+ // private static final String DEFAULT_USE_HTTPS = "false";
+
+ private final Logger logger = Logger.getLogger(InteractionPreference.class);
+
+ private final Properties properties;
+
+ private File getConfigFile() {
+ final File home = appConfig
+ .getApplicationHomeDir();
+ final File config = new File(home, "conf");
+ if (!config.exists()) {
+ config.mkdir();
+ }
+ final File configFile = new File(config, this.getFilePrefix() + "-"
+ + this.getUUID() + ".config");
+ return configFile;
+ }
+
+ private InteractionPreference(ApplicationConfiguration appConfig) {
+ setAppConfig(appConfig);
+ final File configFile = this.getConfigFile();
+ this.properties = new Properties();
+ if (configFile.exists()) {
+ try {
+ final FileReader reader = new FileReader(configFile);
+ this.properties.load(reader);
+ reader.close();
+ } catch (final FileNotFoundException e) {
+ this.logger.error(e);
+ } catch (final IOException e) {
+ this.logger.error(e);
+ }
+ }
+ if (GraphicsEnvironment.isHeadless()
+ || ((System.getProperty("java.awt.headless") != null) && System
+ .getProperty("java.awt.headless").equals("true"))) {
+ final String definedHost = System
+ .getProperty("taverna.interaction.host");
+ if (definedHost != null) {
+ this.properties.setProperty(USE_JETTY, "false");
+ this.logger.info("USE_JETTY set to false");
+ this.properties.setProperty(HOST, definedHost);
+ }
+ final String definedPort = System
+ .getProperty("taverna.interaction.port");
+ if (definedPort != null) {
+ this.properties.setProperty(PORT, definedPort);
+ }
+ final String definedWebDavPath = System
+ .getProperty("taverna.interaction.webdav_path");
+ if (definedWebDavPath != null) {
+ this.properties.setProperty(WEBDAV_PATH, definedWebDavPath);
+ }
+ final String definedFeedPath = System
+ .getProperty("taverna.interaction.feed_path");
+ if (definedFeedPath != null) {
+ this.properties.setProperty(FEED_PATH, definedFeedPath);
+ }
+ } else {
+ this.logger.info("Running non-headless");
+ }
+ this.fillDefaultProperties();
+ }
+
+ private void fillDefaultProperties() {
+ if (!this.properties.containsKey(USE_JETTY)) {
+ this.properties.setProperty(USE_JETTY, DEFAULT_USE_JETTY);
+ this.logger.info("USE_JETTY set to " + DEFAULT_USE_JETTY);
+ }
+ if (!this.properties.containsKey(PORT)) {
+ this.properties.setProperty(PORT, DEFAULT_PORT);
+ }
+ if (!this.properties.containsKey(HOST)) {
+ this.properties.setProperty(HOST, DEFAULT_HOST);
+ }
+ if (!this.properties.containsKey(WEBDAV_PATH)) {
+ this.properties.setProperty(WEBDAV_PATH, DEFAULT_WEBDAV_PATH);
+ }
+ if (!this.properties.containsKey(FEED_PATH)) {
+ this.properties.setProperty(FEED_PATH, DEFAULT_FEED_PATH);
+ }
+ if (!this.properties.containsKey(USE_USERNAME)) {
+ this.properties.setProperty(USE_USERNAME, DEFAULT_USE_USERNAME);
+ }
+ /*
+ * if (!properties.containsKey(USE_HTTPS)) {
+ * properties.setProperty(USE_HTTPS, DEFAULT_USE_HTTPS); }
+ */
+ }
+
+ public String getFilePrefix() {
+ return "Interaction";
+ }
+
+ public void store() {
+ try {
+ final FileOutputStream out = new FileOutputStream(
+ this.getConfigFile());
+ this.properties.store(out, "");
+ out.close();
+ } catch (final FileNotFoundException e) {
+ this.logger.error(e);
+ } catch (final IOException e) {
+ this.logger.error(e);
+ }
+ }
+
+ public String getUUID() {
+ return "DA992717-5A46-469D-AE25-883F0E4CD348";
+ }
+
+ public void setPort(final String text) {
+ this.properties.setProperty(PORT, text);
+ }
+
+ public void setHost(final String text) {
+ this.properties.setProperty(HOST, text);
+ }
+
+ public void setUseJetty(final boolean use) {
+ this.properties.setProperty(USE_JETTY, Boolean.toString(use));
+ }
+
+ public void setFeedPath(final String path) {
+ this.properties.setProperty(FEED_PATH, path);
+ }
+
+ public void setWebDavPath(final String path) {
+ this.properties.setProperty(WEBDAV_PATH, path);
+ }
+
+ public String getPort() {
+ return this.properties.getProperty(PORT);
+ }
+
+ public String getHost() {
+ return this.properties.getProperty(HOST);
+ }
+
+ public boolean getUseJetty() {
+ return (Boolean.parseBoolean(this.properties.getProperty(USE_JETTY)));
+ }
+
+ public String getFeedPath() {
+ return this.properties.getProperty(FEED_PATH);
+ }
+
+ public String getWebDavPath() {
+ return this.properties.getProperty(WEBDAV_PATH);
+ }
+
+ public String getDefaultHost() {
+ return DEFAULT_HOST;
+ }
+
+ public String getDefaultFeedPath() {
+ return DEFAULT_FEED_PATH;
+ }
+
+ public String getDefaultWebDavPath() {
+ return DEFAULT_WEBDAV_PATH;
+ }
+
+ public String getFeedUrlString() {
+ return this.getHost() + ":" + this.getPort() + this.getFeedPath();
+ }
+
+ public String getLocationUrl() {
+ return this.getHost() + ":" + this.getPort() + this.getWebDavPath();
+ }
+
+ public boolean getUseUsername() {
+ return (Boolean.parseBoolean(this.properties.getProperty(USE_USERNAME)));
+ }
+
+ public void setUseUsername(final boolean useUsername) {
+ this.properties
+ .setProperty(USE_USERNAME, Boolean.toString(useUsername));
+ }
+
+ public String getOutputDataUrlString(final String interactionId) {
+ return this.getLocationUrl()
+ + "/interaction" + interactionId + "OutputData.json";
+ }
+
+ public String getInputDataUrlString(final String interactionId) {
+ return this.getLocationUrl()
+ + "/interaction" + interactionId + "InputData.json";
+ }
+
+ public URL getFeedUrl() throws MalformedURLException {
+ return new URL(this.getFeedUrlString());
+ }
+
+ public String getInteractionUrlString(final String interactionId) {
+ return this.getLocationUrl()
+ + "/interaction" + interactionId + ".html";
+ }
+
+ public String getPresentationUrlString(final String interactionId) {
+ return this.getLocationUrl()
+ + "/presentation" + interactionId + ".html";
+ }
+
+ public String getPublicationUrlString(final String interactionId,
+ final String key) {
+ return this.getLocationUrl()
+ + "/interaction" + interactionId + "_" + key;
+ }
+
+ public void setAppConfig(ApplicationConfiguration appConfig) {
+ this.appConfig = appConfig;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
new file mode 100644
index 0000000..774060e
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/InteractionVelocity.java
@@ -0,0 +1,145 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.velocity;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+import org.apache.taverna.activities.interaction.InteractionActivity;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+import org.apache.velocity.Template;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+
+/**
+ * @author alanrw
+ *
+ */
+public class InteractionVelocity {
+
+ public static Logger logger = Logger.getLogger(InteractionVelocity.class);
+
+ private static boolean velocityInitialized = false;
+
+ private static final String TEMPLATE_SUFFIX = ".vm";
+
+ private Template interactionTemplate = null;
+ private static final String INTERACTION_TEMPLATE_NAME = "interaction";
+
+ private ArrayList<String> templateNames = new ArrayList<String>();
+
+ private VelocityEngine ve = new VelocityEngine();
+
+ @SuppressWarnings("deprecation")
+ public synchronized void checkVelocity() {
+ if (velocityInitialized) {
+ return;
+ }
+ velocityInitialized = true;
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "string");
+ ve.setProperty("resource.loader.class",
+ "org.apache.velocity.runtime.resource.loader.StringResourceLoader");
+ ve.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
+ "org.apache.velocity.runtime.log.Log4JLogChute");
+ ve.setProperty("runtime.log.logsystem.log4j.logger",
+ "net.sf.taverna.t2.activities.interaction.velocity.InteractionVelocity");
+ ve.init();
+ ve.loadDirective(RequireDirective.class.getName());
+ ve.loadDirective(ProduceDirective.class.getName());
+ ve.loadDirective(NotifyDirective.class.getName());
+
+ loadTemplates();
+
+ interactionTemplate = ve.getTemplate(INTERACTION_TEMPLATE_NAME);
+ if (interactionTemplate == null) {
+ logger.error("Could not open interaction template "
+ + INTERACTION_TEMPLATE_NAME);
+ }
+ }
+
+ private void loadTemplates() {
+ final InputStream is = InteractionActivity.class
+ .getResourceAsStream("/index");
+ if (is == null) {
+ logger.error("Unable to read /index");
+ return;
+ }
+ final BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ try {
+ for (String line = br.readLine(); line != null; line = br
+ .readLine()) {
+ if (line.startsWith("#")) {
+ continue;
+ }
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+ final String templatePath = line + TEMPLATE_SUFFIX;
+ logger.info("Looking for " + templatePath);
+ final StringResourceRepository repo = StringResourceLoader
+ .getRepository();
+ try {
+ repo.putStringResource(line,
+ getTemplateFromResource(templatePath));
+ } catch (final IOException e) {
+ logger.error(
+ "Failed reading template from " + templatePath, e);
+ }
+ final Template t = Velocity.getTemplate(line);
+ if (t == null) {
+ logger.error("Registration failed");
+ }
+ if (!line.equals(INTERACTION_TEMPLATE_NAME)) {
+ templateNames.add(line);
+ }
+ }
+ } catch (final IOException e) {
+ logger.error("Failed reading template index", e);
+ }
+ }
+
+ public Template getInteractionTemplate() {
+ checkVelocity();
+ return interactionTemplate;
+ }
+
+ private String getTemplateFromResource(final String templatePath)
+ throws IOException {
+ checkVelocity();
+ final InputStream stream = InteractionVelocity.class
+ .getResourceAsStream("/" + templatePath);
+ final String result = IOUtils.toString(stream, "UTF-8");
+ return result;
+ }
+
+ public ArrayList<String> getTemplateNames() {
+ checkVelocity();
+ return templateNames;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
new file mode 100644
index 0000000..d9c6ec2
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyChecker.java
@@ -0,0 +1,42 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.velocity;
+
+import org.apache.velocity.runtime.parser.node.ASTDirective;
+import org.apache.velocity.runtime.visitor.BaseVisitor;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * @author alanrw
+ *
+ */
+public class NotifyChecker extends BaseVisitor {
+
+ @Override
+ public Object visit(final ASTDirective node, final Object data) {
+ ObjectNode json = (ObjectNode) data;
+ if (node.getDirectiveName().equals("notify")) {
+ json.put("progressNotification", true);
+ }
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-common-activities/blob/433612be/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
----------------------------------------------------------------------
diff --git a/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
new file mode 100644
index 0000000..2b314d6
--- /dev/null
+++ b/taverna-interaction-activity/src/main/java/org/apache/taverna/activities/interaction/velocity/NotifyDirective.java
@@ -0,0 +1,74 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.activities.interaction.velocity;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * @author alanrw
+ *
+ */
+public class NotifyDirective extends Directive {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.velocity.runtime.directive.Directive#getName()
+ */
+ @Override
+ public String getName() {
+ return "notify";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.velocity.runtime.directive.Directive#getType()
+ */
+ @Override
+ public int getType() {
+ return LINE;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.velocity.runtime.directive.Directive#render(org.apache.velocity
+ * .context.InternalContextAdapter, java.io.Write\ r,
+ * org.apache.velocity.runtime.parser.node.Node)
+ */
+ @Override
+ public boolean render(final InternalContextAdapter context,
+ final Writer writer, final Node node) throws IOException,
+ ResourceNotFoundException, ParseErrorException,
+ MethodInvocationException {
+ return true;
+ }
+
+}