src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java
changeset 59240 b3116877866f
parent 54731 81de17a33575
--- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java	Sun Nov 24 01:03:33 2019 +0100
+++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java	Thu Oct 10 17:36:38 2019 +0300
@@ -23,16 +23,22 @@
 package com.sun.org.apache.xml.internal.security.utils;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.math.BigInteger;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Base64;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.ArrayBlockingQueue;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -48,6 +54,8 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 /**
  * DOM and XML accessibility and comfort functions.
@@ -58,6 +66,9 @@
     private static boolean ignoreLineBreaks =
         AccessController.doPrivileged(
             (PrivilegedAction<Boolean>) () -> Boolean.getBoolean("com.sun.org.apache.xml.internal.security.ignoreLineBreaks"));
+    private static int parserPoolSize =
+        AccessController.doPrivileged(
+            (PrivilegedAction<Integer>) () -> Integer.getInteger("com.sun.org.apache.xml.internal.security.parser.pool-size", 20));
 
     private static volatile String dsPrefix = "ds";
     private static volatile String ds11Prefix = "dsig11";
@@ -67,6 +78,11 @@
     private static final com.sun.org.slf4j.internal.Logger LOG =
         com.sun.org.slf4j.internal.LoggerFactory.getLogger(XMLUtils.class);
 
+    private static final Map<ClassLoader, Queue<DocumentBuilder>> DOCUMENT_BUILDERS =
+        Collections.synchronizedMap(new WeakHashMap<ClassLoader, Queue<DocumentBuilder>>());
+
+    private static final Map<ClassLoader, Queue<DocumentBuilder>> DOCUMENT_BUILDERS_DISALLOW_DOCTYPE =
+        Collections.synchronizedMap(new WeakHashMap<ClassLoader, Queue<DocumentBuilder>>());
 
     /**
      * Constructor XMLUtils
@@ -147,7 +163,7 @@
         if (rootNode == exclude) {
             return;
         }
-        switch (rootNode.getNodeType()) {
+        switch (rootNode.getNodeType()) { //NOPMD
         case Node.ELEMENT_NODE:
             result.add(rootNode);
             Element el = (Element)rootNode;
@@ -172,20 +188,19 @@
                 }
                 getSetRec(r, result, exclude, com);
             }
-            return;
+            break;
         case Node.COMMENT_NODE:
             if (com) {
                 result.add(rootNode);
             }
-            return;
+            break;
         case Node.DOCUMENT_TYPE_NODE:
-            return;
+            break;
         default:
             result.add(rootNode);
         }
     }
 
-
     /**
      * Outputs a DOM tree to an {@link OutputStream}.
      *
@@ -960,18 +975,105 @@
         return true;
     }
 
-    public static DocumentBuilder createDocumentBuilder(boolean validating)
-            throws ParserConfigurationException {
+    public static Document newDocument() throws ParserConfigurationException {
+        ClassLoader loader = getContextClassLoader();
+        if (loader == null) {
+            loader = getClassLoader(XMLUtils.class);
+        }
+        // If the ClassLoader is null then just create a DocumentBuilder and use it
+        if (loader == null) {
+            DocumentBuilder documentBuilder = buildDocumentBuilder(true);
+            return documentBuilder.newDocument();
+        }
+
+        Queue<DocumentBuilder> queue = getDocumentBuilderQueue(true, loader);
+        DocumentBuilder documentBuilder = getDocumentBuilder(true, queue);
+        Document doc = documentBuilder.newDocument();
+        repoolDocumentBuilder(documentBuilder, queue);
+        return doc;
+    }
+
+    public static Document read(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException {
+        return read(inputStream, true);
+    }
+
+    public static Document read(InputStream inputStream, boolean disAllowDocTypeDeclarations) throws ParserConfigurationException, SAXException, IOException {
+        ClassLoader loader = getContextClassLoader();
+        if (loader == null) {
+            loader = getClassLoader(XMLUtils.class);
+        }
+        // If the ClassLoader is null then just create a DocumentBuilder and use it
+        if (loader == null) {
+            DocumentBuilder documentBuilder = buildDocumentBuilder(disAllowDocTypeDeclarations);
+            return documentBuilder.parse(inputStream);
+        }
+
+        Queue<DocumentBuilder> queue = getDocumentBuilderQueue(disAllowDocTypeDeclarations, loader);
+        DocumentBuilder documentBuilder = getDocumentBuilder(disAllowDocTypeDeclarations, queue);
+        Document doc = documentBuilder.parse(inputStream);
+        repoolDocumentBuilder(documentBuilder, queue);
+        return doc;
+    }
+
+    public static Document read(String uri, boolean disAllowDocTypeDeclarations)
+        throws ParserConfigurationException, SAXException, IOException {
+        ClassLoader loader = getContextClassLoader();
+        if (loader == null) {
+            loader = getClassLoader(XMLUtils.class);
+        }
+        // If the ClassLoader is null then just create a DocumentBuilder and use it
+        if (loader == null) {
+            DocumentBuilder documentBuilder = buildDocumentBuilder(disAllowDocTypeDeclarations);
+            return documentBuilder.parse(uri);
+        }
+
+        Queue<DocumentBuilder> queue = getDocumentBuilderQueue(disAllowDocTypeDeclarations, loader);
+        DocumentBuilder documentBuilder = getDocumentBuilder(disAllowDocTypeDeclarations, queue);
+        Document doc = documentBuilder.parse(uri);
+        repoolDocumentBuilder(documentBuilder, queue);
+        return doc;
+    }
+
+    public static Document read(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
+        return read(inputSource, true);
+    }
+
+    public static Document read(InputSource inputSource, boolean disAllowDocTypeDeclarations)
+        throws ParserConfigurationException, SAXException, IOException {
+        ClassLoader loader = getContextClassLoader();
+        if (loader == null) {
+            loader = getClassLoader(XMLUtils.class);
+        }
+        // If the ClassLoader is null then just create a DocumentBuilder and use it
+        if (loader == null) {
+            DocumentBuilder documentBuilder = buildDocumentBuilder(disAllowDocTypeDeclarations);
+            return documentBuilder.parse(inputSource);
+        }
+
+        Queue<DocumentBuilder> queue = getDocumentBuilderQueue(disAllowDocTypeDeclarations, loader);
+        DocumentBuilder documentBuilder = getDocumentBuilder(disAllowDocTypeDeclarations, queue);
+        Document doc = documentBuilder.parse(inputSource);
+        repoolDocumentBuilder(documentBuilder, queue);
+        return doc;
+    }
+
+    /**
+     * @deprecated Use XMLUtils.read instead to directly read a document.
+     */
+    @Deprecated
+    public static DocumentBuilder createDocumentBuilder(boolean validating) throws ParserConfigurationException {
         return createDocumentBuilder(validating, true);
     }
 
