From 859ece142d7941cb567885cd23d81432017683ba Mon Sep 17 00:00:00 2001 From: rfeng Date: Wed, 27 Jul 2011 23:20:34 +0000 Subject: Add a parser tool for DOM to avoid a JDK bug of deadlock (at com.sun.org.apache.xerces.internal.impl.dv.DTDDVFactory.getInstance) git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1151663 13f79535-47bb-0310-9956-ffa450edef68 --- .../tuscany/sca/common/xml/dom/DOMHelper.java | 89 +++++++++++++++++-- .../tuscany/sca/common/xml/dom/ParserPool.java | 99 ++++++++++++++++++++++ .../sca/common/xml/dom/DOMHelperTestCase.java | 27 +++++- 3 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/ParserPool.java (limited to 'sca-java-2.x') diff --git a/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/DOMHelper.java b/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/DOMHelper.java index 304275beb7..c5e7015364 100644 --- a/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/DOMHelper.java +++ b/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/DOMHelper.java @@ -40,6 +40,7 @@ import javax.xml.transform.stream.StreamResult; import org.apache.tuscany.sca.common.xml.dom.impl.SAX2DOMAdapter; import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.core.LifeCycleListener; import org.apache.tuscany.sca.core.UtilityExtensionPoint; import org.w3c.dom.Attr; import org.w3c.dom.Document; @@ -57,15 +58,19 @@ import org.xml.sax.ext.LexicalHandler; * @version $Rev$ $Date$ * @tuscany.spi.extension.asclient */ -public class DOMHelper { +public class DOMHelper implements LifeCycleListener { + protected static final int INITIAL_POOL_SIZE = 8; + protected static final int MAX_POOL_SIZE = 64; private DocumentBuilderFactory documentBuilderFactory; private TransformerFactory transformerFactory; + protected ParserPool builderPool; + protected ParserPool transformerPool; public static DOMHelper getInstance(ExtensionPointRegistry registry) { UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class); return utilities.getUtility(DOMHelper.class); } - + public DOMHelper(ExtensionPointRegistry registry) { FactoryExtensionPoint factories = registry.getExtensionPoint(FactoryExtensionPoint.class); documentBuilderFactory = factories.getFactory(DocumentBuilderFactory.class); @@ -84,11 +89,24 @@ public class DOMHelper { } public Document newDocument() { - return newDocumentBuilder().newDocument(); + DocumentBuilder builder = newDocumentBuilder(); + try { + return builder.newDocument(); + } finally { + returnDocumentBuilder(builder); + } } public DocumentBuilder newDocumentBuilder() { + return builderPool.borrowFromPool(); + } + + public void returnDocumentBuilder(DocumentBuilder builder) { + builderPool.returnToPool(builder); + } + + private DocumentBuilder createDocumentBuilder() { try { return documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { @@ -98,10 +116,14 @@ public class DOMHelper { public Document load(String xmlString) throws IOException, SAXException { DocumentBuilder builder = newDocumentBuilder(); - InputSource is = new InputSource(new StringReader(xmlString)); - return builder.parse(is); + try { + InputSource is = new InputSource(new StringReader(xmlString)); + return builder.parse(is); + } finally { + returnDocumentBuilder(builder); + } } - + public Document load(Source source) { Transformer transformer = newTransformer(); DOMResult result = new DOMResult(newDocument()); @@ -109,6 +131,8 @@ public class DOMHelper { transformer.transform(source, result); } catch (TransformerException e) { throw new IllegalArgumentException(e); + } finally { + transformerPool.returnToPool(transformer); } return (Document)result.getNode(); } @@ -128,11 +152,21 @@ public class DOMHelper { transformer.transform(new DOMSource(node), result); } catch (TransformerException e) { throw new IllegalArgumentException(e); + } finally { + returnTransformer(transformer); } return result.getWriter().toString(); } - private Transformer newTransformer() { + public Transformer newTransformer() { + return transformerPool.borrowFromPool(); + } + + public void returnTransformer(Transformer transformer) { + transformerPool.returnToPool(transformer); + } + + private Transformer createTransformer() { Transformer transformer = null; try { transformer = transformerFactory.newTransformer(); @@ -143,12 +177,14 @@ public class DOMHelper { } public void saveAsSAX(Node node, ContentHandler contentHandler) { - Transformer transformer = newTransformer(); + Transformer transformer = transformerPool.borrowFromPool(); SAXResult result = new SAXResult(contentHandler); try { transformer.transform(new DOMSource(node), result); } catch (TransformerException e) { throw new IllegalArgumentException(e); + } finally { + returnTransformer(transformer); } } @@ -209,7 +245,7 @@ public class DOMHelper { } return doc; } - + public static String getPrefix(Element element, String namespace) { if (element.isDefaultNamespace(namespace)) { return XMLConstants.DEFAULT_NS_PREFIX; @@ -228,4 +264,39 @@ public class DOMHelper { Node getNode(); } + @Override + public void start() { + builderPool = new ParserPool(MAX_POOL_SIZE, INITIAL_POOL_SIZE) { + + @Override + protected DocumentBuilder newInstance() { + return createDocumentBuilder(); + } + + @Override + protected void resetInstance(DocumentBuilder obj) { + obj.reset(); + } + }; + + transformerPool = new ParserPool(64, 8) { + + @Override + protected Transformer newInstance() { + return createTransformer(); + } + + @Override + protected void resetInstance(Transformer obj) { + obj.reset(); + } + }; + } + + @Override + public void stop() { + builderPool.clear(); + transformerPool.clear(); + } + } diff --git a/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/ParserPool.java b/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/ParserPool.java new file mode 100644 index 0000000000..6c987b39d4 --- /dev/null +++ b/sca-java-2.x/trunk/modules/common-xml/src/main/java/org/apache/tuscany/sca/common/xml/dom/ParserPool.java @@ -0,0 +1,99 @@ +/* + * 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.tuscany.sca.common.xml.dom; + +import java.util.IdentityHashMap; +import java.util.Map; + +public abstract class ParserPool { + private int maxSize; + private Map objects; + + public ParserPool() { + this(32, 0); + } + + public ParserPool(int maxSize, int initialSize) { + super(); + this.maxSize = maxSize; + this.objects = new IdentityHashMap(maxSize); + for (int i = 0; i < Math.min(initialSize, maxSize); i++) { + objects.put(newInstance(), Boolean.FALSE); + } + } + + public synchronized V borrowFromPool() { + while (true) { + for (Map.Entry e : objects.entrySet()) { + if (Boolean.FALSE.equals(e.getValue())) { + e.setValue(Boolean.TRUE); // in use + return e.getKey(); + } + } + if (objects.size() < maxSize) { + V obj = newInstance(); + objects.put(obj, Boolean.TRUE); + return obj; + } + try { + wait(); + } catch (InterruptedException e1) { + throw new IllegalStateException(e1); + } + } + } + + public synchronized void returnToPool(V obj) { + resetInstance(obj); + objects.put(obj, Boolean.FALSE); + notifyAll(); + } + + public synchronized void clear() { + objects.clear(); + } + + public synchronized int inUse() { + int size = 0; + for (Map.Entry e : objects.entrySet()) { + if (Boolean.TRUE.equals(e.getValue())) { + size++; + } + } + return size; + } + + /** + * Create a new instance + * @return + */ + protected abstract V newInstance(); + + /** + * Reset the instance before returning to the pool + * @param obj + */ + protected abstract void resetInstance(V obj); + + // Expose it for testing purpose + public Map getObjects() { + return objects; + } +} diff --git a/sca-java-2.x/trunk/modules/common-xml/src/test/java/org/apache/tuscany/sca/common/xml/dom/DOMHelperTestCase.java b/sca-java-2.x/trunk/modules/common-xml/src/test/java/org/apache/tuscany/sca/common/xml/dom/DOMHelperTestCase.java index 176ea35c1c..1be852ba05 100644 --- a/sca-java-2.x/trunk/modules/common-xml/src/test/java/org/apache/tuscany/sca/common/xml/dom/DOMHelperTestCase.java +++ b/sca-java-2.x/trunk/modules/common-xml/src/test/java/org/apache/tuscany/sca/common/xml/dom/DOMHelperTestCase.java @@ -20,13 +20,15 @@ package org.apache.tuscany.sca.common.xml.dom; import static org.junit.Assert.assertNotNull; -import junit.framework.Assert; + +import javax.xml.parsers.DocumentBuilder; import org.apache.tuscany.sca.common.xml.sax.SAXHelper; import org.apache.tuscany.sca.core.DefaultExtensionPointRegistry; import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.custommonkey.xmlunit.XMLAssert; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.w3c.dom.Document; @@ -78,4 +80,27 @@ public class DOMHelperTestCase { XMLAssert.assertXMLEqual(XML, xml); } + @Test + public void testPool() { + DOMHelper helper = DOMHelper.getInstance(registry); + + DocumentBuilder buidler1 = helper.newDocumentBuilder(); + Assert.assertTrue(helper.builderPool.getObjects().get(buidler1)); + + Assert.assertEquals(1, helper.builderPool.inUse()); + + DocumentBuilder buidler2 = helper.newDocumentBuilder(); + Assert.assertTrue(helper.builderPool.getObjects().get(buidler2)); + Assert.assertEquals(2, helper.builderPool.inUse()); + + helper.returnDocumentBuilder(buidler2); + Assert.assertFalse(helper.builderPool.getObjects().get(buidler2)); + Assert.assertEquals(1, helper.builderPool.inUse()); + + helper.returnDocumentBuilder(buidler1); + Assert.assertFalse(helper.builderPool.getObjects().get(buidler1)); + Assert.assertEquals(0, helper.builderPool.inUse()); + + } + } -- cgit v1.2.3