001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: XMLReaderManager.java 468655 2006-10-28 07:12:06Z minchau $
020 */
021 package org.apache.xml.utils;
022
023 import java.util.Hashtable;
024
025 import javax.xml.parsers.FactoryConfigurationError;
026 import javax.xml.parsers.ParserConfigurationException;
027 import javax.xml.parsers.SAXParserFactory;
028
029 import org.xml.sax.XMLReader;
030 import org.xml.sax.helpers.XMLReaderFactory;
031 import org.xml.sax.SAXException;
032
033 /**
034 * Creates XMLReader objects and caches them for re-use.
035 * This class follows the singleton pattern.
036 */
037 public class XMLReaderManager {
038
039 private static final String NAMESPACES_FEATURE =
040 "http://xml.org/sax/features/namespaces";
041 private static final String NAMESPACE_PREFIXES_FEATURE =
042 "http://xml.org/sax/features/namespace-prefixes";
043 private static final XMLReaderManager m_singletonManager =
044 new XMLReaderManager();
045
046 /**
047 * Parser factory to be used to construct XMLReader objects
048 */
049 private static SAXParserFactory m_parserFactory;
050
051 /**
052 * Cache of XMLReader objects
053 */
054 private ThreadLocal m_readers;
055
056 /**
057 * Keeps track of whether an XMLReader object is in use.
058 */
059 private Hashtable m_inUse;
060
061 /**
062 * Hidden constructor
063 */
064 private XMLReaderManager() {
065 }
066
067 /**
068 * Retrieves the singleton reader manager
069 */
070 public static XMLReaderManager getInstance() {
071 return m_singletonManager;
072 }
073
074 /**
075 * Retrieves a cached XMLReader for this thread, or creates a new
076 * XMLReader, if the existing reader is in use. When the caller no
077 * longer needs the reader, it must release it with a call to
078 * {@link #releaseXMLReader}.
079 */
080 public synchronized XMLReader getXMLReader() throws SAXException {
081 XMLReader reader;
082 boolean readerInUse;
083
084 if (m_readers == null) {
085 // When the m_readers.get() method is called for the first time
086 // on a thread, a new XMLReader will automatically be created.
087 m_readers = new ThreadLocal();
088 }
089
090 if (m_inUse == null) {
091 m_inUse = new Hashtable();
092 }
093
094 // If the cached reader for this thread is in use, construct a new
095 // one; otherwise, return the cached reader.
096 reader = (XMLReader) m_readers.get();
097 boolean threadHasReader = (reader != null);
098 if (!threadHasReader || m_inUse.get(reader) == Boolean.TRUE) {
099 try {
100 try {
101 // According to JAXP 1.2 specification, if a SAXSource
102 // is created using a SAX InputSource the Transformer or
103 // TransformerFactory creates a reader via the
104 // XMLReaderFactory if setXMLReader is not used
105 reader = XMLReaderFactory.createXMLReader();
106 } catch (Exception e) {
107 try {
108 // If unable to create an instance, let's try to use
109 // the XMLReader from JAXP
110 if (m_parserFactory == null) {
111 m_parserFactory = SAXParserFactory.newInstance();
112 m_parserFactory.setNamespaceAware(true);
113 }
114
115 reader = m_parserFactory.newSAXParser().getXMLReader();
116 } catch (ParserConfigurationException pce) {
117 throw pce; // pass along pce
118 }
119 }
120 try {
121 reader.setFeature(NAMESPACES_FEATURE, true);
122 reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
123 } catch (SAXException se) {
124 // Try to carry on if we've got a parser that
125 // doesn't know about namespace prefixes.
126 }
127 } catch (ParserConfigurationException ex) {
128 throw new SAXException(ex);
129 } catch (FactoryConfigurationError ex1) {
130 throw new SAXException(ex1.toString());
131 } catch (NoSuchMethodError ex2) {
132 } catch (AbstractMethodError ame) {
133 }
134
135 // Cache the XMLReader if this is the first time we've created
136 // a reader for this thread.
137 if (!threadHasReader) {
138 m_readers.set(reader);
139 m_inUse.put(reader, Boolean.TRUE);
140 }
141 } else {
142 m_inUse.put(reader, Boolean.TRUE);
143 }
144
145 return reader;
146 }
147
148 /**
149 * Mark the cached XMLReader as available. If the reader was not
150 * actually in the cache, do nothing.
151 *
152 * @param reader The XMLReader that's being released.
153 */
154 public synchronized void releaseXMLReader(XMLReader reader) {
155 // If the reader that's being released is the cached reader
156 // for this thread, remove it from the m_isUse list.
157 if (m_readers.get() == reader && reader != null) {
158 m_inUse.remove(reader);
159 }
160 }
161 }