https://issues.apache.org/jira/browse/AMQ-5333 - make xpath parser features configurable

This commit is contained in:
Dejan Bosanac 2014-08-26 14:46:45 +02:00
parent 4fa10356f0
commit b9696ac80b
3 changed files with 94 additions and 37 deletions

View File

@ -16,32 +16,33 @@
*/ */
package org.apache.activemq.filter; package org.apache.activemq.filter;
import java.io.StringReader; import org.apache.activemq.command.Message;
import org.apache.activemq.util.ByteArrayInputStream;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import javax.jms.BytesMessage; import javax.jms.BytesMessage;
import javax.jms.JMSException; import javax.jms.JMSException;
import javax.jms.TextMessage; import javax.jms.TextMessage;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.xpath.XPath; import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import java.io.StringReader;
import org.apache.activemq.command.Message;
import org.apache.activemq.util.ByteArrayInputStream;
import org.xml.sax.InputSource;
public class JAXPXPathEvaluator implements XPathExpression.XPathEvaluator { public class JAXPXPathEvaluator implements XPathExpression.XPathEvaluator {
private static final XPathFactory FACTORY = XPathFactory.newInstance(); private static final XPathFactory FACTORY = XPathFactory.newInstance();
private final javax.xml.xpath.XPathExpression expression;
private final String xpathExpression; private final String xpathExpression;
private final DocumentBuilder builder;
private final XPath xpath = FACTORY.newXPath();
public JAXPXPathEvaluator(String xpathExpression) { public JAXPXPathEvaluator(String xpathExpression, DocumentBuilder builder) throws Exception {
this.xpathExpression = xpathExpression; this.xpathExpression = xpathExpression;
try { if (builder != null) {
XPath xpath = FACTORY.newXPath(); this.builder = builder;
expression = xpath.compile(xpathExpression); } else {
} catch (XPathExpressionException e) { throw new RuntimeException("No document builder available");
throw new RuntimeException("Invalid XPath expression: " + xpathExpression);
} }
} }
@ -61,8 +62,9 @@ public class JAXPXPathEvaluator implements XPathExpression.XPathEvaluator {
private boolean evaluate(byte[] data) { private boolean evaluate(byte[] data) {
try { try {
InputSource inputSource = new InputSource(new ByteArrayInputStream(data)); InputSource inputSource = new InputSource(new ByteArrayInputStream(data));
return ((Boolean)expression.evaluate(inputSource, XPathConstants.BOOLEAN)).booleanValue(); Document inputDocument = builder.parse(inputSource);
} catch (XPathExpressionException e) { return ((Boolean)xpath.evaluate(xpathExpression, inputDocument, XPathConstants.BOOLEAN)).booleanValue();
} catch (Exception e) {
return false; return false;
} }
} }
@ -70,8 +72,9 @@ public class JAXPXPathEvaluator implements XPathExpression.XPathEvaluator {
private boolean evaluate(String text) { private boolean evaluate(String text) {
try { try {
InputSource inputSource = new InputSource(new StringReader(text)); InputSource inputSource = new InputSource(new StringReader(text));
return ((Boolean)expression.evaluate(inputSource, XPathConstants.BOOLEAN)).booleanValue(); Document inputDocument = builder.parse(inputSource);
} catch (XPathExpressionException e) { return ((Boolean)xpath.evaluate(xpathExpression, inputDocument, XPathConstants.BOOLEAN)).booleanValue();
} catch (Exception e) {
return false; return false;
} }
} }

View File

@ -16,34 +16,33 @@
*/ */
package org.apache.activemq.filter; package org.apache.activemq.filter;
import java.io.StringReader; import org.apache.activemq.command.Message;
import org.apache.activemq.util.ByteArrayInputStream;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import javax.jms.BytesMessage; import javax.jms.BytesMessage;
import javax.jms.JMSException; import javax.jms.JMSException;
import javax.jms.TextMessage; import javax.jms.TextMessage;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.xpath.XPath; import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import java.io.StringReader;
import org.apache.activemq.command.Message;
import org.apache.activemq.util.ByteArrayInputStream;
import org.xml.sax.InputSource;
public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator { public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator {
private static final XPathFactory FACTORY = XPathFactory.newInstance(); private static final XPathFactory FACTORY = XPathFactory.newInstance();
private final javax.xml.xpath.XPathExpression expression;
private final String xpathExpression; private final String xpathExpression;
private final DocumentBuilder builder;
private final XPath xpath = FACTORY.newXPath();
public XalanXPathEvaluator(String xpathExpression) { public XalanXPathEvaluator(String xpathExpression, DocumentBuilder builder) throws Exception {
this.xpathExpression = xpathExpression; this.xpathExpression = xpathExpression;
try { if (builder != null) {
XPath xpath = FACTORY.newXPath(); this.builder = builder;
expression = xpath.compile(xpathExpression); } else {
} catch (XPathExpressionException e) { throw new RuntimeException("No document builder available");
throw new RuntimeException("Invalid XPath expression: " + xpathExpression);
} }
} }
@ -63,8 +62,9 @@ public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator {
private boolean evaluate(byte[] data) { private boolean evaluate(byte[] data) {
try { try {
InputSource inputSource = new InputSource(new ByteArrayInputStream(data)); InputSource inputSource = new InputSource(new ByteArrayInputStream(data));
return ((Boolean)expression.evaluate(inputSource, XPathConstants.BOOLEAN)).booleanValue(); Document inputDocument = builder.parse(inputSource);
} catch (XPathExpressionException e) { return ((Boolean) xpath.evaluate(xpathExpression, inputDocument, XPathConstants.BOOLEAN)).booleanValue();
} catch (Exception e) {
return false; return false;
} }
} }
@ -72,8 +72,9 @@ public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator {
private boolean evaluate(String text) { private boolean evaluate(String text) {
try { try {
InputSource inputSource = new InputSource(new StringReader(text)); InputSource inputSource = new InputSource(new StringReader(text));
return ((Boolean)expression.evaluate(inputSource, XPathConstants.BOOLEAN)).booleanValue(); Document inputDocument = builder.parse(inputSource);
} catch (XPathExpressionException e) { return ((Boolean) xpath.evaluate(xpathExpression, inputDocument, XPathConstants.BOOLEAN)).booleanValue();
} catch (Exception e) {
return false; return false;
} }
} }

View File

@ -19,8 +19,15 @@ package org.apache.activemq.filter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.jms.JMSException; import javax.jms.JMSException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.activemq.command.Message; import org.apache.activemq.command.Message;
import org.apache.activemq.util.JMSExceptionSupport; import org.apache.activemq.util.JMSExceptionSupport;
@ -35,8 +42,10 @@ public final class XPathExpression implements BooleanExpression {
private static final Logger LOG = LoggerFactory.getLogger(XPathExpression.class); private static final Logger LOG = LoggerFactory.getLogger(XPathExpression.class);
private static final String EVALUATOR_SYSTEM_PROPERTY = "org.apache.activemq.XPathEvaluatorClassName"; private static final String EVALUATOR_SYSTEM_PROPERTY = "org.apache.activemq.XPathEvaluatorClassName";
private static final String DEFAULT_EVALUATOR_CLASS_NAME = "org.apache.activemq.filter.XalanXPathEvaluator"; private static final String DEFAULT_EVALUATOR_CLASS_NAME = "org.apache.activemq.filter.XalanXPathEvaluator";
public static final String DOCUMENT_BUILDER_FACTORY_FEATURE = "org.apache.activemq.documentBuilderFactory.feature";
private static final Constructor EVALUATOR_CONSTRUCTOR; private static final Constructor EVALUATOR_CONSTRUCTOR;
private static DocumentBuilder builder = null;
static { static {
String cn = System.getProperty(EVALUATOR_SYSTEM_PROPERTY, DEFAULT_EVALUATOR_CLASS_NAME); String cn = System.getProperty(EVALUATOR_SYSTEM_PROPERTY, DEFAULT_EVALUATOR_CLASS_NAME);
@ -44,6 +53,21 @@ public final class XPathExpression implements BooleanExpression {
try { try {
try { try {
m = getXPathEvaluatorConstructor(cn); m = getXPathEvaluatorConstructor(cn);
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
builderFactory.setIgnoringElementContentWhitespace(true);
builderFactory.setIgnoringComments(true);
try {
// set some reasonable defaults
builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (ParserConfigurationException e) {
LOG.warn("Error setting document builder factory feature", e);
}
// setup the feature from the system property
setupFeatures(builderFactory);
builder = builderFactory.newDocumentBuilder();
} catch (Throwable e) { } catch (Throwable e) {
LOG.warn("Invalid " + XPathEvaluator.class.getName() + " implementation: " + cn + ", reason: " + e, e); LOG.warn("Invalid " + XPathEvaluator.class.getName() + " implementation: " + cn + ", reason: " + e, e);
cn = DEFAULT_EVALUATOR_CLASS_NAME; cn = DEFAULT_EVALUATOR_CLASS_NAME;
@ -75,12 +99,41 @@ public final class XPathExpression implements BooleanExpression {
if (!XPathEvaluator.class.isAssignableFrom(c)) { if (!XPathEvaluator.class.isAssignableFrom(c)) {
throw new ClassCastException("" + c + " is not an instance of " + XPathEvaluator.class); throw new ClassCastException("" + c + " is not an instance of " + XPathEvaluator.class);
} }
return c.getConstructor(new Class[] {String.class}); return c.getConstructor(new Class[] {String.class, DocumentBuilder.class});
}
protected static void setupFeatures(DocumentBuilderFactory factory) {
Properties properties = System.getProperties();
List<String> features = new ArrayList<String>();
for (Map.Entry<Object, Object> prop : properties.entrySet()) {
String key = (String) prop.getKey();
if (key.startsWith(DOCUMENT_BUILDER_FACTORY_FEATURE)) {
String uri = key.split(DOCUMENT_BUILDER_FACTORY_FEATURE + ":")[1];
Boolean value = Boolean.valueOf((String)prop.getValue());
try {
factory.setFeature(uri, value);
features.add("feature " + uri + " value " + value);
} catch (ParserConfigurationException e) {
LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", new Object[]{uri, value, e});
}
}
}
if (features.size() > 0) {
StringBuffer featureString = new StringBuffer();
// just log the configured feature
for (String feature : features) {
if (featureString.length() != 0) {
featureString.append(", ");
}
featureString.append(feature);
}
}
} }
private XPathEvaluator createEvaluator(String xpath2) { private XPathEvaluator createEvaluator(String xpath2) {
try { try {
return (XPathEvaluator)EVALUATOR_CONSTRUCTOR.newInstance(new Object[] {xpath}); return (XPathEvaluator)EVALUATOR_CONSTRUCTOR.newInstance(new Object[] {xpath, builder});
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
Throwable cause = e.getCause(); Throwable cause = e.getCause();
if (cause instanceof RuntimeException) { if (cause instanceof RuntimeException) {