Home | History | Annotate | Download | only in utils
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: XMLReaderManager.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xml.utils;
     22 
     23 import java.util.Hashtable;
     24 
     25 import javax.xml.parsers.FactoryConfigurationError;
     26 import javax.xml.parsers.ParserConfigurationException;
     27 import javax.xml.parsers.SAXParserFactory;
     28 
     29 import org.xml.sax.XMLReader;
     30 import org.xml.sax.helpers.XMLReaderFactory;
     31 import org.xml.sax.SAXException;
     32 
     33 /**
     34  * Creates XMLReader objects and caches them for re-use.
     35  * This class follows the singleton pattern.
     36  */
     37 public class XMLReaderManager {
     38 
     39     private static final String NAMESPACES_FEATURE =
     40                              "http://xml.org/sax/features/namespaces";
     41     private static final String NAMESPACE_PREFIXES_FEATURE =
     42                              "http://xml.org/sax/features/namespace-prefixes";
     43     private static final XMLReaderManager m_singletonManager =
     44                                                      new XMLReaderManager();
     45 
     46     /**
     47      * Parser factory to be used to construct XMLReader objects
     48      */
     49     private static SAXParserFactory m_parserFactory;
     50 
     51     /**
     52      * Cache of XMLReader objects
     53      */
     54     private ThreadLocal m_readers;
     55 
     56     /**
     57      * Keeps track of whether an XMLReader object is in use.
     58      */
     59     private Hashtable m_inUse;
     60 
     61     /**
     62      * Hidden constructor
     63      */
     64     private XMLReaderManager() {
     65     }
     66 
     67     /**
     68      * Retrieves the singleton reader manager
     69      */
     70     public static XMLReaderManager getInstance() {
     71         return m_singletonManager;
     72     }
     73 
     74     /**
     75      * Retrieves a cached XMLReader for this thread, or creates a new
     76      * XMLReader, if the existing reader is in use.  When the caller no
     77      * longer needs the reader, it must release it with a call to
     78      * {@link #releaseXMLReader}.
     79      */
     80     public synchronized XMLReader getXMLReader() throws SAXException {
     81         XMLReader reader;
     82         boolean readerInUse;
     83 
     84         if (m_readers == null) {
     85             // When the m_readers.get() method is called for the first time
     86             // on a thread, a new XMLReader will automatically be created.
     87             m_readers = new ThreadLocal();
     88         }
     89 
     90         if (m_inUse == null) {
     91             m_inUse = new Hashtable();
     92         }
     93 
     94         // If the cached reader for this thread is in use, construct a new
     95         // one; otherwise, return the cached reader.
     96         reader = (XMLReader) m_readers.get();
     97         boolean threadHasReader = (reader != null);
     98         if (!threadHasReader || m_inUse.get(reader) == Boolean.TRUE) {
     99             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 }
    162