[maven-release-plugin] copy for tag xmlsec-2.1.2
git-svn-id: https://svn.apache.org/repos/asf/santuario/xml-security-java/tags/xmlsec-2.1.2@1833182 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/jcp/xml/dsig/internal/dom/DOMRetrievalMethod.java b/src/main/java/org/apache/jcp/xml/dsig/internal/dom/DOMRetrievalMethod.java
index 4aeeb7a..a585596 100644
--- a/src/main/java/org/apache/jcp/xml/dsig/internal/dom/DOMRetrievalMethod.java
+++ b/src/main/java/org/apache/jcp/xml/dsig/internal/dom/DOMRetrievalMethod.java
@@ -36,13 +36,23 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.security.Provider;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
-import javax.xml.crypto.*;
-import javax.xml.crypto.dsig.*;
+import javax.xml.crypto.Data;
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.NodeSetData;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMURIReference;
+import javax.xml.crypto.dsig.Transform;
+import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.keyinfo.RetrievalMethod;
-import javax.xml.parsers.*;
+import javax.xml.parsers.DocumentBuilder;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Attr;
@@ -255,10 +265,11 @@
public XMLStructure dereferenceAsXMLStructure(XMLCryptoContext context)
throws URIReferenceException
{
+ DocumentBuilder db = null;
boolean secVal = Utils.secureValidation(context);
ApacheData data = (ApacheData)dereference(context);
try (InputStream is = new ByteArrayInputStream(data.getXMLSignatureInput().getBytes())) {
- DocumentBuilder db = XMLUtils.createDocumentBuilder(false, secVal);
+ db = XMLUtils.createDocumentBuilder(false, secVal);
Document doc = db.parse(is);
Element kiElem = doc.getDocumentElement();
if (kiElem.getLocalName().equals("X509Data")
@@ -269,6 +280,10 @@
}
} catch (Exception e) {
throw new URIReferenceException(e);
+ } finally {
+ if (db != null) {
+ XMLUtils.repoolDocumentBuilder(db);
+ }
}
}
diff --git a/src/main/java/org/apache/xml/security/Init.java b/src/main/java/org/apache/xml/security/Init.java
index f48a4d7..5e65229 100644
--- a/src/main/java/org/apache/xml/security/Init.java
+++ b/src/main/java/org/apache/xml/security/Init.java
@@ -156,7 +156,13 @@
try {
/* read library configuration file */
DocumentBuilder db = XMLUtils.createDocumentBuilder(false);
- Document doc = db.parse(is);
+ Document doc;
+ try {
+ doc = db.parse(is);
+ } finally {
+ XMLUtils.repoolDocumentBuilder(db);
+ db = null;
+ }
Node config = doc.getFirstChild();
for (; config != null; config = config.getNextSibling()) {
if ("Configuration".equals(config.getLocalName())) {
diff --git a/src/main/java/org/apache/xml/security/c14n/Canonicalizer.java b/src/main/java/org/apache/xml/security/c14n/Canonicalizer.java
index ce3aa86..80d29b6 100644
--- a/src/main/java/org/apache/xml/security/c14n/Canonicalizer.java
+++ b/src/main/java/org/apache/xml/security/c14n/Canonicalizer.java
@@ -282,7 +282,11 @@
*/
db.setErrorHandler(new org.apache.xml.security.utils.IgnoreAllErrorHandler());
- document = db.parse(in);
+ try {
+ document = db.parse(in);
+ } finally {
+ XMLUtils.repoolDocumentBuilder(db);
+ }
}
return this.canonicalizeSubtree(document);
}
diff --git a/src/main/java/org/apache/xml/security/c14n/CanonicalizerSpi.java b/src/main/java/org/apache/xml/security/c14n/CanonicalizerSpi.java
index c77e0aa..9f7c218 100644
--- a/src/main/java/org/apache/xml/security/c14n/CanonicalizerSpi.java
+++ b/src/main/java/org/apache/xml/security/c14n/CanonicalizerSpi.java
@@ -61,7 +61,11 @@
DocumentBuilder db = XMLUtils.createDocumentBuilder(false, secureValidation);
- document = db.parse(in);
+ try {
+ document = db.parse(in);
+ } finally {
+ XMLUtils.repoolDocumentBuilder(db);
+ }
}
return this.engineCanonicalizeSubTree(document);
}
diff --git a/src/main/java/org/apache/xml/security/encryption/DocumentSerializer.java b/src/main/java/org/apache/xml/security/encryption/DocumentSerializer.java
index 7cb9d2a..3c7ce3c 100644
--- a/src/main/java/org/apache/xml/security/encryption/DocumentSerializer.java
+++ b/src/main/java/org/apache/xml/security/encryption/DocumentSerializer.java
@@ -70,8 +70,9 @@
* @throws XMLEncryptionException
*/
private Node deserialize(Node ctx, InputSource inputSource) throws XMLEncryptionException {
+ DocumentBuilder db = null;
try {
- DocumentBuilder db = XMLUtils.createDocumentBuilder(false, secureValidation);
+ db = XMLUtils.createDocumentBuilder(false, secureValidation);
Document d = db.parse(inputSource);
Document contextDocument = null;
@@ -97,6 +98,10 @@
throw new XMLEncryptionException(pce);
} catch (IOException ioe) {
throw new XMLEncryptionException(ioe);
+ } finally {
+ if (db != null) {
+ XMLUtils.repoolDocumentBuilder(db);
+ }
}
}
diff --git a/src/main/java/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java b/src/main/java/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java
index 8e3ddc1..7f96c65 100644
--- a/src/main/java/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java
+++ b/src/main/java/org/apache/xml/security/keys/keyresolver/KeyResolverSpi.java
@@ -262,6 +262,7 @@
this.globalResolver = globalResolver;
}
+
/**
* Parses a byte array and returns the parsed Element.
*
@@ -270,8 +271,9 @@
* @throws KeyResolverException if something goes wrong
*/
protected static Element getDocFromBytes(byte[] bytes, boolean secureValidation) throws KeyResolverException {
+ DocumentBuilder db = null;
try (InputStream is = new ByteArrayInputStream(bytes)) {
- DocumentBuilder db = XMLUtils.createDocumentBuilder(false, secureValidation);
+ db = XMLUtils.createDocumentBuilder(false, secureValidation);
Document doc = db.parse(is);
return doc.getDocumentElement();
} catch (SAXException ex) {
@@ -280,6 +282,10 @@
throw new KeyResolverException(ex);
} catch (ParserConfigurationException ex) {
throw new KeyResolverException(ex);
+ } finally {
+ if (db != null) {
+ XMLUtils.repoolDocumentBuilder(db);
+ }
}
}
diff --git a/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java b/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java
index 1a9c2f8..4571cc3 100644
--- a/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java
+++ b/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java
@@ -250,5 +250,4 @@
}
return e;
}
-
}
diff --git a/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java b/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java
index 5fe8515..126cd09 100644
--- a/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java
+++ b/src/main/java/org/apache/xml/security/keys/keyresolver/implementations/RetrievalMethodResolver.java
@@ -281,7 +281,7 @@
}
return resource;
}
-
+
/**
* Method engineResolveSecretKey
* {@inheritDoc}
diff --git a/src/main/java/org/apache/xml/security/signature/SignedInfo.java b/src/main/java/org/apache/xml/security/signature/SignedInfo.java
index 6b03902..5105824 100644
--- a/src/main/java/org/apache/xml/security/signature/SignedInfo.java
+++ b/src/main/java/org/apache/xml/security/signature/SignedInfo.java
@@ -22,6 +22,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.ParserConfigurationException;
@@ -31,9 +32,9 @@
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.transforms.params.InclusiveNamespaces;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
-import org.apache.xml.security.transforms.params.InclusiveNamespaces;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -218,6 +219,8 @@
newdoc.getDocumentElement(), true);
element.getParentNode().replaceChild(imported, element);
return (Element) imported;
+ } finally {
+ XMLUtils.repoolDocumentBuilder(db);
}
} catch (ParserConfigurationException ex) {
throw new XMLSecurityException(ex);
diff --git a/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java b/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java
index a883e8b..c6f56bf 100644
--- a/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java
+++ b/src/main/java/org/apache/xml/security/signature/XMLSignatureInput.java
@@ -33,9 +33,9 @@
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xml.security.c14n.CanonicalizationException;
-import org.apache.xml.security.c14n.implementations.CanonicalizerBase;
-import org.apache.xml.security.c14n.implementations.Canonicalizer20010315OmitComments;
import org.apache.xml.security.c14n.implementations.Canonicalizer11_OmitComments;
+import org.apache.xml.security.c14n.implementations.Canonicalizer20010315OmitComments;
+import org.apache.xml.security.c14n.implementations.CanonicalizerBase;
import org.apache.xml.security.exceptions.XMLSecurityRuntimeException;
import org.apache.xml.security.utils.JavaUtils;
import org.apache.xml.security.utils.XMLUtils;
@@ -593,6 +593,7 @@
this.subNode = document.getDocumentElement().getFirstChild().getFirstChild();
}
} finally {
+ XMLUtils.repoolDocumentBuilder(db);
if (this.inputOctetStreamProxy != null) {
this.inputOctetStreamProxy.close();
}
diff --git a/src/main/java/org/apache/xml/security/utils/WeakObjectPool.java b/src/main/java/org/apache/xml/security/utils/WeakObjectPool.java
new file mode 100644
index 0000000..967da95
--- /dev/null
+++ b/src/main/java/org/apache/xml/security/utils/WeakObjectPool.java
@@ -0,0 +1,114 @@
+/**
+ * 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<WeakReference<T>>();
+ 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 c4ac35f..ae4fd69 100644
--- a/src/main/java/org/apache/xml/security/utils/XMLUtils.java
+++ b/src/main/java/org/apache/xml/security/utils/XMLUtils.java
@@ -18,7 +18,9 @@
*/
package org.apache.xml.security.utils;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.AccessController;
@@ -34,17 +36,23 @@
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.w3c.dom.Attr;
+import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
/**
* DOM and XML accessibility and comfort functions.
@@ -55,14 +63,14 @@
private static boolean ignoreLineBreaks =
AccessController.doPrivileged(
(PrivilegedAction<Boolean>) () -> Boolean.getBoolean("org.apache.xml.security.ignoreLineBreaks"));
-
+
@SuppressWarnings("unchecked")
- private static final ThreadLocal<DocumentBuilder> tl[][] = new ThreadLocal[2][2];
+ private static final WeakObjectPool<DocumentBuilder, ParserConfigurationException> pools[] = new WeakObjectPool[4];
static {
- tl[0][0] = new MyThreadLocal(false, false);
- tl[0][1] = new MyThreadLocal(false, true);
- tl[1][0] = new MyThreadLocal(true, false);
- tl[1][1] = new MyThreadLocal(true, true);
+ pools[0] = new DocumentBuilderPool(false, false);
+ pools[1] = new DocumentBuilderPool(false, true);
+ pools[2] = new DocumentBuilderPool(true, false);
+ pools[3] = new DocumentBuilderPool(true, true);
}
private static volatile String dsPrefix = "ds";
@@ -1061,19 +1069,25 @@
public static DocumentBuilder createDocumentBuilder(
boolean validating, boolean disAllowDocTypeDeclarations
) throws ParserConfigurationException {
- DocumentBuilder documentBuilder = tl[validating ? 1 : 0][disAllowDocTypeDeclarations ? 1 : 0].get();
- documentBuilder.reset();
- return documentBuilder;
+ int idx = getPoolsIndex(validating, disAllowDocTypeDeclarations);
+ return pools[idx].getObject();
}
+
/**
* Return this document builder to be reused
* @param db DocumentBuilder returned from any of {@link #createDocumentBuilder} methods.
* @return whether it was successfully returned to the pool
*/
- @Deprecated
public static boolean repoolDocumentBuilder(DocumentBuilder db) {
- return true;
+ if (!(db instanceof DocumentBuilderProxy)) {
+ return false;
+ }
+ db.reset();
+ boolean disAllowDocTypeDeclarations =
+ ((DocumentBuilderProxy)db).disAllowDocTypeDeclarations();
+ int idx = getPoolsIndex(db.isValidating(), disAllowDocTypeDeclarations);
+ return pools[idx].repool(db);
}
/**
@@ -1120,32 +1134,122 @@
return resizedBytes;
}
-
- private static final class MyThreadLocal extends ThreadLocal<DocumentBuilder> {
- private final boolean validating;
+
+ /**
+ * We need this proxy wrapping DocumentBuilder to record the value
+ * passed to disAllowDoctypeDeclarations. It's needed to figure out
+ * on which pool to return.
+ */
+ private static class DocumentBuilderProxy extends DocumentBuilder {
+ private final DocumentBuilder delegate;
private final boolean disAllowDocTypeDeclarations;
- public MyThreadLocal(boolean validating, boolean disAllowDocTypeDeclarations) {
+ private DocumentBuilderProxy(DocumentBuilder actual, boolean disAllowDocTypeDeclarations) {
+ delegate = actual;
+ this.disAllowDocTypeDeclarations = disAllowDocTypeDeclarations;
+ }
+
+ boolean disAllowDocTypeDeclarations() {
+ return disAllowDocTypeDeclarations;
+ }
+
+ public void reset() {
+ delegate.reset();
+ }
+
+ public Document parse(InputStream is) throws SAXException, IOException {
+ return delegate.parse(is);
+ }
+
+ public Document parse(InputStream is, String systemId)
+ throws SAXException, IOException {
+ return delegate.parse(is, systemId);
+ }
+
+ public Document parse(String uri) throws SAXException, IOException {
+ return delegate.parse(uri);
+ }
+
+ public Document parse(File f) throws SAXException, IOException {
+ return delegate.parse(f);
+ }
+
+ public Schema getSchema() {
+ return delegate.getSchema();
+ }
+
+ public boolean isXIncludeAware() {
+ return delegate.isXIncludeAware();
+ }
+
+ @Override
+ public Document parse(InputSource is) throws SAXException, IOException {
+ return delegate.parse(is);
+ }
+
+ @Override
+ public boolean isNamespaceAware() {
+ return delegate.isNamespaceAware();
+ }
+
+ @Override
+ public boolean isValidating() {
+ return delegate.isValidating();
+ }
+
+ @Override
+ public void setEntityResolver(EntityResolver er) {
+ delegate.setEntityResolver(er);
+ }
+
+ @Override
+ public void setErrorHandler(ErrorHandler eh) {
+ delegate.setErrorHandler(eh);
+ }
+
+ @Override
+ public Document newDocument() {
+ return delegate.newDocument();
+ }
+
+ @Override
+ public DOMImplementation getDOMImplementation() {
+ return delegate.getDOMImplementation();
+ }
+
+ }
+
+ private static final class DocumentBuilderPool
+ extends WeakObjectPool<DocumentBuilder, ParserConfigurationException> {
+
+ private final boolean validating, disAllowDocTypeDeclarations;
+
+ public DocumentBuilderPool(boolean validating, boolean disAllowDocTypeDeclarations) {
this.validating = validating;
this.disAllowDocTypeDeclarations = disAllowDocTypeDeclarations;
}
@Override
- protected DocumentBuilder initialValue() {
- try {
- 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);
- }
- dfactory.setValidating(validating);
- dfactory.setNamespaceAware(true);
-
- return dfactory.newDocumentBuilder();
- } catch (ParserConfigurationException e) {
- throw new RuntimeException(e);
+ 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);
}
+ dfactory.setValidating(validating);
+ dfactory.setNamespaceAware(true);
+ return new DocumentBuilderProxy(dfactory.newDocumentBuilder(), disAllowDocTypeDeclarations);
}
}
+ /**
+ * Maps the two boolean configuration options for the factories to the array index for the WeakObjectPool
+ * @param validating
+ * @param disAllowDocTypeDeclarations
+ * @return the index to the {@link #pools}
+ */
+ private static int getPoolsIndex(boolean validating, boolean disAllowDocTypeDeclarations) {
+ return (validating ? 2 : 0) + (disAllowDocTypeDeclarations ? 1 : 0);
+ }
+
}
diff --git a/src/test/java/org/apache/xml/security/test/dom/secure_val/ForbiddenReferenceTest.java b/src/test/java/org/apache/xml/security/test/dom/secure_val/ForbiddenReferenceTest.java
index 8d02bb8..013a550 100644
--- a/src/test/java/org/apache/xml/security/test/dom/secure_val/ForbiddenReferenceTest.java
+++ b/src/test/java/org/apache/xml/security/test/dom/secure_val/ForbiddenReferenceTest.java
@@ -73,6 +73,7 @@
javax.xml.parsers.DocumentBuilder db = XMLUtils.createDocumentBuilder(false);
org.w3c.dom.Document doc = db.parse(f);
+ XMLUtils.repoolDocumentBuilder(db);
Element manifestElement =
(Element) doc.getElementsByTagNameNS(Constants.SignatureSpecNS,
diff --git a/src/test/java/org/apache/xml/security/test/dom/signature/SignatureReferenceTest.java b/src/test/java/org/apache/xml/security/test/dom/signature/SignatureReferenceTest.java
index a9a4ebe..f4dc2c4 100644
--- a/src/test/java/org/apache/xml/security/test/dom/signature/SignatureReferenceTest.java
+++ b/src/test/java/org/apache/xml/security/test/dom/signature/SignatureReferenceTest.java
@@ -127,6 +127,8 @@
referenceElement.appendChild(digestValue);
new WrappedReference(referenceElement, "_54321", null);
+
+ XMLUtils.repoolDocumentBuilder(db);
}
/**
diff --git a/src/test/java/org/apache/xml/security/test/dom/utils/DocumentBuilderPoolingTest.java b/src/test/java/org/apache/xml/security/test/dom/utils/DocumentBuilderPoolingTest.java
new file mode 100644
index 0000000..05add92
--- /dev/null
+++ b/src/test/java/org/apache/xml/security/test/dom/utils/DocumentBuilderPoolingTest.java
@@ -0,0 +1,175 @@
+/**
+ * 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.test.dom.utils;
+
+import org.apache.xml.security.utils.WeakObjectPool;
+import org.apache.xml.security.utils.XMLUtils;
+import org.junit.Test;
+
+import javax.xml.parsers.DocumentBuilder;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.Iterator;
+import java.util.concurrent.*;
+
+import static org.junit.Assert.*;
+
+public class DocumentBuilderPoolingTest {
+
+ private static final String DOCUMENTBUILDERPROXY_CLASSNAME =
+ "org.apache.xml.security.utils.XMLUtils$DocumentBuilderProxy";
+
+ @Test
+ public void testEquals() throws Exception {
+ DocumentBuilder documentBuilder = XMLUtils.createDocumentBuilder(true);
+ assertEquals(documentBuilder, documentBuilder);
+ }
+
+ @Test
+ public void testGetValidatingDocumentBuilder() throws Exception {
+ DocumentBuilder documentBuilder = XMLUtils.createDocumentBuilder(true);
+ assertTrue(documentBuilder.isValidating());
+ }
+
+ @Test
+ public void testGetNonValidatingDocumentBuilder() throws Exception {
+ DocumentBuilder documentBuilder = XMLUtils.createDocumentBuilder(false);
+ assertFalse(documentBuilder.isValidating());
+ }
+
+ @Test
+ public void testGetValidatingAndAllowDocTypeDeclarationsDocumentBuilder() throws Exception {
+ DocumentBuilder documentBuilder = XMLUtils.createDocumentBuilder(true, false);
+ assertTrue(documentBuilder.isValidating());
+ assertEquals(documentBuilder.getClass().getName(), DOCUMENTBUILDERPROXY_CLASSNAME);
+ assertAllowDocTypeDeclarations(documentBuilder, false);
+ }
+
+ @Test
+ public void testGetValidatingAndDisAllowDocTypeDeclarationsDocumentBuilder() throws Exception {
+ DocumentBuilder documentBuilder = XMLUtils.createDocumentBuilder(true, true);
+ assertTrue(documentBuilder.isValidating());
+ assertEquals(documentBuilder.getClass().getName(), DOCUMENTBUILDERPROXY_CLASSNAME);
+ assertAllowDocTypeDeclarations(documentBuilder, true);
+ }
+
+ private void assertAllowDocTypeDeclarations(DocumentBuilder documentBuilder, boolean allow) throws Exception {
+ Field field = documentBuilder.getClass().getDeclaredField("disAllowDocTypeDeclarations");
+ field.setAccessible(true);
+ assertEquals(allow, field.get(documentBuilder));
+ }
+
+ @Test
+ public void testNewDocumentBuilderInstances() throws Exception {
+ int count = 4;
+
+ // get all possible combinations of DocumentBuilders:
+ DocumentBuilder[] documentBuilders = new DocumentBuilder[count];
+ for (int i = 0; i < count; i++) {
+ documentBuilders[i] = XMLUtils.createDocumentBuilder(i / 2 > 0, i % 2 == 1);
+ }
+
+ //test that we got always a new instance:
+ for (int i = 0; i < count; i++) {
+ for (int j = i + 1; j < count; j++) {
+ assertNotEquals(documentBuilders[i], documentBuilders[j]);
+ assertNotSame(documentBuilders[i], documentBuilders[j]);
+ }
+ }
+ }
+
+ @Test
+ public void testRepoolingTwice() throws Exception {
+ DocumentBuilder documentBuilder = XMLUtils.createDocumentBuilder(true);
+ assertTrue(XMLUtils.repoolDocumentBuilder(documentBuilder));
+ assertFalse("can't repool the same object twice!", XMLUtils.repoolDocumentBuilder(documentBuilder));
+ }
+
+ @Test(timeout = 30000)
+ public void testPooling() throws Exception {
+ int nThreads = 8;
+ ExecutorService exec = Executors.newFixedThreadPool(nThreads);
+ Future<?>[] results = new Future[nThreads];
+ for (int i = 0; i < nThreads - 1; i++) {
+ results[i] = exec.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ // retrieve some DocumentBuilders...
+ DocumentBuilder documentBuilders[] = new DocumentBuilder[10];
+ for (int j = 0; j < documentBuilders.length; j++) {
+ documentBuilders[j] = XMLUtils.createDocumentBuilder(false);
+ assertNotNull(documentBuilders[j]);
+ }
+ // ...then repool them so that another thread may pickup them again
+ for (int j = 0; j < documentBuilders.length; j++) {
+ assertTrue(XMLUtils.repoolDocumentBuilder(documentBuilders[j]));
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+ // more or less mimic gc
+ results[nThreads - 1] = exec.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final Field poolField = XMLUtils.class.getDeclaredField("pools");
+ poolField.setAccessible(true);
+ final WeakObjectPool[] weakObjectPools = (WeakObjectPool[]) poolField.get(null);
+
+ final Field availableField = WeakObjectPool.class.getDeclaredField("available");
+ availableField.setAccessible(true);
+
+ while (true) {
+ final BlockingDeque blockingDeque = (BlockingDeque) availableField.get(weakObjectPools[1]);
+ Iterator iterator = blockingDeque.iterator();
+ while (iterator.hasNext()) {
+ ((WeakReference) iterator.next()).clear();
+ }
+ Thread.sleep(200);
+ }
+ } catch (InterruptedException e) {
+ return;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ exec.shutdown();
+ exec.awaitTermination(5, TimeUnit.SECONDS);
+ for (Future<?> f : results) {
+ if (!f.isDone()) {
+ f.cancel(false);
+ }
+ try {
+ assertNull(f.get(1000, TimeUnit.MILLISECONDS));
+ } catch (CancellationException ce) {
+ //expected since we did cancel it
+ } catch (TimeoutException e) {
+ fail(f + "didn't cancel after timeout?");
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/apache/xml/security/test/dom/utils/XMLUtilsPerformanceTest.java b/src/test/java/org/apache/xml/security/test/dom/utils/XMLUtilsPerformanceTest.java
index 3d896bb..62ccdcc 100644
--- a/src/test/java/org/apache/xml/security/test/dom/utils/XMLUtilsPerformanceTest.java
+++ b/src/test/java/org/apache/xml/security/test/dom/utils/XMLUtilsPerformanceTest.java
@@ -20,6 +20,8 @@
import java.io.StringReader;
+import javax.xml.parsers.DocumentBuilder;
+
import org.apache.xml.security.utils.XMLUtils;
import org.junit.Test;
import org.xml.sax.InputSource;
@@ -36,7 +38,9 @@
@Test
public void testCreateDocumentBuilder() throws Exception {
InputSource inputSource = new InputSource(new StringReader("<xml>123</xml>"));
- XMLUtils.createDocumentBuilder(false).parse(inputSource);
+ DocumentBuilder db = XMLUtils.createDocumentBuilder(false);
+ db.parse(inputSource);
+ XMLUtils.repoolDocumentBuilder(db);
}
}