Limit the TCCL when instantiating UntrustedXML 67/77267/4
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 24 Oct 2018 21:12:02 +0000 (23:12 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 26 Oct 2018 10:03:31 +0000 (12:03 +0200)
JAXP newInstance() methods can end up going through ServiceLoader
with TCCL as its ClassLoader, which means our constants may end up
pointing to a ClassLoader beyond our normal visibility.

This means we can end up picking up a different factory depending
on where we are called from and also can mean the TCCL cannot be
garbage-collected.

Force the TCCL to be the ClassLoader holding UntrustedXML, thus
establishing tight coupling between JAXP implementation discovery
mechanism and UntrustedXML.

JIRA: CONTROLLER-1867
Change-Id: Icacb0e193c7360c73bd07bc5c815bf803ca9ac44
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
common/util/src/main/java/org/opendaylight/yangtools/util/xml/UntrustedXML.java

index 669715bd731eb224a23708b09f8add11b43f4e19..9ee3188be48173244f4c226e0a0227442a572494 100644 (file)
@@ -11,7 +11,7 @@ import com.google.common.annotations.Beta;
 import java.io.InputStream;
 import java.io.Reader;
 import java.nio.charset.Charset;
-import javax.annotation.Nonnull;
+import java.util.function.Supplier;
 import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -21,6 +21,8 @@ import javax.xml.parsers.SAXParserFactory;
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamReader;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXNotRecognizedException;
 import org.xml.sax.SAXNotSupportedException;
@@ -35,7 +37,7 @@ public final class UntrustedXML {
     private static final DocumentBuilderFactory DBF;
 
     static {
-        final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
+        final DocumentBuilderFactory f = getLimited(DocumentBuilderFactory::newInstance);
         f.setCoalescing(true);
         f.setExpandEntityReferences(false);
         f.setIgnoringElementContentWhitespace(true);
@@ -57,7 +59,7 @@ public final class UntrustedXML {
     private static final SAXParserFactory SPF;
 
     static {
-        final SAXParserFactory f = SAXParserFactory.newInstance();
+        final SAXParserFactory f = getLimited(SAXParserFactory::newInstance);
         f.setNamespaceAware(true);
         f.setXIncludeAware(false);
         try {
@@ -76,8 +78,7 @@ public final class UntrustedXML {
     private static final XMLInputFactory XIF;
 
     static {
-        final XMLInputFactory f = XMLInputFactory.newInstance();
-
+        final XMLInputFactory f = getLimited(XMLInputFactory::newInstance);
         f.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
         f.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE);
         f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
@@ -97,7 +98,7 @@ public final class UntrustedXML {
      * @return A new DocumentBuilder
      * @throws UnsupportedOperationException if the runtime fails to instantiate a good enough builder
      */
-    public static @Nonnull DocumentBuilder newDocumentBuilder() {
+    public static @NonNull DocumentBuilder newDocumentBuilder() {
         try {
             return DBF.newDocumentBuilder();
         } catch (ParserConfigurationException e) {
@@ -112,7 +113,7 @@ public final class UntrustedXML {
      * @return A new SAXParser
      * @throws UnsupportedOperationException if the runtime fails to instantiate a good enough builder
      */
-    public static @Nonnull SAXParser newSAXParser() {
+    public static @NonNull SAXParser newSAXParser() {
         try {
             return SPF.newSAXParser();
         } catch (ParserConfigurationException | SAXException e) {
@@ -127,7 +128,7 @@ public final class UntrustedXML {
      * @return A new XMLStreamReader
      * @throws XMLStreamException when the underlying factory throws it
      */
-    public static @Nonnull XMLStreamReader createXMLStreamReader(final InputStream stream) throws XMLStreamException {
+    public static @NonNull XMLStreamReader createXMLStreamReader(final InputStream stream) throws XMLStreamException {
         return XIF.createXMLStreamReader(stream);
     }
 
@@ -138,7 +139,7 @@ public final class UntrustedXML {
      * @return A new XMLStreamReader
      * @throws XMLStreamException when the underlying factory throws it
      */
-    public static @Nonnull XMLStreamReader createXMLStreamReader(final InputStream stream, final Charset charset)
+    public static @NonNull XMLStreamReader createXMLStreamReader(final InputStream stream, final Charset charset)
             throws XMLStreamException {
         return XIF.createXMLStreamReader(stream, charset.name());
     }
@@ -150,7 +151,12 @@ public final class UntrustedXML {
      * @return A new XMLStreamReader
      * @throws XMLStreamException when the underlying factory throws it
      */
-    public static @Nonnull XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
+    public static @NonNull XMLStreamReader createXMLStreamReader(final Reader reader) throws XMLStreamException {
         return XIF.createXMLStreamReader(reader);
     }
+
+    private static <T> T getLimited(final @NonNull Supplier<T> supplier) {
+        final ClassLoader loader = UntrustedXML.class.getClassLoader();
+        return loader == null ? supplier.get() : ClassLoaderUtils.getWithClassLoader(loader, supplier);
+    }
 }