-    // The current implementation does not throw a ParserConfigurationException.
-    // Kept here in case we create the DocumentBuilder inline again.
+    /**
+     * @deprecated Use XMLUtils.read instead to directly read a document.
+     */
+    @Deprecated
     public static DocumentBuilder createDocumentBuilder(
         boolean validating, boolean disAllowDocTypeDeclarations
     ) throws ParserConfigurationException {
         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
-        dfactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        dfactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
         if (disAllowDocTypeDeclarations) {
             dfactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
         }
@@ -981,6 +1083,14 @@
     }
 
     /**
+     * @deprecated This method has no effect in Santuario 2.1.4
+     */
+    @Deprecated
+    public static boolean repoolDocumentBuilder(DocumentBuilder db) {
+        return true;
+    }
+
+    /**
      * Returns a byte-array representation of a {@code {@link BigInteger}}.
      * No sign-bit is output.
      *
@@ -1024,4 +1134,64 @@
 
         return resizedBytes;
     }
+
+    private static Queue<DocumentBuilder> getDocumentBuilderQueue(boolean disAllowDocTypeDeclarations, ClassLoader loader) throws ParserConfigurationException {
+        Map<ClassLoader, Queue<DocumentBuilder>> docBuilderCache =
+            disAllowDocTypeDeclarations ? DOCUMENT_BUILDERS_DISALLOW_DOCTYPE : DOCUMENT_BUILDERS;
+        Queue<DocumentBuilder> queue = docBuilderCache.get(loader);
+        if (queue == null) {
+            queue = new ArrayBlockingQueue<>(parserPoolSize);
+            docBuilderCache.put(loader, queue);
+        }
+
+        return queue;
+    }
+
+    private static DocumentBuilder getDocumentBuilder(boolean disAllowDocTypeDeclarations, Queue<DocumentBuilder> queue) throws ParserConfigurationException {
+        DocumentBuilder db = queue.poll();
+        if (db == null) {
+            db = buildDocumentBuilder(disAllowDocTypeDeclarations);
+        }
+        return db;
+    }
+
+    private static DocumentBuilder buildDocumentBuilder(boolean disAllowDocTypeDeclarations) throws ParserConfigurationException {
+        DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
+        f.setNamespaceAware(true);
+        f.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", disAllowDocTypeDeclarations);
+        return f.newDocumentBuilder();
+    }
+
+    private static void repoolDocumentBuilder(DocumentBuilder db, Queue<DocumentBuilder> queue) {
+        if (queue != null) {
+            db.reset();
+            queue.offer(db);
+        }
+    }
+
+    private static ClassLoader getContextClassLoader() {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                public ClassLoader run() {
+                    return Thread.currentThread().getContextClassLoader();
+                }
+            });
+        }
+        return Thread.currentThread().getContextClassLoader();
+    }
+
+    private static ClassLoader getClassLoader(final Class<?> clazz) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+                public ClassLoader run() {
+                    return clazz.getClassLoader();
+                }
+            });
+        }
+        return clazz.getClassLoader();
+    }
+
 }