001    // XMLReaderFactory.java - factory for creating a new reader.
002    // http://www.saxproject.org
003    // Written by David Megginson
004    // and by David Brownell
005    // NO WARRANTY!  This class is in the Public Domain.
006    // $Id: XMLReaderFactory.java 670295 2008-06-22 01:46:43Z mrglavas $
007    
008    package org.xml.sax.helpers;
009    import java.io.BufferedReader;
010    import java.io.IOException;
011    import java.io.InputStream;
012    import java.io.InputStreamReader;
013    import org.xml.sax.XMLReader;
014    import org.xml.sax.SAXException;
015    
016    
017    /**
018     * Factory for creating an XML reader.
019     *
020     * <blockquote>
021     * <em>This module, both source code and documentation, is in the
022     * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
023     * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
024     * for further information.
025     * </blockquote>
026     *
027     * <p>This class contains static methods for creating an XML reader
028     * from an explicit class name, or based on runtime defaults:</p>
029     *
030     * <pre>
031     * try {
032     *   XMLReader myReader = XMLReaderFactory.createXMLReader();
033     * } catch (SAXException e) {
034     *   System.err.println(e.getMessage());
035     * }
036     * </pre>
037     *
038     * <p><strong>Note to Distributions bundled with parsers:</strong>
039     * You should modify the implementation of the no-arguments
040     * <em>createXMLReader</em> to handle cases where the external
041     * configuration mechanisms aren't set up.  That method should do its
042     * best to return a parser when one is in the class path, even when
043     * nothing bound its class name to <code>org.xml.sax.driver</code> so
044     * those configuration mechanisms would see it.</p>
045     *
046     * @since SAX 2.0
047     * @author David Megginson, David Brownell
048     * @version 2.0.1 (sax2r2)
049     */
050    final public class XMLReaderFactory
051    {
052        /**
053         * Private constructor.
054         *
055         * <p>This constructor prevents the class from being instantiated.</p>
056         */
057        private XMLReaderFactory ()
058        {
059        }
060        
061        private static final String property = "org.xml.sax.driver";
062        
063        /**
064         * Default columns per line.
065         */
066        private static final int DEFAULT_LINE_LENGTH = 80;
067        
068        /**
069         * Attempt to create an XMLReader from system defaults.
070         * In environments which can support it, the name of the XMLReader
071         * class is determined by trying each these options in order, and
072         * using the first one which succeeds:</p> <ul>
073         *
074         * <li>If the system property <code>org.xml.sax.driver</code>
075         * has a value, that is used as an XMLReader class name. </li>
076         *
077         * <li>The JAR "Services API" is used to look for a class name
078         * in the <em>META-INF/services/org.xml.sax.driver</em> file in
079         * jarfiles available to the runtime.</li>
080         *
081         * <li> SAX parser distributions are strongly encouraged to provide
082         * a default XMLReader class name that will take effect only when
083         * previous options (on this list) are not successful.</li>
084         *
085         * <li>Finally, if {@link ParserFactory#makeParser()} can
086         * return a system default SAX1 parser, that parser is wrapped in
087         * a {@link ParserAdapter}.  (This is a migration aid for SAX1
088         * environments, where the <code>org.xml.sax.parser</code> system
089         * property will often be usable.) </li>
090         *
091         * </ul>
092         *
093         * <p> In environments such as small embedded systems, which can not
094         * support that flexibility, other mechanisms to determine the default
095         * may be used. </p>
096         *
097         * <p>Note that many Java environments allow system properties to be
098         * initialized on a command line.  This means that <em>in most cases</em>
099         * setting a good value for that property ensures that calls to this
100         * method will succeed, except when security policies intervene.
101         * This will also maximize application portability to older SAX
102         * environments, with less robust implementations of this method.
103         * </p>
104         *
105         * @return A new XMLReader.
106         * @exception org.xml.sax.SAXException If no default XMLReader class
107         *            can be identified and instantiated.
108         * @see #createXMLReader(java.lang.String)
109         */
110        public static XMLReader createXMLReader ()
111        throws SAXException
112        {
113            String          className = null;
114            ClassLoader     loader = NewInstance.getClassLoader ();
115            
116            // 1. try the JVM-instance-wide system property
117            try { className = SecuritySupport.getSystemProperty (property); }
118            catch (Exception e) { /* normally fails for applets */ }
119            
120            // 2. if that fails, try META-INF/services/
121            if (className == null || className.length() == 0) {
122                String      service = "META-INF/services/" + property;
123                
124                    try {
125                        // If we are deployed into an OSGi environment, leverage it
126                        Class spiClass = org.apache.servicemix.specs.locator.OsgiLocator.locate(property);
127                        if (spiClass != null) {
128                            return (XMLReader) spiClass.newInstance();
129                        }
130                    } catch (Throwable e) {
131                    }
132    
133                InputStream is = null;
134                className = null;
135                
136                // First try the Context ClassLoader
137                ClassLoader cl = SecuritySupport.getContextClassLoader();
138                if (cl != null) {
139                    is = SecuritySupport.getResourceAsStream(cl, service);
140                    
141                    // If no provider found then try the current ClassLoader
142                    if (is == null) {
143                        cl = XMLReaderFactory.class.getClassLoader();
144                        is = SecuritySupport.getResourceAsStream(cl, service);
145                    }
146                } else {
147                    // No Context ClassLoader or JDK 1.1 so try the current
148                    // ClassLoader
149                    cl = XMLReaderFactory.class.getClassLoader();
150                    is = SecuritySupport.getResourceAsStream(cl, service);
151                }
152                
153                if (is != null) {
154                    
155                    // Read the service provider name in UTF-8 as specified in
156                    // the jar spec.  Unfortunately this fails in Microsoft
157                    // VJ++, which does not implement the UTF-8
158                    // encoding. Theoretically, we should simply let it fail in
159                    // that case, since the JVM is obviously broken if it
160                    // doesn't support such a basic standard.  But since there
161                    // are still some users attempting to use VJ++ for
162                    // development, we have dropped in a fallback which makes a
163                    // second attempt using the platform's default encoding. In
164                    // VJ++ this is apparently ASCII, which is a subset of
165                    // UTF-8... and since the strings we'll be reading here are
166                    // also primarily limited to the 7-bit ASCII range (at
167                    // least, in English versions), this should work well
168                    // enough to keep us on the air until we're ready to
169                    // officially decommit from VJ++. [Edited comment from
170                    // jkesselm]
171                    BufferedReader rd;
172                    try {
173                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
174                    } catch (java.io.UnsupportedEncodingException e) {
175                        rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
176                    }
177                    
178                    try {
179                        // XXX Does not handle all possible input as specified by the
180                        // Jar Service Provider specification
181                        className = rd.readLine();
182                    } 
183                    catch (Exception x) {
184                        // No provider found
185                    } 
186                    finally {
187                        try { 
188                            // try to close the reader. 
189                            rd.close(); 
190                        } 
191                        // Ignore the exception. 
192                        catch (IOException exc) {}
193                    }
194                }
195            }
196            
197            // 3. Distro-specific fallback
198            if (className == null) {
199                // BEGIN DISTRIBUTION-SPECIFIC
200                
201                // EXAMPLE:
202                // className = "com.example.sax.XmlReader";
203                // or a $JAVA_HOME/jre/lib/*properties setting...
204                className = "org.apache.xerces.parsers.SAXParser";
205                
206                // END DISTRIBUTION-SPECIFIC
207            }
208            
209            // do we know the XMLReader implementation class yet?
210            if (className != null)
211                return loadClass (loader, className);
212            
213            // 4. panic -- adapt any SAX1 parser
214            try {
215                return new ParserAdapter (ParserFactory.makeParser ());
216            } catch (Exception e) {
217                throw new SAXException ("Can't create default XMLReader; "
218                        + "is system property org.xml.sax.driver set?");
219            }
220        }
221        
222        
223        /**
224         * Attempt to create an XML reader from a class name.
225         *
226         * <p>Given a class name, this method attempts to load
227         * and instantiate the class as an XML reader.</p>
228         *
229         * <p>Note that this method will not be usable in environments where
230         * the caller (perhaps an applet) is not permitted to load classes
231         * dynamically.</p>
232         *
233         * @return A new XML reader.
234         * @exception org.xml.sax.SAXException If the class cannot be
235         *            loaded, instantiated, and cast to XMLReader.
236         * @see #createXMLReader()
237         */
238        public static XMLReader createXMLReader (String className)
239        throws SAXException
240        {
241            return loadClass (NewInstance.getClassLoader (), className);
242        }
243        
244        private static XMLReader loadClass (ClassLoader loader, String className)
245        throws SAXException
246        {
247            try {
248                return (XMLReader) NewInstance.newInstance (loader, className);
249            } catch (ClassNotFoundException e1) {
250                throw new SAXException("SAX2 driver class " + className +
251                        " not found", e1);
252            } catch (IllegalAccessException e2) {
253                throw new SAXException("SAX2 driver class " + className +
254                        " found but cannot be loaded", e2);
255            } catch (InstantiationException e3) {
256                throw new SAXException("SAX2 driver class " + className +
257                        " loaded but cannot be instantiated (no empty public constructor?)",
258                        e3);
259            } catch (ClassCastException e4) {
260                throw new SAXException("SAX2 driver class " + className +
261                        " does not implement XMLReader", e4);
262            }
263        }
264    }