From 3d1e3192f2f87f1e115aeeec064296a5bd3ebdff Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 12:48:36 +0100 Subject: [PATCH 01/19] reduce use of xalan --- .../util/PropertiesBasedPrefixResolver.java | 40 +- ...ropertiesBasedPrefixResolverForXpath2.java | 29 +- .../org/apache/jmeter/util/XPathUtil.java | 400 +++++++++--------- .../org/apache/jmeter/util/XPathUtilTest.java | 17 + .../jmeter/functions/XPathFileContainer.java | 2 +- 5 files changed, 280 insertions(+), 208 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java index 4cd789485f1..e4d774b6cc5 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Properties; @@ -34,10 +35,12 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Node; +import javax.xml.namespace.NamespaceContext; + /** * {@link PrefixResolver} implementation that loads prefix configuration from jmeter property xpath.namespace.config */ -public class PropertiesBasedPrefixResolver extends PrefixResolverDefault { +public class PropertiesBasedPrefixResolver extends PrefixResolverDefault implements NamespaceContext { private static final Logger log = LoggerFactory.getLogger(PropertiesBasedPrefixResolver.class); private static final String XPATH_NAMESPACE_CONFIG = "xpath.namespace.config"; private static final Map NAMESPACE_MAP = new HashMap<>(); @@ -85,11 +88,44 @@ public PropertiesBasedPrefixResolver(Node xpathExpressionContext) { */ @Override public String getNamespaceForPrefix(String prefix, Node namespaceContext) { - String namespace = NAMESPACE_MAP.get(prefix); + String namespace = getNamespaceURI(prefix); if(namespace==null) { return super.getNamespaceForPrefix(prefix, namespaceContext); } else { return namespace; } } + + @Override + public String getNamespaceURI(String prefix) { + return NAMESPACE_MAP.get(prefix); + } + + @Override + public String getPrefix(String namespaceURI) { + if (namespaceURI != null) { + for (Map.Entry entry : NAMESPACE_MAP.entrySet()) { + if (namespaceURI.equals(entry.getValue())) { + return entry.getValue(); + } + } + } + return null; + } + + @Override + public Iterator getPrefixes(String namespaceURI) { + return NAMESPACE_MAP.keySet().iterator(); + } + + String getNamespacesAsLineDelimitedProperties() { + StringBuilder builder = new StringBuilder(); + for (Map.Entry entry : NAMESPACE_MAP.entrySet()) { + builder.append(entry.getKey()) + .append('=') + .append(entry.getValue()) + .append('\n'); + } + return builder.toString(); + } } diff --git a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java index 44eb5296fc4..946039f08f9 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java @@ -18,17 +18,20 @@ package org.apache.jmeter.util; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import org.apache.xml.utils.PrefixResolver; import org.apache.xml.utils.PrefixResolverDefault; import org.w3c.dom.Node; +import javax.xml.namespace.NamespaceContext; + /** * {@link PrefixResolver} implementation that loads prefix configuration from * jmeter property xpath.namespace.config */ -public class PropertiesBasedPrefixResolverForXpath2 extends PrefixResolverDefault { +public class PropertiesBasedPrefixResolverForXpath2 extends PrefixResolverDefault implements NamespaceContext { private Map namespaceMap = new HashMap<>(); /** @@ -55,11 +58,33 @@ public PropertiesBasedPrefixResolverForXpath2(Node xpathExpressionContext, Strin */ @Override public String getNamespaceForPrefix(String prefix, Node namespaceContext) { - String namespace = namespaceMap.get(prefix); + String namespace = getNamespaceURI(prefix); if (namespace == null) { return super.getNamespaceForPrefix(prefix, namespaceContext); } else { return namespace; } } + + @Override + public String getNamespaceURI(String prefix) { + return namespaceMap.get(prefix); + } + + @Override + public String getPrefix(String namespaceURI) { + if (namespaceURI != null) { + for (Map.Entry entry : namespaceMap.entrySet()) { + if (namespaceURI.equals(entry.getValue())) { + return entry.getValue(); + } + } + } + return null; + } + + @Override + public Iterator getPrefixes(String namespaceURI) { + return namespaceMap.keySet().iterator(); + } } diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index d183e7da5ea..952956dd2d8 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -27,8 +27,9 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletionException; -import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -45,6 +46,10 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactoryConfigurationException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -55,7 +60,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.tidy.Tidy; @@ -72,10 +76,13 @@ import net.sf.saxon.s9api.XdmItem; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmValue; +import net.sf.saxon.xpath.XPathFactoryImpl; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING; + /** * This class provides a few utility methods for dealing with XML/XPath. */ @@ -122,7 +129,7 @@ private static synchronized DocumentBuilderFactory makeDocumentBuilderFactory(bo || documentBuilderFactory.isIgnoringElementContentWhitespace() != whitespace) { // configure the document builder factory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + documentBuilderFactory.setFeature(FEATURE_SECURE_PROCESSING, true); documentBuilderFactory.setValidating(validate); documentBuilderFactory.setNamespaceAware(namespace); documentBuilderFactory.setIgnoringElementContentWhitespace(whitespace); @@ -312,7 +319,7 @@ private static String getNodeContent(Node node) { StringWriter sw = new StringWriter(); try { TransformerFactory factory = TransformerFactory.newInstance(); - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature(FEATURE_SECURE_PROCESSING, true); Transformer t = factory.newTransformer(); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); t.transform(new DOMSource(node), new StreamResult(sw)); @@ -345,8 +352,11 @@ public static String getValueForNode(Node node) { * @throws TransformerException when the internally used xpath engine fails */ public static NodeList selectNodeList(Document document, String xPathExpression) throws TransformerException { - XObject xObject = XPathAPI.eval(document, xPathExpression, getPrefixResolver(document)); - return xObject.nodelist(); + try { + return (NodeList) newXPath(document).evaluate(xPathExpression, document, XPathConstants.NODESET); + } catch (XPathExpressionException|XPathFactoryConfigurationException e) { + throw new TransformerException(e); + } } /** @@ -374,41 +384,14 @@ public static void putValuesForXPathInList(Document document, */ public static void putValuesForXPathInList(Document document, String xPathQuery, List matchStrings, boolean fragment, int matchNumber) throws TransformerException { - String val = null; - XObject xObject = XPathAPI.eval(document, xPathQuery, getPrefixResolver(document)); - final int objectType = xObject.getType(); - if (objectType == XObject.CLASS_NODESET) { - NodeList matches = xObject.nodelist(); - int length = matches.getLength(); - int indexToMatch = matchNumber; - if(matchNumber == 0 && length>0) { - indexToMatch = JMeterUtils.getRandomInt(length)+1; - } - for (int i = 0 ; i < length; i++) { - Node match = matches.item(i); - if(indexToMatch >= 0 && indexToMatch != (i+1)) { - continue; - } - if ( match instanceof Element ){ - if (fragment){ - val = getNodeContent(match); - } else { - val = getValueForNode(match); - } - } else { - val = match.getNodeValue(); - } - matchStrings.add(val); - } - } else if (objectType == XObject.CLASS_NULL - || objectType == XObject.CLASS_UNKNOWN - || objectType == XObject.CLASS_UNRESOLVEDVARIABLE) { - if (log.isWarnEnabled()) { - log.warn("Unexpected object type: {} returned for: {}", xObject.getTypeString(), xPathQuery); - } - } else { - val = xObject.toString(); - matchStrings.add(val); + PropertiesBasedPrefixResolver namespaceContext = getPrefixResolver(document); + String namespaces = namespaceContext.getNamespacesAsLineDelimitedProperties(); + net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); + try { + XdmNode xdmNode = builder.build(new DOMSource(document)); + putValuesForXPathInListUsingSaxon(xdmNode, xPathQuery, matchStrings, fragment, matchNumber, namespaces); + } catch (SaxonApiException|FactoryConfigurationError e) { + throw new TransformerException(e); } } @@ -417,6 +400,22 @@ public static void putValuesForXPathInListUsingSaxon( List matchStrings, boolean fragment, int matchNumber, String namespaces) throws SaxonApiException, FactoryConfigurationError { + try (StringReader reader = new StringReader(xmlFile)) { + // We could instantiate it once but might trigger issues in the future + // Sharing of a DocumentBuilder across multiple threads is not recommended. + // However, in the current implementation sharing a DocumentBuilder (once initialized) + // will only cause problems if a SchemaValidator is used. + net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); + XdmNode xdmNode = builder.build(new SAXSource(new InputSource(reader))); + putValuesForXPathInListUsingSaxon(xdmNode, xPathQuery, matchStrings, fragment, matchNumber, namespaces); + } + } + + private static void putValuesForXPathInListUsingSaxon( + XdmNode xdmNode, String xPathQuery, + List matchStrings, boolean fragment, + int matchNumber, String namespaces) + throws SaxonApiException, FactoryConfigurationError { // generating the cache key final ImmutablePair key = ImmutablePair.of(xPathQuery, namespaces); @@ -431,54 +430,45 @@ public static void putValuesForXPathInListUsingSaxon( return; } - try (StringReader reader = new StringReader(xmlFile)) { - // We could instantiate it once but might trigger issues in the future - // Sharing of a DocumentBuilder across multiple threads is not recommended. - // However, in the current implementation sharing a DocumentBuilder (once initialized) - // will only cause problems if a SchemaValidator is used. - net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); - XdmNode xdmNode = builder.build(new SAXSource(new InputSource(reader))); - - if(xPathExecutable!=null) { - XPathSelector selector = null; - try { - selector = xPathExecutable.load(); - selector.setContextItem(xdmNode); - XdmValue nodes = selector.evaluate(); - int length = nodes.size(); - int indexToMatch = matchNumber; - // In case we need to extract everything - if(matchNumber < 0) { - for(XdmItem item : nodes) { - if(fragment) { - matchStrings.add(item.toString()); - } - else { - matchStrings.add(item.getStringValue()); - } + if(xPathExecutable!=null) { + XPathSelector selector = null; + try { + selector = xPathExecutable.load(); + selector.setContextItem(xdmNode); + XdmValue nodes = selector.evaluate(); + int length = nodes.size(); + int indexToMatch = matchNumber; + // In case we need to extract everything + if(matchNumber < 0) { + for(XdmItem item : nodes) { + if(fragment) { + matchStrings.add(item.toString()); } - } else { - if(indexToMatch <= length) { - if(matchNumber == 0 && length>0) { - indexToMatch = JMeterUtils.getRandomInt(length)+1; - } - XdmItem item = nodes.itemAt(indexToMatch-1); - matchStrings.add(fragment ? item.toString() : item.getStringValue()); - } else { - if(log.isWarnEnabled()) { - log.warn("Error : {}{}", JMeterUtils.getResString("xpath2_extractor_match_number_failure"),indexToMatch); - } + else { + matchStrings.add(item.getStringValue()); } } - } finally { - if(selector != null) { - try { - selector.getUnderlyingXPathContext().setContextItem(null); - } catch (Exception e) { // NOSONAR Ignored on purpose - // NOOP + } else { + if(indexToMatch <= length) { + if(matchNumber == 0 && length>0) { + indexToMatch = JMeterUtils.getRandomInt(length)+1; + } + XdmItem item = nodes.itemAt(indexToMatch-1); + matchStrings.add(fragment ? item.toString() : item.getStringValue()); + } else { + if(log.isWarnEnabled()) { + log.warn("Error : {}{}", JMeterUtils.getResString("xpath2_extractor_match_number_failure"),indexToMatch); } } } + } finally { + if(selector != null) { + try { + selector.getUnderlyingXPathContext().setContextItem(null); + } catch (Exception e) { // NOSONAR Ignored on purpose + // NOOP + } + } } } } @@ -562,7 +552,7 @@ private static void addToList(XMLStreamReader reader, List res) { * @param document XML Document * @return {@link PrefixResolver} */ - private static PrefixResolver getPrefixResolver(Document document) { + private static PropertiesBasedPrefixResolver getPrefixResolver(Document document) { return new PropertiesBasedPrefixResolver(document.getDocumentElement()); } @@ -573,24 +563,30 @@ private static PrefixResolver getPrefixResolver(Document document) { * @throws TransformerException if expression fails to evaluate */ public static void validateXPath(Document document, String xpathString) throws TransformerException { - if (XPathAPI.eval(document, xpathString, getPrefixResolver(document)) == null) { - // We really should never get here - // because eval will throw an exception - // if xpath is invalid, but whatever, better - // safe - throw new IllegalArgumentException("xpath eval of '" + xpathString + "' was null"); + try { + XPath xPath = newXPath(document); + if (xPath.evaluate(xpathString, document) == null) { + // We really should never get here + // because eval will throw an exception + // if xpath is invalid, but whatever, better + // safe + throw new IllegalArgumentException("xpath eval of '" + xpathString + "' was null"); + } + } catch (XPathExpressionException|XPathFactoryConfigurationException e) { + throw new TransformerException(e); } } /** * * @param document XML Document - * @param namespaces String series of prefix/namespace values separateur by line break + * @param namespaces String series of prefix/namespace values separator by line break * @return {@link PrefixResolver} */ - private static PrefixResolver getPrefixResolverForXPath2(Document document,String namespaces) { - return new PropertiesBasedPrefixResolverForXpath2(document.getDocumentElement(),namespaces); + private static PropertiesBasedPrefixResolverForXpath2 getPrefixResolverForXPath2(Document document, String namespaces) { + return new PropertiesBasedPrefixResolverForXpath2(document.getDocumentElement(), namespaces); } + /** * Validate xpathString is a valid XPath expression * @param document XML Document @@ -598,15 +594,21 @@ private static PrefixResolver getPrefixResolverForXPath2(Document document,Strin * @param namespaces Space separated set of prefix=namespace * @throws TransformerException if expression fails to evaluate */ - public static void validateXPath2(Document document, String xpathString,String namespaces) throws TransformerException { - if (XPathAPI.eval(document, xpathString, getPrefixResolverForXPath2(document,namespaces)) == null) { - // We really should never get here - // because eval will throw an exception - // if xpath is invalid, but whatever, better - // safe - throw new IllegalArgumentException("xpath eval of '" + xpathString + "' was null"); + public static void validateXPath2(Document document, String xpathString, String namespaces) throws TransformerException { + try { + XPath xPath = newXPath(getPrefixResolverForXPath2(document, namespaces)); + if (xPath.evaluate(xpathString, document) == null) { + // We really should never get here + // because eval will throw an exception + // if xpath is invalid, but whatever, better + // safe + throw new IllegalArgumentException("xpath eval of '" + xpathString + "' was null"); + } + } catch (XPathExpressionException|XPathFactoryConfigurationException e) { + throw new TransformerException(e); } } + /** * Fills result * @param result {@link AssertionResult} @@ -618,116 +620,89 @@ public static void computeAssertionResult(AssertionResult result, Document doc, String xPathExpression, boolean isNegated) { + net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); try { - XObject xObject = XPathAPI.eval(doc, xPathExpression, getPrefixResolver(doc)); - switch (xObject.getType()) { - case XObject.CLASS_NODESET: - NodeList nodeList = xObject.nodelist(); - final int len = (nodeList != null) ? nodeList.getLength() : 0; - log.debug("nodeList length {}", len); - // length == 0 means nodelist is null - if (len == 0) { - log.debug("nodeList is null or empty. No match by xpath expression: {}", xPathExpression); - result.setFailure(!isNegated); - result.setFailureMessage("No Nodes Matched " + xPathExpression); - return; + XdmNode xdmNode = builder.build(new DOMSource(doc)); + String namespaces = getPrefixResolver(doc).getNamespacesAsLineDelimitedProperties(); + computeAssertionResultUsingSaxon(result, xdmNode, xPathExpression, namespaces, isNegated); + } catch (Exception e) { + result.setError(true); + result.setFailureMessage("TransformerException: " + unwrapException(e).getMessage() + " for: " + xPathExpression); + } + } + + /*** + * + * @param result The result of xpath2 assertion + * @param xmlFile XML data + * @param xPathQuery XPath Query + * @param namespaces Space separated set of prefix=namespace + * @param isNegated invert result + * @throws SaxonApiException when the parser has problems with the given xml or xpath query + * @throws FactoryConfigurationError when the parser can not be instantiated + */ + public static void computeAssertionResultUsingSaxon(AssertionResult result, String xmlFile, String xPathQuery, + String namespaces, Boolean isNegated) throws SaxonApiException, FactoryConfigurationError { + try (StringReader reader = new StringReader(xmlFile)) { + // We could instantiate it once but might trigger issues in the future + // Sharing of a DocumentBuilder across multiple threads is not recommended. + // However, in the current implementation sharing a DocumentBuilder (once + // initialized) + // will only cause problems if a SchemaValidator is used. + net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); + XdmNode xdmNode = builder.build(new SAXSource(new InputSource(reader))); + computeAssertionResultUsingSaxon(result, xdmNode, xPathQuery, namespaces, isNegated); + } + } + + private static void computeAssertionResultUsingSaxon(AssertionResult result, XdmNode xdmNode, String xPathQuery, + String namespaces, Boolean isNegated) throws SaxonApiException, FactoryConfigurationError { + // generating the cache key + final ImmutablePair key = ImmutablePair.of(xPathQuery, namespaces); + // check the cache + XPathExecutable xPathExecutable; + if (StringUtils.isNotEmpty(xPathQuery)) { + xPathExecutable = XPATH_CACHE.get(key); + } else { + log.warn("Error : {}", JMeterUtils.getResString("xpath2_extractor_empty_query")); + return; + } + if (xPathExecutable != null) { + XPathSelector selector = null; + try { + Document doc; + doc = XPathUtil.makeDocumentBuilder(false, false, false, false).newDocument(); + XObject xObject = XPathAPI.eval(doc, xPathQuery, getPrefixResolverForXPath2(doc, namespaces)); + selector = xPathExecutable.load(); + selector.setContextItem(xdmNode); + XdmValue nodes = selector.evaluate(); + boolean resultOfEval = true; + int length = nodes.size(); + // In case we need to extract everything + if (length == 0) { + resultOfEval = false; + } else if (xObject.getType() == XObject.CLASS_BOOLEAN) { + resultOfEval = Boolean.parseBoolean(nodes.itemAt(0).getStringValue()); } - if (log.isDebugEnabled() && !isNegated) { - for (int i = 0; i < len; i++) { - log.debug("nodeList[{}]: {}", i, nodeList.item(i)); + result.setFailure(isNegated ? resultOfEval : !resultOfEval); + result.setFailureMessage( + isNegated ? "Nodes Matched for " + xPathQuery : "No Nodes Matched for " + xPathQuery); + } catch (ParserConfigurationException | TransformerException e) { // NOSONAR Exception handled by return + result.setError(true); + result.setFailureMessage("Exception: " + e.getMessage() + " for:" + xPathQuery); + } finally { + if (selector != null) { + try { + selector.getUnderlyingXPathContext().setContextItem(null); + } catch (Exception e) { // NOSONAR Ignored on purpose + result.setError(true); + result.setFailureMessage("Exception: " + e.getMessage() + " for:" + xPathQuery); } } - result.setFailure(isNegated); - if (isNegated) { - result.setFailureMessage("Specified XPath was found... Turn off negate if this is not desired"); - } - return; - case XObject.CLASS_BOOLEAN: - boolean resultOfEval = xObject.bool(); - result.setFailure(isNegated ? resultOfEval : !resultOfEval); - result.setFailureMessage(isNegated ? - "Nodes Matched for " + xPathExpression - : "No Nodes Matched for " + xPathExpression); - return; - default: - result.setFailure(true); - result.setFailureMessage("Cannot understand: " + xPathExpression); - return; } - } catch (TransformerException e) { - result.setError(true); - result.setFailureMessage("TransformerException: " + e.getMessage() + " for: " + xPathExpression); } } - - /*** - * - * @param result The result of xpath2 assertion - * @param xmlFile XML data - * @param xPathQuery XPath Query - * @param namespaces Space separated set of prefix=namespace - * @param isNegated invert result - * @throws SaxonApiException when the parser has problems with the given xml or xpath query - * @throws FactoryConfigurationError when the parser can not be instantiated - */ - public static void computeAssertionResultUsingSaxon(AssertionResult result, String xmlFile, String xPathQuery, - String namespaces, Boolean isNegated) throws SaxonApiException, FactoryConfigurationError { - // generating the cache key - final ImmutablePair key = ImmutablePair.of(xPathQuery, namespaces); - // check the cache - XPathExecutable xPathExecutable; - if (StringUtils.isNotEmpty(xPathQuery)) { - xPathExecutable = XPATH_CACHE.get(key); - } else { - log.warn("Error : {}", JMeterUtils.getResString("xpath2_extractor_empty_query")); - return; - } - try (StringReader reader = new StringReader(xmlFile)) { - // We could instantiate it once but might trigger issues in the future - // Sharing of a DocumentBuilder across multiple threads is not recommended. - // However, in the current implementation sharing a DocumentBuilder (once - // initialized) - // will only cause problems if a SchemaValidator is used. - net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); - XdmNode xdmNode = builder.build(new SAXSource(new InputSource(reader))); - if (xPathExecutable != null) { - XPathSelector selector = null; - try { - Document doc; - doc = XPathUtil.makeDocumentBuilder(false, false, false, false).newDocument(); - XObject xObject = XPathAPI.eval(doc, xPathQuery, getPrefixResolverForXPath2(doc, namespaces)); - selector = xPathExecutable.load(); - selector.setContextItem(xdmNode); - XdmValue nodes = selector.evaluate(); - boolean resultOfEval = true; - int length = nodes.size(); - // In case we need to extract everything - if (length == 0) { - resultOfEval = false; - } else if (xObject.getType() == XObject.CLASS_BOOLEAN) { - resultOfEval = Boolean.parseBoolean(nodes.itemAt(0).getStringValue()); - } - result.setFailure(isNegated ? resultOfEval : !resultOfEval); - result.setFailureMessage( - isNegated ? "Nodes Matched for " + xPathQuery : "No Nodes Matched for " + xPathQuery); - } catch (ParserConfigurationException | TransformerException e) { // NOSONAR Exception handled by return - result.setError(true); - result.setFailureMessage("Exception: " + e.getMessage() + " for:" + xPathQuery); - } finally { - if (selector != null) { - try { - selector.getUnderlyingXPathContext().setContextItem(null); - } catch (Exception e) { // NOSONAR Ignored on purpose - result.setError(true); - result.setFailureMessage("Exception: " + e.getMessage() + " for:" + xPathQuery); - } - } - } - } - } - } - /** * Formats XML * @param xml string to format @@ -736,7 +711,7 @@ public static void computeAssertionResultUsingSaxon(AssertionResult result, Stri public static String formatXml(String xml){ try { TransformerFactory factory = TransformerFactory.newInstance(); - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature(FEATURE_SECURE_PROCESSING, true); Transformer serializer= factory.newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); @@ -750,4 +725,23 @@ public static String formatXml(String xml){ } } + private static XPath newXPath(Document document) throws XPathFactoryConfigurationException { + return newXPath(getPrefixResolver(document)); + } + + private static XPath newXPath(NamespaceContext namespaceContext) throws XPathFactoryConfigurationException { + XPathFactoryImpl xPathFactory = new XPathFactoryImpl(); + xPathFactory.setFeature(FEATURE_SECURE_PROCESSING, true); + XPath xPath = xPathFactory.newXPath(); + xPath.setNamespaceContext(namespaceContext); + return xPath; + } + + private static Throwable unwrapException(Throwable t) { + Throwable cause = t.getCause(); + if (cause != null && cause != t) { + return unwrapException(cause); + } + return t; + } } diff --git a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java index e3c1af65198..f9d47f7bfcc 100644 --- a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java +++ b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java @@ -40,6 +40,8 @@ import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import net.sf.saxon.s9api.Processor; @@ -276,4 +278,19 @@ public void testPutValuesForXPathInList() throws ParserConfigurationException, S assertEquals("one", matchs.get(0)); assertEquals("two", matchs.get(1)); } + + @Test + public void testSelectNodeList() throws ParserConfigurationException, SAXException, IOException, TidyException, TransformerException { + String responseData = "onetwo"; + Document testDoc = XPathUtil.makeDocument( + new ByteArrayInputStream(responseData.getBytes(StandardCharsets.UTF_8)), false, false, false, false, + false, false, false, false, false); + String xpathquery = "/book/page"; + NodeList nodeList = XPathUtil.selectNodeList(testDoc, xpathquery); + assertEquals(2, nodeList.getLength()); + Element e0 = (Element) nodeList.item(0); + Element e1 = (Element) nodeList.item(1); + assertEquals("one", e0.getTextContent()); + assertEquals("two", e1.getTextContent()); + } } diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/XPathFileContainer.java b/src/functions/src/main/java/org/apache/jmeter/functions/XPathFileContainer.java index 7c6e998428c..dfa20ebe23b 100644 --- a/src/functions/src/main/java/org/apache/jmeter/functions/XPathFileContainer.java +++ b/src/functions/src/main/java/org/apache/jmeter/functions/XPathFileContainer.java @@ -62,7 +62,7 @@ public XPathFileContainer(String file, String xpath) throws FileNotFoundExceptio nodeList=load(xpath); } - private NodeList load(String xpath) throws IOException, FileNotFoundException, ParserConfigurationException, SAXException, + private NodeList load(String xpath) throws IOException, ParserConfigurationException, SAXException, TransformerException { NodeList nl = null; try ( FileInputStream fis = new FileInputStream(fileName); From 75a5f9e869f5c103e3531edfa6260905562ab308 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 13:06:04 +0100 Subject: [PATCH 02/19] remove no longer used method --- .../org/apache/jmeter/util/XPathUtil.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index 952956dd2d8..4539004c8ad 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -27,7 +27,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletionException; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; @@ -310,26 +309,6 @@ public void fatalError(SAXParseException ex) throws SAXException { } } - /** - * Return value for node including node element - * @param node Node - * @return String - */ - private static String getNodeContent(Node node) { - StringWriter sw = new StringWriter(); - try { - TransformerFactory factory = TransformerFactory.newInstance(); - factory.setFeature(FEATURE_SECURE_PROCESSING, true); - Transformer t = factory.newTransformer(); - t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - t.transform(new DOMSource(node), new StreamResult(sw)); - } catch (TransformerException e) { - sw.write(e.getMessageAndLocation()); - } - return sw.toString(); - } - - /** * @param node {@link Node} * @return String content of node From b0236ee32c51d6c1f8264653c2bb1038d06f9ef4 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 13:23:34 +0100 Subject: [PATCH 03/19] remove remaining use of xalan in xpath code --- .../jmeter/util/PrefixResolverDefault.java | 144 ++++++++++++++++++ .../util/PropertiesBasedPrefixResolver.java | 4 +- ...ropertiesBasedPrefixResolverForXpath2.java | 4 +- .../org/apache/jmeter/util/XPathUtil.java | 12 +- 4 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java diff --git a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java new file mode 100644 index 00000000000..1abc15844e7 --- /dev/null +++ b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java @@ -0,0 +1,144 @@ +/* + * 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.jmeter.util; + +import org.apache.xml.utils.Constants; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +/** + * This class implements a generic PrefixResolver that + * can be used to perform prefix-to-namespace lookup + * for the XPath object. + * @xsl.usage general + */ +public class PrefixResolverDefault +{ + + /** + * The context to resolve the prefix from, if the context + * is not given. + */ + Node m_context; + + /** + * Construct a PrefixResolverDefault object. + * @param xpathExpressionContext The context from + * which XPath expression prefixes will be resolved. + * Warning: This will not work correctly if xpathExpressionContext + * is an attribute node. + */ + public PrefixResolverDefault(Node xpathExpressionContext) + { + m_context = xpathExpressionContext; + } + + /** + * Given a namespace, get the corrisponding prefix. This assumes that + * the PrevixResolver hold's it's own namespace context, or is a namespace + * context itself. + * @param prefix Prefix to resolve. + * @return Namespace that prefix resolves to, or null if prefix + * is not bound. + */ + public String getNamespaceForPrefix(String prefix) + { + return getNamespaceForPrefix(prefix, m_context); + } + + /** + * Given a namespace, get the corrisponding prefix. + * Warning: This will not work correctly if namespaceContext + * is an attribute node. + * @param prefix Prefix to resolve. + * @param namespaceContext Node from which to start searching for a + * xmlns attribute that binds a prefix to a namespace. + * @return Namespace that prefix resolves to, or null if prefix + * is not bound. + */ + public String getNamespaceForPrefix(String prefix, + org.w3c.dom.Node namespaceContext) + { + + Node parent = namespaceContext; + String namespace = null; + + if (prefix.equals("xml")) + { + namespace = Constants.S_XMLNAMESPACEURI; + } + else + { + int type; + + while ((null != parent) && (null == namespace) + && (((type = parent.getNodeType()) == Node.ELEMENT_NODE) + || (type == Node.ENTITY_REFERENCE_NODE))) + { + if (type == Node.ELEMENT_NODE) + { + if (parent.getNodeName().indexOf(prefix+":") == 0) + return parent.getNamespaceURI(); + NamedNodeMap nnm = parent.getAttributes(); + + for (int i = 0; i < nnm.getLength(); i++) + { + Node attr = nnm.item(i); + String aname = attr.getNodeName(); + boolean isPrefix = aname.startsWith("xmlns:"); + + if (isPrefix || aname.equals("xmlns")) + { + int index = aname.indexOf(':'); + String p = isPrefix ? aname.substring(index + 1) : ""; + + if (p.equals(prefix)) + { + namespace = attr.getNodeValue(); + + break; + } + } + } + } + + parent = parent.getParentNode(); + } + } + + return namespace; + } + + /** + * Return the base identifier. + * + * @return null + */ + public String getBaseIdentifier() + { + return null; + } + + /** + * @see PrefixResolver#handlesNullPrefixes() + */ + public boolean handlesNullPrefixes() { + return false; + } + +} diff --git a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java index e4d774b6cc5..f1b45b61232 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java @@ -29,8 +29,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.jorphan.util.JOrphanUtils; -import org.apache.xml.utils.PrefixResolver; -import org.apache.xml.utils.PrefixResolverDefault; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; @@ -38,7 +36,7 @@ import javax.xml.namespace.NamespaceContext; /** - * {@link PrefixResolver} implementation that loads prefix configuration from jmeter property xpath.namespace.config + * PrefixResolver implementation that loads prefix configuration from jmeter property xpath.namespace.config */ public class PropertiesBasedPrefixResolver extends PrefixResolverDefault implements NamespaceContext { private static final Logger log = LoggerFactory.getLogger(PropertiesBasedPrefixResolver.class); diff --git a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java index 946039f08f9..71729bd6ffa 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java @@ -21,14 +21,12 @@ import java.util.Iterator; import java.util.Map; -import org.apache.xml.utils.PrefixResolver; -import org.apache.xml.utils.PrefixResolverDefault; import org.w3c.dom.Node; import javax.xml.namespace.NamespaceContext; /** - * {@link PrefixResolver} implementation that loads prefix configuration from + * PrefixResolver implementation that loads prefix configuration from * jmeter property xpath.namespace.config */ public class PropertiesBasedPrefixResolverForXpath2 extends PrefixResolverDefault implements NamespaceContext { diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index 4539004c8ad..bd84999f287 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -50,12 +50,11 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactoryConfigurationException; +import net.sf.saxon.s9api.ItemType; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.jmeter.assertions.AssertionResult; -import org.apache.xml.utils.PrefixResolver; -import org.apache.xpath.XPathAPI; -import org.apache.xpath.objects.XObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -560,7 +559,7 @@ public static void validateXPath(Document document, String xpathString) throws T * * @param document XML Document * @param namespaces String series of prefix/namespace values separator by line break - * @return {@link PrefixResolver} + * @return {@link PropertiesBasedPrefixResolverForXpath2} */ private static PropertiesBasedPrefixResolverForXpath2 getPrefixResolverForXPath2(Document document, String namespaces) { return new PropertiesBasedPrefixResolverForXpath2(document.getDocumentElement(), namespaces); @@ -651,7 +650,6 @@ private static void computeAssertionResultUsingSaxon(AssertionResult result, Xdm try { Document doc; doc = XPathUtil.makeDocumentBuilder(false, false, false, false).newDocument(); - XObject xObject = XPathAPI.eval(doc, xPathQuery, getPrefixResolverForXPath2(doc, namespaces)); selector = xPathExecutable.load(); selector.setContextItem(xdmNode); XdmValue nodes = selector.evaluate(); @@ -660,13 +658,13 @@ private static void computeAssertionResultUsingSaxon(AssertionResult result, Xdm // In case we need to extract everything if (length == 0) { resultOfEval = false; - } else if (xObject.getType() == XObject.CLASS_BOOLEAN) { + } else if (nodes.itemAt(0).matches(ItemType.BOOLEAN)) { resultOfEval = Boolean.parseBoolean(nodes.itemAt(0).getStringValue()); } result.setFailure(isNegated ? resultOfEval : !resultOfEval); result.setFailureMessage( isNegated ? "Nodes Matched for " + xPathQuery : "No Nodes Matched for " + xPathQuery); - } catch (ParserConfigurationException | TransformerException e) { // NOSONAR Exception handled by return + } catch (ParserConfigurationException e) { // NOSONAR Exception handled by return result.setError(true); result.setFailureMessage("Exception: " + e.getMessage() + " for:" + xPathQuery); } finally { From 41a8eef192188e284c544d4fa20d26d02f74a0d3 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 13:28:21 +0100 Subject: [PATCH 04/19] format issues --- .../jmeter/util/PropertiesBasedPrefixResolver.java | 4 ++-- .../PropertiesBasedPrefixResolverForXpath2.java | 4 ++-- .../java/org/apache/jmeter/util/XPathUtil.java | 14 ++++++-------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java index f1b45b61232..d6df1ee3615 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolver.java @@ -27,14 +27,14 @@ import java.util.Map; import java.util.Properties; +import javax.xml.namespace.NamespaceContext; + import org.apache.commons.lang3.StringUtils; import org.apache.jorphan.util.JOrphanUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import javax.xml.namespace.NamespaceContext; - /** * PrefixResolver implementation that loads prefix configuration from jmeter property xpath.namespace.config */ diff --git a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java index 71729bd6ffa..dbb1fd2cca2 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PropertiesBasedPrefixResolverForXpath2.java @@ -21,10 +21,10 @@ import java.util.Iterator; import java.util.Map; -import org.w3c.dom.Node; - import javax.xml.namespace.NamespaceContext; +import org.w3c.dom.Node; + /** * PrefixResolver implementation that loads prefix configuration from * jmeter property xpath.namespace.config diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index bd84999f287..c11eeedf796 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; +import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -50,8 +51,6 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactoryConfigurationException; -import net.sf.saxon.s9api.ItemType; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.jmeter.assertions.AssertionResult; @@ -67,6 +66,7 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import net.sf.saxon.s9api.ItemType; import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XPathExecutable; @@ -79,8 +79,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; -import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING; - /** * This class provides a few utility methods for dealing with XML/XPath. */ @@ -127,7 +125,7 @@ private static synchronized DocumentBuilderFactory makeDocumentBuilderFactory(bo || documentBuilderFactory.isIgnoringElementContentWhitespace() != whitespace) { // configure the document builder factory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - documentBuilderFactory.setFeature(FEATURE_SECURE_PROCESSING, true); + documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); documentBuilderFactory.setValidating(validate); documentBuilderFactory.setNamespaceAware(namespace); documentBuilderFactory.setIgnoringElementContentWhitespace(whitespace); @@ -528,7 +526,7 @@ private static void addToList(XMLStreamReader reader, List res) { /** * * @param document XML Document - * @return {@link PrefixResolver} + * @return {@link PropertiesBasedPrefixResolver} */ private static PropertiesBasedPrefixResolver getPrefixResolver(Document document) { return new PropertiesBasedPrefixResolver(document.getDocumentElement()); @@ -688,7 +686,7 @@ private static void computeAssertionResultUsingSaxon(AssertionResult result, Xdm public static String formatXml(String xml){ try { TransformerFactory factory = TransformerFactory.newInstance(); - factory.setFeature(FEATURE_SECURE_PROCESSING, true); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); Transformer serializer= factory.newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); @@ -708,7 +706,7 @@ private static XPath newXPath(Document document) throws XPathFactoryConfiguratio private static XPath newXPath(NamespaceContext namespaceContext) throws XPathFactoryConfigurationException { XPathFactoryImpl xPathFactory = new XPathFactoryImpl(); - xPathFactory.setFeature(FEATURE_SECURE_PROCESSING, true); + xPathFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); XPath xPath = xPathFactory.newXPath(); xPath.setNamespaceContext(namespaceContext); return xPath; From c93a69558556f2b9a86e238cb0f2402737009c95 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 13:33:17 +0100 Subject: [PATCH 05/19] remove problematic annotation --- .../main/java/org/apache/jmeter/util/PrefixResolverDefault.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java index 1abc15844e7..2b9034458b7 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java @@ -25,7 +25,6 @@ * This class implements a generic PrefixResolver that * can be used to perform prefix-to-namespace lookup * for the XPath object. - * @xsl.usage general */ public class PrefixResolverDefault { From e7895657da173d1c6056d97ce8accfe2721164d7 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 13:42:58 +0100 Subject: [PATCH 06/19] Update XPathUtil.java --- src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index c11eeedf796..201623bdb77 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -646,8 +646,6 @@ private static void computeAssertionResultUsingSaxon(AssertionResult result, Xdm if (xPathExecutable != null) { XPathSelector selector = null; try { - Document doc; - doc = XPathUtil.makeDocumentBuilder(false, false, false, false).newDocument(); selector = xPathExecutable.load(); selector.setContextItem(xdmNode); XdmValue nodes = selector.evaluate(); @@ -662,9 +660,6 @@ private static void computeAssertionResultUsingSaxon(AssertionResult result, Xdm result.setFailure(isNegated ? resultOfEval : !resultOfEval); result.setFailureMessage( isNegated ? "Nodes Matched for " + xPathQuery : "No Nodes Matched for " + xPathQuery); - } catch (ParserConfigurationException e) { // NOSONAR Exception handled by return - result.setError(true); - result.setFailureMessage("Exception: " + e.getMessage() + " for:" + xPathQuery); } finally { if (selector != null) { try { From cc4ec5e2cfaf8f311ca87e5dc563153b11293653 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 13:56:32 +0100 Subject: [PATCH 07/19] style issue --- .../main/java/org/apache/jmeter/util/PrefixResolverDefault.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java index 2b9034458b7..716f7918121 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java @@ -92,7 +92,9 @@ public String getNamespaceForPrefix(String prefix, if (type == Node.ELEMENT_NODE) { if (parent.getNodeName().indexOf(prefix+":") == 0) + { return parent.getNamespaceURI(); + } NamedNodeMap nnm = parent.getAttributes(); for (int i = 0; i < nnm.getLength(); i++) From a9602fd54fde23649a8347c73b93c9311fe984d6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 14:13:46 +0100 Subject: [PATCH 08/19] adjust test result --- .../java/org/apache/jmeter/assertions/XPathAssertionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java b/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java index b98f32a4b3a..048f79a04cb 100644 --- a/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java +++ b/src/components/src/test/java/org/apache/jmeter/assertions/XPathAssertionTest.java @@ -240,7 +240,8 @@ public void testAssertionNumber() throws Exception { testLog.debug("isError() {} isFailure() {}", res.isError(), res.isFailure()); testLog.debug("failure message: {}", res.getFailureMessage()); assertFalse("Should not be an error", res.isError()); - assertTrue("Should be a failure",res.isFailure()); + //this used to result in isFailure()=true but is now fixed (after switching from Xalan to Saxon for XPath support) + assertFalse("Should not be a failure", res.isFailure()); } @Test From 134ee780479c544697e871f21a4a96996b95d64e Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 15:32:28 +0100 Subject: [PATCH 09/19] Update XPathUtilTest.java --- .../src/test/java/org/apache/jmeter/util/XPathUtilTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java index c258cba5d31..89f1f4dc765 100644 --- a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java +++ b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java @@ -19,6 +19,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -251,7 +252,12 @@ public void testPutValuesForXPathInList() assertEquals("two", matchs.get(1)); matchs=new ArrayList<>(); XPathUtil.putValuesForXPathInList(testDoc, xpathquery, matchs, false); + assertEquals(2, matchs.size()); assertEquals("one", matchs.get(0)); assertEquals("two", matchs.get(1)); + matchs=new ArrayList<>(); + XPathUtil.putValuesForXPathInList(testDoc, "/book/a", matchs, false); + assertEquals(1, matchs.size()); + assertNull(matchs.get(0)); } } From 299423aafeacab15ae691b9382aa10c46260d4ff Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 16:08:53 +0100 Subject: [PATCH 10/19] try to fix some tests --- .../jmeter/extractor/XPath2Extractor.java | 3 +- .../jmeter/extractor/TestXPathExtractor.java | 5 ++-- .../org/apache/jmeter/util/XPathUtil.java | 29 ++++++++++++++----- .../org/apache/jmeter/util/XPathUtilTest.java | 15 ++++++++-- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/components/src/main/java/org/apache/jmeter/extractor/XPath2Extractor.java b/src/components/src/main/java/org/apache/jmeter/extractor/XPath2Extractor.java index 77683d147b5..cf68c314912 100644 --- a/src/components/src/main/java/org/apache/jmeter/extractor/XPath2Extractor.java +++ b/src/components/src/main/java/org/apache/jmeter/extractor/XPath2Extractor.java @@ -22,6 +22,7 @@ import java.util.List; import javax.xml.stream.FactoryConfigurationError; +import javax.xml.transform.TransformerException; import org.apache.jmeter.assertions.AssertionResult; import org.apache.jmeter.processor.PostProcessor; @@ -223,7 +224,7 @@ public void setFragment(boolean selected) { * @throws FactoryConfigurationError */ private void getValuesForXPath(String query, List matchStrings, int matchNumber, String responseData) - throws SaxonApiException, FactoryConfigurationError { + throws SaxonApiException, TransformerException, FactoryConfigurationError { XPathUtil.putValuesForXPathInListUsingSaxon(responseData, query, matchStrings, getFragment(), matchNumber, getNamespaces()); } diff --git a/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java b/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java index 6138fb1dbf7..8ba118c9df5 100644 --- a/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java +++ b/src/components/src/test/java/org/apache/jmeter/extractor/TestXPathExtractor.java @@ -32,6 +32,7 @@ import org.apache.jmeter.threads.JMeterContext; import org.apache.jmeter.threads.JMeterContextService; import org.apache.jmeter.threads.JMeterVariables; +import org.apache.jmeter.util.XPathUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -195,7 +196,7 @@ public void testVariableExtraction() throws Exception { // No text, but using fragment mode extractor.setXPathQuery("//a"); extractor.process(); - assertEquals("", vars.get(VAL_NAME)); + assertEquals(XPathUtil.formatXml(""), XPathUtil.formatXml(vars.get(VAL_NAME))); } @Test @@ -283,7 +284,7 @@ public void testInvalidXpath() throws Exception { if (Locale.getDefault().getLanguage().startsWith(Locale.ENGLISH.getLanguage())) { assertThat( firstResult.getFailureMessage(), - containsString("A location path was expected, but the following token was encountered") + containsString("Unexpected token \"<\" at start of expression") ); } assertEquals("Default", vars.get(VAL_NAME)); diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index 201623bdb77..bae4dfbdda4 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -365,9 +365,9 @@ public static void putValuesForXPathInList(Document document, String xPathQuery, net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); try { XdmNode xdmNode = builder.build(new DOMSource(document)); - putValuesForXPathInListUsingSaxon(xdmNode, xPathQuery, matchStrings, fragment, matchNumber, namespaces); + putValuesForXPathInListUsingSaxon(xdmNode, xPathQuery, matchStrings, fragment, matchNumber, namespaces, true); } catch (SaxonApiException|FactoryConfigurationError e) { - throw new TransformerException(e); + throw new TransformerException(unwrapException(e)); } } @@ -375,7 +375,7 @@ public static void putValuesForXPathInListUsingSaxon( String xmlFile, String xPathQuery, List matchStrings, boolean fragment, int matchNumber, String namespaces) - throws SaxonApiException, FactoryConfigurationError { + throws SaxonApiException, TransformerException, FactoryConfigurationError { try (StringReader reader = new StringReader(xmlFile)) { // We could instantiate it once but might trigger issues in the future // Sharing of a DocumentBuilder across multiple threads is not recommended. @@ -383,15 +383,15 @@ public static void putValuesForXPathInListUsingSaxon( // will only cause problems if a SchemaValidator is used. net.sf.saxon.s9api.DocumentBuilder builder = PROCESSOR.newDocumentBuilder(); XdmNode xdmNode = builder.build(new SAXSource(new InputSource(reader))); - putValuesForXPathInListUsingSaxon(xdmNode, xPathQuery, matchStrings, fragment, matchNumber, namespaces); + putValuesForXPathInListUsingSaxon(xdmNode, xPathQuery, matchStrings, fragment, matchNumber, namespaces, false); } } private static void putValuesForXPathInListUsingSaxon( XdmNode xdmNode, String xPathQuery, List matchStrings, boolean fragment, - int matchNumber, String namespaces) - throws SaxonApiException, FactoryConfigurationError { + int matchNumber, String namespaces, boolean useNullForEmpty) + throws SaxonApiException, TransformerException, FactoryConfigurationError { // generating the cache key final ImmutablePair key = ImmutablePair.of(xPathQuery, namespaces); @@ -399,7 +399,15 @@ private static void putValuesForXPathInListUsingSaxon( //check the cache XPathExecutable xPathExecutable; if(StringUtils.isNotEmpty(xPathQuery)) { - xPathExecutable = XPATH_CACHE.get(key); + try { + xPathExecutable = XPATH_CACHE.get(key); + } catch (RuntimeException e) { + Throwable unwrapped = unwrapException(e); + if (unwrapped instanceof TransformerException) { + throw (TransformerException) unwrapped; + } + throw e; + } } else { log.warn("Error : {}", JMeterUtils.getResString("xpath2_extractor_empty_query")); @@ -421,7 +429,12 @@ private static void putValuesForXPathInListUsingSaxon( matchStrings.add(item.toString()); } else { - matchStrings.add(item.getStringValue()); + String value = item.getStringValue(); + if (useNullForEmpty && value != null && value.isEmpty()) { + matchStrings.add(null); + } else { + matchStrings.add(value); + } } } } else { diff --git a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java index f9d47f7bfcc..fd607b36951 100644 --- a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java +++ b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java @@ -20,6 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; @@ -72,7 +73,7 @@ public void testBug63033() throws SaxonApiException { } @Test - public void testputValuesForXPathInListUsingSaxon() throws SaxonApiException, FactoryConfigurationError{ + public void testputValuesForXPathInListUsingSaxon() throws Exception { String xPathQuery="//Employees/Employee/role"; ArrayList matchStrings = new ArrayList(); boolean fragment = false; @@ -263,10 +264,13 @@ public void testComputeAssertionResultUsingSaxon() throws SaxonApiException, Fac } @Test - public void testPutValuesForXPathInList() throws ParserConfigurationException, SAXException, IOException, TidyException, TransformerException { + public void testPutValuesForXPathInList() + throws ParserConfigurationException, SAXException, IOException, TidyException, TransformerException { String responseData = "onetwo"; Document testDoc = XPathUtil.makeDocument( - new ByteArrayInputStream(responseData.getBytes(StandardCharsets.UTF_8)), false, false, false, false, + new ByteArrayInputStream( + responseData.getBytes(StandardCharsets.UTF_8)), + false, false, false, false, false, false, false, false, false); String xpathquery = "/book/page"; List matchs=new ArrayList<>(); @@ -275,8 +279,13 @@ public void testPutValuesForXPathInList() throws ParserConfigurationException, S assertEquals("two", matchs.get(1)); matchs=new ArrayList<>(); XPathUtil.putValuesForXPathInList(testDoc, xpathquery, matchs, false); + assertEquals(2, matchs.size()); assertEquals("one", matchs.get(0)); assertEquals("two", matchs.get(1)); + matchs=new ArrayList<>(); + XPathUtil.putValuesForXPathInList(testDoc, "/book/a", matchs, false); + assertEquals(1, matchs.size()); + assertNull(matchs.get(0)); } @Test From d84e7fdf91d887d51c40933029bdd771e8221d45 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 16:13:11 +0100 Subject: [PATCH 11/19] Update XPathUtilTest.java --- .../org/apache/jmeter/util/XPathUtilTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java index 89f1f4dc765..dd02987a6f5 100644 --- a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java +++ b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java @@ -45,6 +45,8 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import net.sf.saxon.s9api.Processor; @@ -260,4 +262,19 @@ public void testPutValuesForXPathInList() assertEquals(1, matchs.size()); assertNull(matchs.get(0)); } + + @Test + public void testSelectNodeList() throws ParserConfigurationException, SAXException, IOException, TidyException, TransformerException { + String responseData = "onetwo"; + Document testDoc = XPathUtil.makeDocument( + new ByteArrayInputStream(responseData.getBytes(StandardCharsets.UTF_8)), false, false, false, false, + false, false, false, false, false); + String xpathquery = "/book/page"; + NodeList nodeList = XPathUtil.selectNodeList(testDoc, xpathquery); + assertEquals(2, nodeList.getLength()); + Element e0 = (Element) nodeList.item(0); + Element e1 = (Element) nodeList.item(1); + assertEquals("one", e0.getTextContent()); + assertEquals("two", e1.getTextContent()); + } } From d86846c80bcb4bf36d659e745659c9e85356a60d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 16:32:58 +0100 Subject: [PATCH 12/19] javadoc issue --- .../java/org/apache/jmeter/util/PrefixResolverDefault.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java index 716f7918121..e4d1d7776bf 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java @@ -128,7 +128,7 @@ public String getNamespaceForPrefix(String prefix, /** * Return the base identifier. * - * @return null + * @return null by default */ public String getBaseIdentifier() { @@ -136,7 +136,7 @@ public String getBaseIdentifier() } /** - * @see PrefixResolver#handlesNullPrefixes() + * @return false by default */ public boolean handlesNullPrefixes() { return false; From b2e946dd1228b43fbf83c32b23d238e022a2a313 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 18:12:29 +0100 Subject: [PATCH 13/19] remove xalan constant --- .../java/org/apache/jmeter/util/PrefixResolverDefault.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java index e4d1d7776bf..0d4ab6116f5 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java @@ -17,10 +17,11 @@ package org.apache.jmeter.util; -import org.apache.xml.utils.Constants; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; +import javax.xml.XMLConstants; + /** * This class implements a generic PrefixResolver that * can be used to perform prefix-to-namespace lookup @@ -79,7 +80,7 @@ public String getNamespaceForPrefix(String prefix, if (prefix.equals("xml")) { - namespace = Constants.S_XMLNAMESPACEURI; + namespace = XMLConstants.XML_NS_URI; } else { From 00d937f0cab9d59462841c0c7779a883d9d4b1bc Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 18:20:59 +0100 Subject: [PATCH 14/19] Update PrefixResolverDefault.java --- .../java/org/apache/jmeter/util/PrefixResolverDefault.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java index 0d4ab6116f5..f760a7b2417 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java +++ b/src/core/src/main/java/org/apache/jmeter/util/PrefixResolverDefault.java @@ -17,11 +17,11 @@ package org.apache.jmeter.util; +import javax.xml.XMLConstants; + import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import javax.xml.XMLConstants; - /** * This class implements a generic PrefixResolver that * can be used to perform prefix-to-namespace lookup From d0e380d9e11d420d7578c0a70bebcbd0a6834b9b Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 20:48:40 +0100 Subject: [PATCH 15/19] more work on removing xalan --- extras/build.xml | 25 +-------------------- gradle.properties | 1 - lib/aareadme.txt | 12 +--------- src/bom/build.gradle.kts | 2 -- src/core/build.gradle.kts | 5 ----- src/dist/src/dist/expected_release_jars.csv | 2 -- 6 files changed, 2 insertions(+), 45 deletions(-) diff --git a/extras/build.xml b/extras/build.xml index 6d950371100..8f739ca7edd 100644 --- a/extras/build.xml +++ b/extras/build.xml @@ -105,20 +105,13 @@ - - - - - - Report generated at ${report.datestamp} - + - - - - - - - - - - - Cannot find all xalan and/or serialiser jars - The XSLT formatting may not work correctly. - Check you have xalan and serializer jars in ${lib.dir} - - - diff --git a/gradle.properties b/gradle.properties index 874c65a5204..c66221b8add 100644 --- a/gradle.properties +++ b/gradle.properties @@ -134,7 +134,6 @@ spock-core.version=2.1-groovy-3.0 springframework.version=4.3.17.RELEASE svgSalamander.version=1.1.2.4 tika.version=1.28.3 -xalan.version=2.7.2 xercesImpl.version=2.12.2 xml-apis.version=1.4.01 xmlgraphics-commons.version=2.7 diff --git a/lib/aareadme.txt b/lib/aareadme.txt index ee01ff8812d..4db7c7f48e1 100644 --- a/lib/aareadme.txt +++ b/lib/aareadme.txt @@ -237,11 +237,6 @@ rsyntaxtextarea-3.0.4 http://fifesoft.com/rsyntaxtextarea/ - syntax coloration -serialiser-2.7.1 ----------------- -http://www.apache.org/dyn/closer.cgi/xml/xalan-j -- xalan - slf4j-api-1.7.28 ---------------- http://www.slf4j.org/ @@ -258,7 +253,7 @@ commons-dbcp2-2.5.0 (org.apache.commons.dbcp2) -------------------------- - DataSourceElement (JDBC) -Saxon-HE-9.9.1-5 (net.sf.saxon) +Saxon-HE-11.3 (net.sf.saxon) -------------------------- - XPath2Extractor (XML) @@ -267,11 +262,6 @@ velocity-1.7 http://velocity.apache.org/download.cgi - Anakia (create documentation) Not used by JMeter runtime -xalan_2.7.1 ------------ -http://www.apache.org/dyn/closer.cgi/xml/xalan-j -+org.apache.xalan|xml|xpath - xercesImpl-2.12.0 ---------------- http://xerces.apache.org/xerces2-j/download.cgi diff --git a/src/bom/build.gradle.kts b/src/bom/build.gradle.kts index 79e4e4c548f..23fe0860155 100644 --- a/src/bom/build.gradle.kts +++ b/src/bom/build.gradle.kts @@ -162,8 +162,6 @@ dependencies { apiv("org.slf4j:slf4j-api", "slf4j") apiv("org.spockframework:spock-core") apiv("oro:oro") - apiv("xalan:serializer", "xalan") - apiv("xalan:xalan", "xalan") apiv("xerces:xercesImpl") apiv("xml-apis:xml-apis") apiv("xmlpull:xmlpull") diff --git a/src/core/build.gradle.kts b/src/core/build.gradle.kts index db221daf451..b957dcaa8d9 100644 --- a/src/core/build.gradle.kts +++ b/src/core/build.gradle.kts @@ -50,11 +50,6 @@ dependencies { api("oro:oro") { because("Perl5Matcher org.apache.jmeter.util.JMeterUtils.getMatcher()") } - api("xalan:xalan") { - because("PropertiesBasedPrefixResolver extends PrefixResolverDefault") - } - // Note: Saxon should go AFTER xalan so xalan XSLT is used - // org.apache.jmeter.util.XPathUtilTest.testFormatXmlSimple assumes xalan transformer api("net.sf.saxon:Saxon-HE") { because("XPathUtil: throws SaxonApiException") } diff --git a/src/dist/src/dist/expected_release_jars.csv b/src/dist/src/dist/expected_release_jars.csv index c032b63b412..33e71bea67a 100644 --- a/src/dist/src/dist/expected_release_jars.csv +++ b/src/dist/src/dist/expected_release_jars.csv @@ -118,7 +118,6 @@ 1383644,rhino-1.7.14.jar 1248250,rsyntaxtextarea-3.2.0.jar 5111311,Saxon-HE-11.3.jar -276420,serializer-2.7.2.jar 41125,slf4j-api-1.7.36.jar 301279,svgSalamander-1.1.2.4.jar 10192,swing-extensions-laf-support-0.1.3.jar @@ -126,7 +125,6 @@ 735701,tika-core-1.28.3.jar 1587751,tika-parsers-1.28.3.jar 174661,vis-svg-portable-jvm-2.2.1.jar -3154938,xalan-2.7.2.jar 1446149,xercesImpl-2.12.2.jar 220536,xml-apis-1.4.01.jar 85686,xml-apis-ext-1.3.04.jar From c360b924e44b486063690c4c5f07b6348a7a1311 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 21:03:07 +0100 Subject: [PATCH 16/19] update test expectation --- .../java/org/apache/jmeter/util/XPathUtilTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java index 4722cdbadff..f7c9e0faebb 100644 --- a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java +++ b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java @@ -147,7 +147,7 @@ public void testnamespacesParse(String namespaces, String key, String value, int @Test public void testFormatXmlSimple() { assertThat(XPathUtil.formatXml("Test"), - CoreMatchers.is("" + CoreMatchers.is("" + lineSeparator + "Test" + lineSeparator)); } @@ -156,11 +156,12 @@ public void testFormatXmlComplex() { assertThat( XPathUtil.formatXml( "..."), - CoreMatchers.is(String.join(lineSeparator, "", - " ", - " ", - " ", - " ...", + CoreMatchers.is(String.join(lineSeparator, "", + "", + " ", + " ", + " ", + " ...", ""))); } From 48593093c13dd6a04c5aa2acb3b67c414cfa887d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 21:15:09 +0100 Subject: [PATCH 17/19] Update XPathUtil.java --- .../src/main/java/org/apache/jmeter/util/XPathUtil.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java index bae4dfbdda4..ca5a11938fd 100644 --- a/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java +++ b/src/core/src/main/java/org/apache/jmeter/util/XPathUtil.java @@ -42,7 +42,6 @@ import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; @@ -66,6 +65,7 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import net.sf.saxon.jaxp.SaxonTransformerFactory; import net.sf.saxon.s9api.ItemType; import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.SaxonApiException; @@ -693,11 +693,12 @@ private static void computeAssertionResultUsingSaxon(AssertionResult result, Xdm */ public static String formatXml(String xml){ try { - TransformerFactory factory = TransformerFactory.newInstance(); + SaxonTransformerFactory factory = new SaxonTransformerFactory(); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - Transformer serializer= factory.newTransformer(); + Transformer serializer = factory.newTransformer(); + serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); - serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + //serializer.setOutputProperty("{http://saxon.sf.net/}indent-spaces", "2"); Source xmlSource = new SAXSource(new InputSource(new StringReader(xml))); StringWriter stringWriter = new StringWriter(); StreamResult res = new StreamResult(stringWriter); From e9bb92b9875f2b2cd9cb0e6477de8b699ad93b34 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 21:16:25 +0100 Subject: [PATCH 18/19] Update expected_release_jars.csv --- src/dist/src/dist/expected_release_jars.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dist/src/dist/expected_release_jars.csv b/src/dist/src/dist/expected_release_jars.csv index 33e71bea67a..c032b63b412 100644 --- a/src/dist/src/dist/expected_release_jars.csv +++ b/src/dist/src/dist/expected_release_jars.csv @@ -118,6 +118,7 @@ 1383644,rhino-1.7.14.jar 1248250,rsyntaxtextarea-3.2.0.jar 5111311,Saxon-HE-11.3.jar +276420,serializer-2.7.2.jar 41125,slf4j-api-1.7.36.jar 301279,svgSalamander-1.1.2.4.jar 10192,swing-extensions-laf-support-0.1.3.jar @@ -125,6 +126,7 @@ 735701,tika-core-1.28.3.jar 1587751,tika-parsers-1.28.3.jar 174661,vis-svg-portable-jvm-2.2.1.jar +3154938,xalan-2.7.2.jar 1446149,xercesImpl-2.12.2.jar 220536,xml-apis-1.4.01.jar 85686,xml-apis-ext-1.3.04.jar From 2cecac40b482fff0216dce28bb8c079bb037aca2 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 23 Jul 2022 21:34:10 +0100 Subject: [PATCH 19/19] Update XPathUtilTest.java --- .../src/test/java/org/apache/jmeter/util/XPathUtilTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java index f7c9e0faebb..70c1f344371 100644 --- a/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java +++ b/src/core/src/test/java/org/apache/jmeter/util/XPathUtilTest.java @@ -147,8 +147,8 @@ public void testnamespacesParse(String namespaces, String key, String value, int @Test public void testFormatXmlSimple() { assertThat(XPathUtil.formatXml("Test"), - CoreMatchers.is("" + lineSeparator - + "Test" + lineSeparator)); + CoreMatchers.is("" + "\n" + + "Test" + "\n")); } @Test @@ -156,7 +156,7 @@ public void testFormatXmlComplex() { assertThat( XPathUtil.formatXml( "..."), - CoreMatchers.is(String.join(lineSeparator, "", + CoreMatchers.is(String.join("\n", "", "", " ", " ",