org.apache.camel.converter.jaxp.XMLReaderPool Maven / Gradle / Ivy
The newest version!
/*
* 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> pool = new ConcurrentLinkedQueue<>();
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 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 final class OneTimeXMLReader implements XMLReader {
private final XMLReader xmlReader;
private final Map initFeatures = new HashMap<>();
private final Map initProperties = new HashMap<>();
private final ContentHandler initContentHandler;
private final DTDHandler initDtdHandler;
private final EntityResolver initEntityResolver;
private final ErrorHandler initErrorHandler;
private boolean readerInvalid;
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() {
try {
// reset XMLReader to its initial state
for (Map.Entry feature : initFeatures.entrySet()) {
try {
xmlReader.setFeature(feature.getKey(), feature.getValue().booleanValue());
} catch (Exception e) {
// ignore
}
}
for (Map.Entry 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));
} finally {
readerInvalid = true;
}
}
@Override
public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
return xmlReader.getFeature(name);
}
@Override
public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
if (!readerInvalid) {
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 {
return xmlReader.getProperty(name);
}
@Override
public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
if (!readerInvalid) {
if (!initProperties.containsKey(name)) {
initProperties.put(name, xmlReader.getProperty(name));
}
xmlReader.setProperty(name, value);
}
}
@Override
public ContentHandler getContentHandler() {
return xmlReader.getContentHandler();
}
@Override
public void setContentHandler(ContentHandler handler) {
if (!readerInvalid) {
xmlReader.setContentHandler(handler);
}
}
@Override
public DTDHandler getDTDHandler() {
return xmlReader.getDTDHandler();
}
@Override
public void setDTDHandler(DTDHandler handler) {
if (!readerInvalid) {
xmlReader.setDTDHandler(handler);
}
}
@Override
public EntityResolver getEntityResolver() {
return xmlReader.getEntityResolver();
}
@Override
public void setEntityResolver(EntityResolver resolver) {
if (!readerInvalid) {
xmlReader.setEntityResolver(resolver);
}
}
@Override
public ErrorHandler getErrorHandler() {
return xmlReader.getErrorHandler();
}
@Override
public void setErrorHandler(ErrorHandler handler) {
if (!readerInvalid) {
xmlReader.setErrorHandler(handler);
}
}
@Override
public synchronized void parse(InputSource input) throws IOException, SAXException {
checkValid();
try {
xmlReader.parse(input);
} finally {
release();
}
}
@Override
public synchronized void parse(String systemId) throws IOException, SAXException {
checkValid();
try {
xmlReader.parse(systemId);
} finally {
release();
}
}
private void checkValid() {
if (readerInvalid) {
throw new IllegalStateException("OneTimeXMLReader can only be used once!");
}
}
}
}