19f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson/*
29f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * Licensed to the Apache Software Foundation (ASF) under one
39f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * or more contributor license agreements. See the NOTICE file
49f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * distributed with this work for additional information
59f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * regarding copyright ownership. The ASF licenses this file
69f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * to you under the Apache License, Version 2.0 (the  "License");
79f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * you may not use this file except in compliance with the License.
89f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * You may obtain a copy of the License at
99f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson *
109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson *     http://www.apache.org/licenses/LICENSE-2.0
119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson *
129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * Unless required by applicable law or agreed to in writing, software
139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * See the License for the specific language governing permissions and
169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * limitations under the License.
179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson */
189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson/*
199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * $Id: XMLReaderManager.java 468655 2006-10-28 07:12:06Z minchau $
209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson */
219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonpackage org.apache.xml.utils;
229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport java.util.Hashtable;
249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.parsers.FactoryConfigurationError;
269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.parsers.ParserConfigurationException;
279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.parsers.SAXParserFactory;
289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.xml.sax.XMLReader;
309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.xml.sax.helpers.XMLReaderFactory;
319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.xml.sax.SAXException;
329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson/**
349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * Creates XMLReader objects and caches them for re-use.
359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * This class follows the singleton pattern.
369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson */
379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonpublic class XMLReaderManager {
389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private static final String NAMESPACES_FEATURE =
409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                             "http://xml.org/sax/features/namespaces";
419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private static final String NAMESPACE_PREFIXES_FEATURE =
429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                             "http://xml.org/sax/features/namespace-prefixes";
439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private static final XMLReaderManager m_singletonManager =
449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                                     new XMLReaderManager();
459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Parser factory to be used to construct XMLReader objects
489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private static SAXParserFactory m_parserFactory;
509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Cache of XMLReader objects
539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private ThreadLocal m_readers;
559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Keeps track of whether an XMLReader object is in use.
589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private Hashtable m_inUse;
609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Hidden constructor
639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    private XMLReaderManager() {
659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Retrieves the singleton reader manager
699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    public static XMLReaderManager getInstance() {
719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        return m_singletonManager;
729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Retrieves a cached XMLReader for this thread, or creates a new
769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * XMLReader, if the existing reader is in use.  When the caller no
779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * longer needs the reader, it must release it with a call to
789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * {@link #releaseXMLReader}.
799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    public synchronized XMLReader getXMLReader() throws SAXException {
819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        XMLReader reader;
829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        boolean readerInUse;
839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (m_readers == null) {
859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            // When the m_readers.get() method is called for the first time
869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            // on a thread, a new XMLReader will automatically be created.
879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_readers = new ThreadLocal();
889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (m_inUse == null) {
919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_inUse = new Hashtable();
929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // If the cached reader for this thread is in use, construct a new
959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // one; otherwise, return the cached reader.
969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        reader = (XMLReader) m_readers.get();
979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        boolean threadHasReader = (reader != null);
989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (!threadHasReader || m_inUse.get(reader) == Boolean.TRUE) {
999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            try {
1009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                try {
1019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    // According to JAXP 1.2 specification, if a SAXSource
1029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    // is created using a SAX InputSource the Transformer or
1039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    // TransformerFactory creates a reader via the
1049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    // XMLReaderFactory if setXMLReader is not used
1059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    reader = XMLReaderFactory.createXMLReader();
1069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                } catch (Exception e) {
1079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                   try {
1089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // If unable to create an instance, let's try to use
1099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // the XMLReader from JAXP
1109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        if (m_parserFactory == null) {
1119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                            m_parserFactory = SAXParserFactory.newInstance();
1129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                            m_parserFactory.setNamespaceAware(true);
1139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        }
1149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        reader = m_parserFactory.newSAXParser().getXMLReader();
1169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                   } catch (ParserConfigurationException pce) {
1179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                       throw pce;   // pass along pce
1189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                   }
1199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                }
1209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                try {
1219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    reader.setFeature(NAMESPACES_FEATURE, true);
1229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    reader.setFeature(NAMESPACE_PREFIXES_FEATURE, false);
1239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                } catch (SAXException se) {
1249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    // Try to carry on if we've got a parser that
1259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    // doesn't know about namespace prefixes.
1269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                }
1279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            } catch (ParserConfigurationException ex) {
1289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                throw new SAXException(ex);
1299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            } catch (FactoryConfigurationError ex1) {
1309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                throw new SAXException(ex1.toString());
1319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            } catch (NoSuchMethodError ex2) {
1329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            } catch (AbstractMethodError ame) {
1339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            }
1349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            // Cache the XMLReader if this is the first time we've created
1369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            // a reader for this thread.
1379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            if (!threadHasReader) {
1389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                m_readers.set(reader);
1399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                m_inUse.put(reader, Boolean.TRUE);
1409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            }
1419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        } else {
1429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_inUse.put(reader, Boolean.TRUE);
1439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
1449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        return reader;
1469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
1479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /**
1499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * Mark the cached XMLReader as available.  If the reader was not
1509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * actually in the cache, do nothing.
1519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     *
1529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     * @param reader The XMLReader that's being released.
1539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson     */
1549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    public synchronized void releaseXMLReader(XMLReader reader) {
1559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // If the reader that's being released is the cached reader
1569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // for this thread, remove it from the m_isUse list.
1579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (m_readers.get() == reader && reader != null) {
1589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_inUse.remove(reader);
1599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
1609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
1619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson}
162