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

org.mule.module.xml.el.XPath3Function Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.module.xml.el;

import static org.mule.util.ClassUtils.isConsumable;
import static org.mule.util.Preconditions.checkArgument;
import static org.mule.util.Preconditions.checkState;

import org.mule.RequestContext;
import org.mule.api.MuleContext;
import org.mule.api.MuleEvent;
import org.mule.api.MuleMessage;
import org.mule.api.MuleRuntimeException;
import org.mule.api.el.ExpressionLanguageContext;
import org.mule.api.el.ExpressionLanguageFunction;
import org.mule.api.registry.RegistrationException;
import org.mule.api.transformer.DataType;
import org.mule.api.transport.OutputHandler;
import org.mule.el.context.MessageContext;
import org.mule.el.mvel.MVELExpressionLanguageContext;
import org.mule.module.xml.transformer.DelayedResult;
import org.mule.module.xml.util.NamespaceManager;
import org.mule.module.xml.util.XMLUtils;
import org.mule.module.xml.xpath.SaxonXpathEvaluator;
import org.mule.module.xml.xpath.XPathEvaluator;
import org.mule.module.xml.xpath.XPathReturnType;
import org.mule.transformer.TransformerUtils;
import org.mule.transformer.types.DataTypeFactory;

import com.google.common.base.Joiner;
import com.google.common.base.Supplier;

import java.io.InputStream;

import javax.xml.stream.XMLStreamReader;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * A MEL function capable of evaluation XPath expressions by delegating into an instance
 * of {@link XPathEvaluator}
 * 

* The function will now accept the following arguments (in order): *

*

* * * * * * * * * * * * * *
expression (required String)The Xpath expression to be evaluated. Cannot be null or blank.
input (optional Object, defaults to the message payload)The input data on which the expression is going to be evaluated. This is an optional argument, it defaults to the message payload if not provided.
Output type (optional String, defaults to ?STRING?)When executing an XPath expression, a developer might have very different intents. Sometimes you want to retrieve actual data, * sometimes you just want to verify if a node exists. Also, the JAXP API (JSR-206) defines the standard way for a Java application to handle XML, and therefore, * how to execute XPath expressions. This API accounts for the different intents a developer might have and allows choosing from a list of possible output types. * We consider this to be a really useful features in JAXP, and we also consider that many Java developers that are familiar with this API would appreciate * that Mule accounts for this while hiding the rest of the API's complexity. That is why there's a third parameter (optional, String), * which will allow specifying one of the following: *
    *
  • BOOLEAN: returns the effective boolean value of the expression, as a java.lang.Boolean. This is the same as wrapping the expression in a call of the XPath boolean() function.
  • *
  • STRING: returns the result of the expression converted to a string, as a java.lang.String. This is the same as wrapping the expression in a call of the XPath string() function.
  • *
  • NUMBER: returns the result of the expression converted to a double as a java.lang.Double. This is the same as wrapping the expression in a call of the XPath number() function.
  • *
  • NODE: returns the result the result as a node object.
  • *
  • NODESET: returns a {@link NodeList}.
  • *
*

* Input types *

* This function supports the following input types: *

