All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.taverna.activities.xpath.XPathActivity Maven / Gradle / Ivy

/*
* 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.xpath;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.taverna.invocation.InvocationContext;
import org.apache.taverna.reference.ErrorDocumentService;
import org.apache.taverna.reference.ReferenceService;
import org.apache.taverna.reference.T2Reference;
import org.apache.taverna.workflowmodel.processor.activity.AbstractAsynchronousActivity;
import org.apache.taverna.workflowmodel.processor.activity.ActivityConfigurationException;
import org.apache.taverna.workflowmodel.processor.activity.AsynchronousActivityCallback;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.InvalidXPathException;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.dom4j.XPathException;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * Enhanced XPath activity.
 *
 * @author Sergejs Aleksejevs
 */
public class XPathActivity extends AbstractAsynchronousActivity {

	public static final String URI = "http://ns.taverna.org.uk/2010/activity/xpath";

	// These ports are default ones (and only ones - XPath activity will not have dynamic ports)
	public static final String IN_XML = "xml_text";
	public static final String OUT_TEXT = "nodelist";
	public static final String OUT_XML = "nodelistAsXML";

	private static final String SINGLE_VALUE_TEXT = "firstNode";
	private static final String SINGLE_VALUE_XML = "firstNodeAsXML";
	
	// Configuration bean for this activity - essentially defines a particular instance
	// of the activity through the values of its parameters
	private JsonNode json;

	@Override
	public JsonNode getConfiguration() {
		return this.json;
	}

	@Override
	public void configure(JsonNode json) throws ActivityConfigurationException {
		// Check configBean is valid
		if (!XPathUtils.isValid(json)) {
			throw new ActivityConfigurationException("Invalid configuration of XPath activity...");
			// TODO - check this
		}

		// Store for getConfiguration()
		this.json = json;
	}

	protected void configurePorts() {
		// ---- REMOVE OLD PORTS ----
	  
		// In case we are being reconfigured - remove existing ports first to avoid duplicates
		removeInputs();
		removeOutputs();

		// ---- CREATE NEW INPUTS AND OUTPUTS ----
		
		// all ports in this activity are static, so no dependency on the values in config bean
		
		// single input port: the input XML text will be treated as String for now
		addInput(IN_XML, 0, true, null, String.class);

		addOutput(SINGLE_VALUE_TEXT, 0);
		addOutput(SINGLE_VALUE_XML, 0);
		addOutput(OUT_TEXT, 1);
		addOutput(OUT_XML, 1);
	}

	/**
	 * This method executes pre-configured instance of XPath activity.
	 */
	@Override
	public void executeAsynch(final Map inputs,
			final AsynchronousActivityCallback callback) {
		// Don't execute service directly now, request to be run asynchronously
		callback.requestRun(new Runnable() {
			@Override
			@SuppressWarnings("unchecked")
			public void run() {

				InvocationContext context = callback.getContext();
				ReferenceService referenceService = context.getReferenceService();

				// ---- RESOLVE INPUT ----

				String xmlInput = (String) referenceService.renderIdentifier(inputs.get(IN_XML),
						String.class, context);

				// ---- DO THE ACTUAL SERVICE INVOCATION ----

				List matchingNodes = new ArrayList();

				// only attempt to execute XPath expression if there is some input data
				if (xmlInput != null && xmlInput.length() > 0) {
					// XPath configuration is taken from the config bean
					try {
						XPath expr = DocumentHelper.createXPath(json.get("xpathExpression").textValue());
						Map xpathNamespaceMap = new HashMap<>();
						for (JsonNode namespaceMapping : json.get("xpathNamespaceMap")) {
							xpathNamespaceMap.put(namespaceMapping.get("prefix").textValue(),
									namespaceMapping.get("uri").textValue());
						}
						expr.setNamespaceURIs(xpathNamespaceMap);
						Document doc = DocumentHelper.parseText(xmlInput);
						matchingNodes = expr.selectNodes(doc);
					} catch (InvalidXPathException e) {
						callback.fail("Incorrect XPath Expression -- XPath processing library "
								+ "reported the following error: " + e.getMessage(), e);

						// make sure we don't call callback.receiveResult later
						return;
					} catch (DocumentException e) {
						callback.fail("XML document was not valid -- XPath processing library "
								+ "reported the following error: " + e.getMessage(), e);

						// make sure we don't call callback.receiveResult later
						return;
					} catch (XPathException e) {
						callback.fail(
								"Unexpected error has occurred while executing the XPath expression. "
										+ "-- XPath processing library reported the following error:\n"
										+ e.getMessage(), e);

						// make sure we don't call callback.receiveResult later
						return;
					}
				}
				
				// --- PREPARE OUTPUTS ---

				List outNodesText = new ArrayList();
				List outNodesXML = new ArrayList();
				Object textValue = null;
				Object xmlValue = null;

				for (Object o : matchingNodes) {
					if (o instanceof Node) {
						Node n = (Node) o;
						if (n.getStringValue() != null
								&& n.getStringValue().length() > 0) {
							outNodesText.add(n.getStringValue());
							if (textValue == null)
								textValue = n.getStringValue();
						}
						outNodesXML.add(n.asXML());
						if (xmlValue == null)
							xmlValue = n.asXML();
					} else {
						outNodesText.add(o.toString());
						if (textValue == null)
							textValue = o.toString();
					}
				}

				// ---- REGISTER OUTPUTS ----

				Map outputs = new HashMap();
				if (textValue == null) {
					ErrorDocumentService errorDocService = referenceService
							.getErrorDocumentService();
					textValue = errorDocService.registerError(
							"No value produced", 0, callback.getContext());
				}

				if (xmlValue == null) {
					ErrorDocumentService errorDocService = referenceService
							.getErrorDocumentService();
					xmlValue = errorDocService.registerError(
							"No value produced", 0, callback.getContext());
				}

				T2Reference firstNodeAsText = referenceService.register(
						textValue, 0, true, context);
				outputs.put(SINGLE_VALUE_TEXT, firstNodeAsText);

				T2Reference firstNodeAsXml = referenceService.register(
						xmlValue, 0, true, context);
				outputs.put(SINGLE_VALUE_XML, firstNodeAsXml);

				T2Reference outNodesAsText = referenceService.register(
						outNodesText, 1, true, context);
				outputs.put(OUT_TEXT, outNodesAsText);

				T2Reference outNodesAsXML = referenceService.register(
						outNodesXML, 1, true, context);
				outputs.put(OUT_XML, outNodesAsXML);

				// return map of output data, with empty index array as this is
				// the only and final result (this index parameter is used if
				// pipelining output)
				callback.receiveResult(outputs, new int[0]);
			}
		});
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy