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

org.mule.module.xml.xpath.JaxpXPathEvaluator 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.xpath;

import static org.mule.util.Preconditions.checkArgument;
import org.mule.api.MuleEvent;
import org.mule.api.MuleRuntimeException;
import org.mule.module.xml.i18n.XmlMessages;
import org.mule.module.xml.util.NamespaceManager;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.ImmutableMap;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathVariableResolver;

import org.apache.commons.pool.impl.GenericObjectPool;
import org.w3c.dom.Node;

/**
 * This is the preferred base implementation of {@link XPathEvaluator}. Because it's
 * based on the JAXP API (JSR-206), it's ideal for keeping a common code base which
 * can work with different engines, as long as they implement that API.
 * 

* This base class contains all of the logic necessary to comply with the * {@link XPathEvaluator} contract. Implementations only need to implement * {@link #createXPathFactory()} in order to provide the {@link XPathFactory} * implementation it wishes to use. *

* Another important feature of this implementation is that it caches compiled * versions of executed expressions to provide better performance. Expressions that haven't * been used for more than a minute are automatically evicted. *

* In addition to the {@link #registerNamespaces(Map)} and {@link #registerNamespaces(NamespaceManager)} * methods, this implementation also provides out of the box support for the standard namespaces * defined in {@link XPathNamespaceContext} *

* In order to allow binding expression parameters to flow variables, this class also * implements the {@link XPathVariableResolver} interface. Because this class caches * compiled expressions which might be executed concurrently in different threads, we need a * way to correlate different {@link MuleEvent} instances to each invocation of the {@link #resolveVariable(QName)} * method. To do that, it uses a {@link ThreadLocal} in the {@link #evaluationEvent} attribute, so that * we can determine the corresponding event for each thread evaluating an XPath expression. Notice that * because xpath evaluation is an operation that happens in RAM memory (basically because the DOM {@link Node} * needs to be completely loaded), we can use a {@link ThreadLocal} without risking failure if this is * executed in a non-blocking environment. * * @since 3.6.0 */ public abstract class JaxpXPathEvaluator implements XPathEvaluator, XPathVariableResolver { private static final int MIN_IDLE_XPATH_EXPRESSIONS = 1; private static final int MAX_IDLE_XPATH_EXPRESSIONS = 32; private static final int MAX_ACTIVE_XPATH_EXPRESSIONS = MAX_IDLE_XPATH_EXPRESSIONS; private final XPathFactory xpathFactory; private final Map prefixToNamespaceMap = new HashMap<>(); private final ThreadLocal evaluationEvent = new ThreadLocal<>(); private final LoadingCache> xpathExpressionPoolCache = CacheBuilder.newBuilder() .expireAfterAccess(10, TimeUnit.MINUTES) .removalListener(new RemovalListener>() { @Override public void onRemoval(RemovalNotification> notification) { notification.getValue().clear(); } }) .build(new CacheLoader>() { @Override public GenericObjectPool load(String key) throws Exception { return getXPathExpressionPool(key); } }); private NamespaceContext namespaceContext; public JaxpXPathEvaluator() { xpathFactory = createXPathFactory(); namespaceContext = newNamespaceContext(); } /** * Returns the {@link XPathFactory} to be used when * compiling expressions * * @return a {@link XPathFactory} */ protected abstract XPathFactory createXPathFactory(); /** * {@inheritDoc} */ @Override public String evaluate(String xpathExpression, Node input, MuleEvent event) { return (String) evaluate(xpathExpression, input, XPathReturnType.STRING, event); } /** * {@inheritDoc} */ @Override public Object evaluate(String xpathExpression, Node input, XPathReturnType returnType, MuleEvent event) { XPathExpression xpath = null; GenericObjectPool xpathExpressionPool = null; try { evaluationEvent.set(event); xpathExpressionPool = xpathExpressionPoolCache.get(xpathExpression); xpath = xpathExpressionPool.borrowObject(); Object result = xpath.evaluate(input, returnType.toQName()); return result; } catch (Exception e) { throw new MuleRuntimeException(XmlMessages.failedToProcessXPath(xpathExpression), e); } finally { evaluationEvent.remove(); try { if (xpathExpressionPool != null && xpath != null) { xpathExpressionPool.returnObject(xpath); } } catch (Exception e) { throw new MuleRuntimeException(XmlMessages.failedToProcessXPath(xpathExpression), e); } } } /** * Resolves the given variable against the flow variables * in the {@link MuleEvent} held by {@link #evaluationEvent} * * @param variableName the variable name * @return the variable value. Might be {@code null} */ @Override public Object resolveVariable(QName variableName) { MuleEvent event = evaluationEvent.get(); if (event != null) { return event.getFlowVariable(variableName.getLocalPart()); } return null; } /** * {@inheritDoc} */ @Override public void registerNamespaces(Map namespaces) { checkArgument(namespaces != null, "cannot register null namespaces"); prefixToNamespaceMap.putAll(namespaces); namespaceContext = newNamespaceContext(); xpathExpressionPoolCache.invalidateAll(); } /** * {@inheritDoc} */ @Override public void registerNamespaces(NamespaceManager namespaceManager) { checkArgument(namespaceManager != null, "cannot register a null namespace manager"); registerNamespaces(namespaceManager.getNamespaces()); } /** * {@inheritDoc} * Returns an immutable map with the current registered namespaces. * The returned map will not reflect any changes performed afterwards. */ @Override public Map getRegisteredNamespaces() { return ImmutableMap.copyOf(prefixToNamespaceMap); } private GenericObjectPool getXPathExpressionPool(String expression) { GenericObjectPool genericPool = new GenericObjectPool(new XPathExpressionFactory(xpathFactory, expression, namespaceContext, this)); genericPool.setMaxActive(MAX_ACTIVE_XPATH_EXPRESSIONS); genericPool.setMaxIdle(MAX_IDLE_XPATH_EXPRESSIONS); genericPool.setMinIdle(MIN_IDLE_XPATH_EXPRESSIONS); return genericPool; } protected NamespaceContext newNamespaceContext() { return new XPathNamespaceContext(prefixToNamespaceMap); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy