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