Repository: camel
Updated Branches:
  refs/heads/master 7d781cce7 -> 070ed43e1


CAMEL-9534: XmlConverter: use a pool of SAXParser / XMLReader instances

Creating and configuring a new SAXParserFactory and SAXParser / XMLReader
for every SAXSource is very expensive.

A SAXParser / XMLReader instance can be used to parse many XML documents.

Add an XMLReaderPool helper to manage existing XMLReader instances (i.e.
hand out existing instances from the pool, reset properties / features and
return the instance to the pool after use).

Use pooled XMLReaders in XmlConverter.toSAXSource* (except if a specific
SAXParserFactory is configured on the Exchange).

Signed-off-by: Karsten Blees <karsten.bl...@dcon.de>


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/3c8560f0
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/3c8560f0
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/3c8560f0

Branch: refs/heads/master
Commit: 3c8560f003e0a4534cef428664fd4811621aa03c
Parents: 7d90d5d
Author: Karsten Blees <bl...@dcon.de>
Authored: Wed Jan 27 19:10:22 2016 +0100
Committer: Daniel Kulp <dk...@apache.org>
Committed: Mon Feb 1 16:06:24 2016 -0500

----------------------------------------------------------------------
 .../camel/converter/jaxp/XMLReaderPool.java     | 234 +++++++++++++++++++
 .../camel/converter/jaxp/XmlConverter.java      |  50 ++--
 2 files changed, 265 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/3c8560f0/camel-core/src/main/java/org/apache/camel/converter/jaxp/XMLReaderPool.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/converter/jaxp/XMLReaderPool.java 