*

  • *
      {@link Document}
    *
      {@link Node}
    *
      {@link InputSource}
    *
      {@link OutputHandler}
    *
      {@code byte[]}
    *
      {@link InputStream}
    *
      {@link XMLStreamReader}
    *
      {@link DelayedResult}
    *
  • *

    *

    * If the input if not of any of these types, then we'll attempt to use a registered transformer to transform the input into a DOM document or Node. * If no such transformer can be found, then an {@link IllegalArgumentException} will be thrown. *

    * This function will verify if the input is a consumable type (streams, readers, etc). Because evaluating the expression over a consumable input * will cause that source to be exhausted, in the cases in which the input value was the actual message payload * (no matter if it was given explicitly or by default), it will update the output message payload with the result obtained from consuming the input. * * @since 3.6.0 */ public class XPath3Function implements ExpressionLanguageFunction { private static final Logger LOGGER = LoggerFactory.getLogger(XPath3Function.class); private static final XPathReturnType DEFAULT_RETURN_TYPE = XPathReturnType.STRING; private static final DataType[] SUPPORTED_TYPES = new DataType[] {DataTypeFactory.create(Document.class), DataTypeFactory.create(Node.class)}; private static final String SUPPORTED_TYPES_AS_STRING = Joiner.on(',').join(SUPPORTED_TYPES); private final MuleContext muleContext; private XPathEvaluator xpathEvaluator; private Supplier xpathEvaluatorSupplier = new LookupEvaluatorSupplier(); XPath3Function(MuleContext muleContext) { this.muleContext = muleContext; } @Override public Object call(Object[] params, ExpressionLanguageContext context) { validateParams(params); final MessageContext ctx = context.getVariable("message"); final String xpathExpression = getXpathExpression(params); final XPathReturnType returnType = getReturnType(params); final MuleEvent event = getMuleEvent(context); final Object input = getInput(params, event); try { Node node = toDOMNode(input, event); Object result = xpathEvaluatorSupplier.get().evaluate(xpathExpression, node, returnType, event); MuleMessage message = event.getMessage(); if (input == message.getPayload() && isConsumable(message.getPayload().getClass())) { ctx.setPayload(node); } return result; } catch (Exception e) { throw new MuleRuntimeException(e); } } private MuleEvent getMuleEvent(ExpressionLanguageContext context) { MuleEvent event = context.getVariable(MVELExpressionLanguageContext.MULE_EVENT_INTERNAL_VARIABLE); if (event == null) { event = RequestContext.getEvent(); } checkState(event != null, "Could not obtain MuleEvent"); return event; } private Node toDOMNode(Object input, MuleEvent event) throws Exception { if (input == null) { throw new IllegalArgumentException("Can't evaluate an XPath expression over a null input"); } Node node = XMLUtils.toDOMNode(input, event); if (node == null) { node = (Node) TransformerUtils.transformToAny(input, muleContext, SUPPORTED_TYPES); } if (node == null) { throw new IllegalArgumentException( String.format("Could not transform input of type '%s' to a supported one. Supported types are '%s'", input.getClass().getName(), SUPPORTED_TYPES_AS_STRING)); } return node; } private Object getInput(Object[] params, MuleEvent event) { if (params.length >= 2) { return params[1]; } return event.getMessage().getPayload(); } private void validateParams(Object[] params) { checkArgument(params.length > 0 && params.length <= 3, String.format("xpath3() function accepts up to 3 arguments, but %s were provided instead", params.length)); } private String getXpathExpression(Object[] params) { checkArgument(params[0] != null, "XPath expression cannot be null"); checkArgument(params[0] instanceof String, "XPath expression must be a String"); String expression = (String) params[0]; checkArgument(!StringUtils.isBlank(expression), "XPath expression cannot be blank"); return expression; } private XPathReturnType getReturnType(Object[] params) { if (params.length >= 3) { Object returnType = params[2]; if (returnType == null) { return DEFAULT_RETURN_TYPE; } checkArgument(returnType instanceof String, "returnType argument must be of type String"); try { return XPathReturnType.valueOf((String) returnType); } catch (Exception e) { throw new IllegalArgumentException(returnType + " is not a valid XPath return type", e); } } return DEFAULT_RETURN_TYPE; } private class LookupEvaluatorSupplier implements Supplier { @Override public synchronized XPathEvaluator get() { if (xpathEvaluator == null) { xpathEvaluator = new SaxonXpathEvaluator(); try { NamespaceManager namespaceManager = muleContext.getRegistry().lookupObject(NamespaceManager.class); if (namespaceManager != null) { xpathEvaluator.registerNamespaces(namespaceManager); } } catch (RegistrationException e) { LOGGER.debug("No namespace manager found. Will not register any namespaces"); } xpathEvaluatorSupplier = new FastEvaluatorSupplier(); } return xpathEvaluator; } } private class FastEvaluatorSupplier implements Supplier { @Override public XPathEvaluator get() { return xpathEvaluator; } } }





    © 2015 - 2024 Weber Informatics LLC | Privacy Policy