From 97fe9eb3b583204c9eb20d9374fdd2f3b33a22f1 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 5 Jul 2021 16:15:55 -0700 Subject: [PATCH] Fix #483 --- release-notes/VERSION-2.x | 2 + .../jackson/dataformat/xml/XmlFactory.java | 8 ++- .../dataformat/xml/XmlFactoryBuilder.java | 58 +++++++++++++++---- .../failing/UnwrappedAndList299DeserTest.java | 2 +- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index bc8096796..40f0ae668 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -17,6 +17,8 @@ Project: jackson-dataformat-xml #474: Empty String ("") parsed as 0 for int even if DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES enabled (note: actual fix in `jackson-databind) +#483: Explicitly pass ClassLoader of XmlFactory when creating Stax input/output factory, + instead of context ClassLoader - Rename `XmlFactoryBuilder` methods "inputFactory()"->"xmlInputFactory()", "outputFactory()" -> "xmlOutputFactory()" - Woodstox dependency 6.2.6 (from 6.2.4) diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java index 6968ef955..e41f11b1e 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java @@ -111,14 +111,18 @@ protected XmlFactory(ObjectCodec oc, int xpFeatures, int xgFeatures, _xmlGeneratorFeatures = xgFeatures; _cfgNameForTextElement = nameForTextElem; if (xmlIn == null) { - xmlIn = XMLInputFactory.newInstance(); + // 05-Jul-2021, tatu: as per [dataformat-xml#483], specify ClassLoader + xmlIn = XMLInputFactory.newFactory(XMLInputFactory.class.getName(), + getClass().getClassLoader()); // as per [dataformat-xml#190], disable external entity expansion by default xmlIn.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); // and ditto wrt [dataformat-xml#211], SUPPORT_DTD xmlIn.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); } if (xmlOut == null) { - xmlOut = XMLOutputFactory.newInstance(); + // 05-Jul-2021, tatu: as per [dataformat-xml#483], specify ClassLoader + xmlOut = XMLOutputFactory.newFactory(XMLOutputFactory.class.getName(), + getClass().getClassLoader()); } _initFactories(xmlIn, xmlOut); _xmlInputFactory = xmlIn; diff --git a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java index c6f282762..2c83ddd96 100644 --- a/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java +++ b/src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactoryBuilder.java @@ -8,11 +8,8 @@ import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; /** - * {@link com.fasterxml.jackson.core.TSFBuilder} - * implementation for constructing {@link XmlFactory} - * instances. - * - * @since 3.0 + * {@link com.fasterxml.jackson.core.TSFBuilder} implementation + * for constructing {@link XmlFactory} instances. */ public class XmlFactoryBuilder extends TSFBuilder { @@ -51,11 +48,21 @@ public class XmlFactoryBuilder extends TSFBuilder *

* Name used for pseudo-property used for returning XML Text value (which does * not have actual element name to use). Defaults to empty String, but - * may be changed for inter-operability reasons: JAXB, for example, uses + * may be changed for interoperability reasons: JAXB, for example, uses * "value" as name. */ protected String _nameForTextElement; + /** + * Optional {@link ClassLoader} to use for constructing + * {@link XMLInputFactory} and {@kink XMLOutputFactory} instances if + * not explicitly specified by caller. If not specified, will + * default to {@link ClassLoader} that loaded this class. + * + * @since 2.13 + */ + protected ClassLoader _classLoaderForStax; + /* /********************************************************** /* Life cycle @@ -65,6 +72,7 @@ public class XmlFactoryBuilder extends TSFBuilder protected XmlFactoryBuilder() { _formatParserFeatures = XmlFactory.DEFAULT_XML_PARSER_FEATURE_FLAGS; _formatGeneratorFeatures = XmlFactory.DEFAULT_XML_GENERATOR_FEATURE_FLAGS; + _classLoaderForStax = null; } public XmlFactoryBuilder(XmlFactory base) { @@ -74,6 +82,7 @@ public XmlFactoryBuilder(XmlFactory base) { _xmlInputFactory = base._xmlInputFactory; _xmlOutputFactory = base._xmlOutputFactory; _nameForTextElement = base._cfgNameForTextElement; + _classLoaderForStax = null; } // // // Accessors @@ -90,8 +99,11 @@ public XMLInputFactory xmlInputFactory() { return _xmlInputFactory; } - protected static XMLInputFactory defaultInputFactory() { - XMLInputFactory xmlIn = XMLInputFactory.newInstance(); + protected XMLInputFactory defaultInputFactory() { + // 05-Jul-2021, tatu: as per [dataformat-xml#483], consider ClassLoader + XMLInputFactory xmlIn = XMLInputFactory.newFactory(XMLInputFactory.class.getName(), + staxClassLoader()); + // as per [dataformat-xml#190], disable external entity expansion by default xmlIn.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); // and ditto wrt [dataformat-xml#211], SUPPORT_DTD @@ -106,13 +118,21 @@ public XMLOutputFactory xmlOutputFactory() { return _xmlOutputFactory; } - protected static XMLOutputFactory defaultOutputFactory() { - XMLOutputFactory xmlOut = XMLOutputFactory.newInstance(); + protected XMLOutputFactory defaultOutputFactory() { + // 05-Jul-2021, tatu: as per [dataformat-xml#483], consider ClassLoader + XMLOutputFactory xmlOut = XMLOutputFactory.newFactory(XMLOutputFactory.class.getName(), + staxClassLoader()); // [dataformat-xml#326]: Better ensure namespaces get built properly: xmlOut.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE); return xmlOut; } + // @since 2.13 + protected ClassLoader staxClassLoader() { + return (_classLoaderForStax == null) ? + getClass().getClassLoader() : _classLoaderForStax; + } + // // // Parser features public XmlFactoryBuilder enable(FromXmlParser.Feature f) { @@ -217,11 +237,27 @@ public XmlFactoryBuilder outputFactory(XMLOutputFactory xmlOut) { return xmlOutputFactory(xmlOut); } + /** + * Method that can be used to specific {@link ClassLoader} for creating + * {@link XMLInputFactory} and {@link XMLOutputFactory} instances if + * those are not explicitly defined by caller: passed to respective + * {@code newFactory()} methods. + *
+ * NOTE: recommended approach is to explicitly pass {@link XMLInputFactory} + * and {@link XMLOutputFactory} methods instead of relying on JDK SPI + * mechanism. + * + * @since 2.13 + */ + public XmlFactoryBuilder staxClassLoader(ClassLoader cl) { + _classLoaderForStax = cl; + return _this(); + } + // // // Actual construction @Override public XmlFactory build() { - // 28-Dec-2017, tatu: No special settings beyond base class ones, so: return new XmlFactory(this); } } diff --git a/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/UnwrappedAndList299DeserTest.java b/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/UnwrappedAndList299DeserTest.java index 626db2024..7304db20d 100644 --- a/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/UnwrappedAndList299DeserTest.java +++ b/src/test/java/com/fasterxml/jackson/dataformat/xml/failing/UnwrappedAndList299DeserTest.java @@ -53,7 +53,7 @@ public void testXmlMarshallingAndUnmarshalling() throws Exception { assertEquals(request.composite.number, anotherRequest.composite.number); assertEquals("ABC", anotherRequest.composite.messageId); - assertEquals(new Integer(123), anotherRequest.composite.number); + assertEquals(Integer.valueOf(123), anotherRequest.composite.number); assertEquals(2, anotherRequest.composite.headers.size()); assertEquals("headerID1", anotherRequest.composite.headers.get(0).headerId); assertEquals("headerID2", anotherRequest.composite.headers.get(1).headerId);