b/camel-core/src/main/java/org/apache/camel/converter/jaxp/XMLReaderPool.java
new file mode 100644
index 0000000..ec50920
--- /dev/null
+++ 
b/camel-core/src/main/java/org/apache/camel/converter/jaxp/XMLReaderPool.java
@@ -0,0 +1,234 @@
+/**
+ * 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.camel.converter.jaxp;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Manages a pool of XMLReader (and associated SAXParser) instances for reuse.
+ */
+public class XMLReaderPool {
+    private final Queue<WeakReference<XMLReader>> pool = new 
ConcurrentLinkedQueue<WeakReference<XMLReader>>();
+    private final SAXParserFactory saxParserFactory;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param saxParserFactory
+     *            the SAXParserFactory used to create new SAXParser instances
+     */
+    public XMLReaderPool(SAXParserFactory saxParserFactory) {
+        this.saxParserFactory = saxParserFactory;
+    }
+
+    /**
+     * Returns an XMLReader that can be used exactly once. Calling one of the
+     * {@code parse} methods returns the reader to the pool. This is useful
+     * for e.g. SAXSource which bundles an XMLReader with an InputSource that
+     * can also be consumed just once.
+     *
+     * @return the XMLReader
+     * @throws SAXException
+     *             see {@link SAXParserFactory#newSAXParser()}
+     * @throws ParserConfigurationException
+     *             see {@link SAXParserFactory#newSAXParser()}
+     */
+    public XMLReader createXMLReader() throws SAXException, 
ParserConfigurationException {
+        XMLReader xmlReader = null;
+        WeakReference<XMLReader> ref;
+        while ((ref = pool.poll()) != null) {
+            if ((xmlReader = ref.get()) != null)
+                break;
+        }
+
+        if (xmlReader == null) {
+            xmlReader = saxParserFactory.newSAXParser().getXMLReader();
+        }
+
+        return new OneTimeXMLReader(xmlReader);
+    }
+
+    /**
+     * Wraps another XMLReader for single use only.
+     */
+    private class OneTimeXMLReader implements XMLReader {
+        private XMLReader xmlReader;
+        private final Map<String, Boolean> initFeatures = new HashMap<String, 
Boolean>();
+        private final Map<String, Object> initProperties = new HashMap<String, 
Object>();
+        private final ContentHandler initContentHandler;
+        private final DTDHandler initDtdHandler;
+        private final EntityResolver initEntityResolver;
+        private final ErrorHandler initErrorHandler;
+
+        private OneTimeXMLReader(XMLReader xmlReader) {
+            this.xmlReader = xmlReader;
+            this.initContentHandler = xmlReader.getContentHandler();
+            this.initDtdHandler = xmlReader.getDTDHandler();
+            this.initEntityResolver = xmlReader.getEntityResolver();
+            this.initErrorHandler = xmlReader.getErrorHandler();
+        }
+
+        private void release() {
+            // reset XMLReader to its initial state
+            for (Map.Entry<String, Boolean> feature : initFeatures.entrySet()) 
{
+                try {
+                    xmlReader.setFeature(feature.getKey(), 
feature.getValue().booleanValue());
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+            for (Map.Entry<String, Object> property : 
initProperties.entrySet()) {
+                try {
+                    xmlReader.setProperty(property.getKey(), 
property.getValue());
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+            xmlReader.setContentHandler(initContentHandler);
+            xmlReader.setDTDHandler(initDtdHandler);
+            xmlReader.setEntityResolver(initEntityResolver);
+            xmlReader.setErrorHandler(initErrorHandler);
+
+            // return the wrapped instance to the pool
+            pool.offer(new WeakReference<XMLReader>(xmlReader));
+            xmlReader = null;
+        }
+
+        private void checkValid() {
+            if (xmlReader == null)
+                throw new IllegalStateException("OneTimeXMLReader.parse() can 
only be used once!");
+        }
+
+        @Override
+        public boolean getFeature(String name)
+                throws SAXNotRecognizedException, SAXNotSupportedException {
+            checkValid();
+            return xmlReader.getFeature(name);
+        }
+
+        @Override
+        public void setFeature(String name, boolean value)
+                throws SAXNotRecognizedException, SAXNotSupportedException {
+            checkValid();
+            if (!initFeatures.containsKey(name))
+                initFeatures.put(name, 
Boolean.valueOf(xmlReader.getFeature(name)));
+            xmlReader.setFeature(name, value);
+        }
+
+        @Override
+        public Object getProperty(String name)
+                throws SAXNotRecognizedException, SAXNotSupportedException {
+            checkValid();
+            return xmlReader.getProperty(name);
+        }
+
+        @Override
+        public void setProperty(String name, Object value)
+                throws SAXNotRecognizedException, SAXNotSupportedException {
+            checkValid();
+            if (!initProperties.containsKey(name))
+                initProperties.put(name, xmlReader.getProperty(name));
+            xmlReader.setProperty(name, value);
+        }
+
+        @Override
+        public ContentHandler getContentHandler() {
+            checkValid();
+            return xmlReader.getContentHandler();
+        }
+
+        @Override
+        public void setContentHandler(ContentHandler handler) {
+            checkValid();
+            xmlReader.setContentHandler(handler);
+        }
+
+        @Override
+        public DTDHandler getDTDHandler() {
+            checkValid();
+            return xmlReader.getDTDHandler();
+        }
+
+        @Override
+        public void setDTDHandler(DTDHandler handler) {
+            checkValid();
+            xmlReader.setDTDHandler(handler);
+        }
+
+        @Override
+        public EntityResolver getEntityResolver() {
+            checkValid();
+            return xmlReader.getEntityResolver();
+        }
+
+        @Override
+        public void setEntityResolver(EntityResolver resolver) {
+            checkValid();
+            xmlReader.setEntityResolver(resolver);
+        }
+
+        @Override
+        public ErrorHandler getErrorHandler() {
+            checkValid();
+            return xmlReader.getErrorHandler();
+        }
+
+        @Override
+        public void setErrorHandler(ErrorHandler handler) {
+            checkValid();
+            xmlReader.setErrorHandler(handler);
+        }
+
+        @Override
+        public void parse(InputSource input) throws IOException, SAXException {
+            checkValid();
+            try {
+                xmlReader.parse(input);
+            } finally {
+                release();
+            }
+        }
+
+        @Override
+        public void parse(String systemId) throws IOException, SAXException {
+            checkValid();
+            try {
+                xmlReader.parse(systemId);
+            } finally {
+                release();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/3c8560f0/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
----------------------------------------------------------------------
diff --git 
a/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java 
b/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
index d29f643..649d327 100644
--- a/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
+++ b/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
@@ -36,7 +36,6 @@ import java.util.Properties;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamReader;
@@ -91,6 +90,7 @@ public class XmlConverter {
 
     private volatile DocumentBuilderFactory documentBuilderFactory;
     private volatile TransformerFactory transformerFactory;
+    private volatile XMLReaderPool xmlReaderPool;
 
     public XmlConverter() {
     }
@@ -581,31 +581,25 @@ public class XmlConverter {
         }
         inputSource.setSystemId(source.getSystemId());
         inputSource.setPublicId(source.getPublicId());
+
         XMLReader xmlReader = null;
-        SAXParserFactory sfactory = null;
-        //Need to setup XMLReader security feature by default
         try {
             // use the SAXPaserFactory which is set from exchange
             if (exchange != null) {
-                sfactory = exchange.getProperty(Exchange.SAXPARSER_FACTORY, 
SAXParserFactory.class);
-            }
-            if (sfactory == null) {
-                sfactory = SAXParserFactory.newInstance();
-                try {
-                    
sfactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
-                } catch (Exception e) {
-                    LOG.warn("SAXParser doesn't support the feature {} with 
value {}, due to {}.", new 
Object[]{javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, "true", e});
+                SAXParserFactory sfactory = 
exchange.getProperty(Exchange.SAXPARSER_FACTORY, SAXParserFactory.class);
+                if (sfactory != null) {
+                    if (!sfactory.isNamespaceAware()) {
+                        sfactory.setNamespaceAware(true);
+                    }
+                    xmlReader = sfactory.newSAXParser().getXMLReader();
                 }
-                try {
-                    
sfactory.setFeature("http://xml.org/sax/features/external-general-entities";, 
false);
-                } catch (SAXException e) {
-                    LOG.warn("SAXParser doesn't support the feature {} with 
value {}, due to {}."
-                            , new 
Object[]{"http://xml.org/sax/features/external-general-entities";, false, e});   
             
+            }
+            if (xmlReader == null) {
+                if (xmlReaderPool == null) {
+                    xmlReaderPool = new 
XMLReaderPool(createSAXParserFactory());
                 }
+                xmlReader = xmlReaderPool.createXMLReader();
             }
-            sfactory.setNamespaceAware(true);
-            SAXParser parser = sfactory.newSAXParser();
-            xmlReader = parser.getXMLReader();
         } catch (Exception ex) {
             LOG.warn("Cannot create the SAXParser XMLReader, due to {}", ex);
         }
@@ -1208,4 +1202,22 @@ public class XmlConverter {
             }
         }
     }
+
+    public SAXParserFactory createSAXParserFactory() {
+        SAXParserFactory sfactory = SAXParserFactory.newInstance();
+        // Need to setup XMLReader security feature by default
+        try {
+            
sfactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        } catch (Exception e) {
+            LOG.warn("SAXParser doesn't support the feature {} with value {}, 
due to {}.", new Object[]{javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, 
"true", e});
+        }
+        try {
+            
sfactory.setFeature("http://xml.org/sax/features/external-general-entities";, 
false);
+        } catch (Exception e) {
+            LOG.warn("SAXParser doesn't support the feature {} with value {}, 
due to {}."
+                    , new 
Object[]{"http://xml.org/sax/features/external-general-entities";, false, e});
+        }
+        sfactory.setNamespaceAware(true);
+        return sfactory;
+    }
 }

Reply via email to