Merge branch 'SANTUARIO-504' into trunk
git-svn-id: https://svn.apache.org/repos/asf/santuario/xml-security-java/trunk@1862064 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/xml/security/utils/WeakObjectPool.java b/src/main/java/org/apache/xml/security/utils/WeakObjectPool.java
deleted file mode 100644
index adf3dba..0000000
--- a/src/main/java/org/apache/xml/security/utils/WeakObjectPool.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * 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.xml.security.utils;
-
-import java.lang.ref.WeakReference;
-import java.util.Collections;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingDeque;
-
-/**
- * Abstract base class for pooling objects. The two public methods are
- * {@link #getObject()} and ({@link #repool(Object)}. Objects are held through
- * weak references so even objects that are not repooled are subject to garbage collection.
- *
- * Subclasses must implement the abstract {@link #createObject()}.
- * <p>
- *
- * Internally, the pool is stored in a java.util.concurrent.LinkedBlockingDeque
- * instance.
- */
-public abstract class WeakObjectPool<T, E extends Throwable> {
-
- private static final Integer MARKER_VALUE = Integer.MAX_VALUE;//once here rather than auto-box it?
-
- /** created, available objects to be checked out to clients */
- private final BlockingQueue<WeakReference<T>> available;
-
- /**
- * Synchronized, identity map of loaned out objects (WeakHashMap);
- * use to ensure we repool only object originating from here
- * and do it once.
- */
- private final Map<T, Integer> onLoan;
-
- /**
- * The lone constructor.
- */
- protected WeakObjectPool() {
- //alternative implementations: ArrayBlockingQueue has a fixed size
- // PriorityBlockingQueue: requires a dummy comparator; less memory but more overhead
- available = new LinkedBlockingDeque<>();
- this.onLoan = Collections.synchronizedMap(new WeakHashMap<T, Integer>());
- }
-
- /**
- * Called whenever a new pool object is desired; subclasses must implement.
- *
- * @return object of the type desired by the subclass
- * @throws E Throwable's subclass
- */
- protected abstract T createObject() throws E;
-
-
- /**
- * Subclasses can subclass to return a more specific type.
- *
- * @return an object from the pool; will block until an object is available
- * @throws E
- */
- public T getObject() throws E {
- WeakReference<T> ref;
- T retValue = null;
- do {
- //remove any stale entries as well
- ref = available.poll();
- } while (ref != null && (retValue = ref.get()) == null);
-
- if (retValue == null) {
- //empty pool; create & add new one
- retValue = createObject();
- }
- onLoan.put(retValue, MARKER_VALUE);
- return retValue;
- }
-
-
- /**
- * Adds the given object to the pool, provided that the object
- * was created by this pool.
- *
- * @param obj the object to return to the pool
- * @return whether the object was successfully added as available
- */
- public boolean repool(T obj) {
- if (obj != null && onLoan.containsKey(obj)) {
- //synchronize to protect against a caller returning the same object again...
- synchronized (obj) {
- //...and check to see that it was removed
- if (onLoan.remove(obj) != null) {
- return available.offer(new WeakReference<T>(obj));
- }
- }
- }
- return false;
- }
-}
diff --git a/src/main/java/org/apache/xml/security/utils/XMLUtils.java b/src/main/java/org/apache/xml/security/utils/XMLUtils.java
index b70aab3..92a1e81 100644
--- a/src/main/java/org/apache/xml/security/utils/XMLUtils.java
+++ b/src/main/java/org/apache/xml/security/utils/XMLUtils.java
@@ -26,12 +26,16 @@
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.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -59,12 +63,9 @@
AccessController.doPrivileged(
(PrivilegedAction<Boolean>) () -> Boolean.getBoolean("org.apache.xml.security.ignoreLineBreaks"));
- @SuppressWarnings("unchecked")
- private static final WeakObjectPool<DocumentBuilder, ParserConfigurationException> pools[] = new WeakObjectPool[2];
- static {
- pools[0] = new DocumentBuilderPool(false);
- pools[1] = new DocumentBuilderPool(true);
- }
+ private static int parserPoolSize =
+ AccessController.doPrivileged(
+ (PrivilegedAction<Integer>) () -> Integer.getInteger("org.apache.xml.security.parser.pool-size", 20));
private static volatile String dsPrefix = "ds";
private static volatile String ds11Prefix = "dsig11";
@@ -74,6 +75,11 @@
private static final org.slf4j.Logger LOG =
org.slf4j.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
@@ -1004,7 +1010,7 @@
}
public static Document newDocument() throws ParserConfigurationException {
- DocumentBuilder documentBuilder = createDocumentBuilder(true);
+ DocumentBuilder documentBuilder = getDocumentBuilder(true);
Document doc = documentBuilder.newDocument();
repoolDocumentBuilder(documentBuilder, true);
return doc;
@@ -1015,7 +1021,7 @@
}
public static Document read(InputStream inputStream, boolean disAllowDocTypeDeclarations) throws ParserConfigurationException, SAXException, IOException {
- DocumentBuilder documentBuilder = createDocumentBuilder(disAllowDocTypeDeclarations);
+ DocumentBuilder documentBuilder = getDocumentBuilder(disAllowDocTypeDeclarations);
Document doc = documentBuilder.parse(inputStream);
repoolDocumentBuilder(documentBuilder, disAllowDocTypeDeclarations);
return doc;
@@ -1023,7 +1029,7 @@
public static Document read(String uri, boolean disAllowDocTypeDeclarations)
throws ParserConfigurationException, SAXException, IOException {
- DocumentBuilder documentBuilder = createDocumentBuilder(disAllowDocTypeDeclarations);
+ DocumentBuilder documentBuilder = getDocumentBuilder(disAllowDocTypeDeclarations);
Document doc = documentBuilder.parse(uri);
repoolDocumentBuilder(documentBuilder, disAllowDocTypeDeclarations);
return doc;
@@ -1035,26 +1041,12 @@
public static Document read(InputSource inputSource, boolean disAllowDocTypeDeclarations)
throws ParserConfigurationException, SAXException, IOException {
- DocumentBuilder documentBuilder = createDocumentBuilder(disAllowDocTypeDeclarations);
+ DocumentBuilder documentBuilder = getDocumentBuilder(disAllowDocTypeDeclarations);
Document doc = documentBuilder.parse(inputSource);
repoolDocumentBuilder(documentBuilder, disAllowDocTypeDeclarations);
return doc;
}
- private static DocumentBuilder createDocumentBuilder(
- boolean disAllowDocTypeDeclarations
- ) throws ParserConfigurationException {
- int idx = getPoolsIndex(disAllowDocTypeDeclarations);
- return pools[idx].getObject();
- }
-
-
- private static boolean repoolDocumentBuilder(DocumentBuilder db, boolean disAllowDocTypeDeclarations) {
- db.reset();
- int idx = getPoolsIndex(disAllowDocTypeDeclarations);
- return pools[idx].repool(db);
- }
-
/**
* Returns a byte-array representation of a <code>{@link BigInteger}</code>.
* No sign-bit is output.
@@ -1100,34 +1092,75 @@
return resizedBytes;
}
- private static final class DocumentBuilderPool
- extends WeakObjectPool<DocumentBuilder, ParserConfigurationException> {
-
- private final boolean disAllowDocTypeDeclarations;
-
- public DocumentBuilderPool(boolean disAllowDocTypeDeclarations) {
- this.disAllowDocTypeDeclarations = disAllowDocTypeDeclarations;
+ private static DocumentBuilder getDocumentBuilder(boolean disAllowDocTypeDeclarations) throws ParserConfigurationException {
+ ClassLoader loader = getContextClassLoader();
+ if (loader == null) {
+ loader = getClassLoader(XMLUtils.class);
+ }
+ if (loader == null) {
+ return createDocumentBuilder(disAllowDocTypeDeclarations);
}
- @Override
- protected DocumentBuilder createObject() throws ParserConfigurationException {
- DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
- dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
- if (disAllowDocTypeDeclarations) {
- dfactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ 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);
+ }
+
+ DocumentBuilder db = queue.poll();
+ if (db == null) {
+ db = createDocumentBuilder(disAllowDocTypeDeclarations);
+ }
+ return db;
+ }
+
+ private static DocumentBuilder createDocumentBuilder(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, boolean disAllowDocTypeDeclarations) {
+ ClassLoader loader = getContextClassLoader();
+ if (loader == null) {
+ loader = getClassLoader(XMLUtils.class);
+ }
+ if (loader != null) {
+ Queue<DocumentBuilder> queue =
+ disAllowDocTypeDeclarations ? DOCUMENT_BUILDERS_DISALLOW_DOCTYPE.get(loader) : DOCUMENT_BUILDERS.get(loader);
+ if (queue != null) {
+ db.reset();
+ queue.offer(db);
}
- dfactory.setNamespaceAware(true);
- return dfactory.newDocumentBuilder();
}
}
- /**
- * Maps the boolean configuration options for the factories to the array index for the WeakObjectPool
- * @param disAllowDocTypeDeclarations
- * @return the index to the {@link #pools}
- */
- private static int getPoolsIndex(boolean disAllowDocTypeDeclarations) {
- return disAllowDocTypeDeclarations ? 1 : 0;
+ 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();
}
}