1 /*
   2  * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package org.xml.sax.helpers;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.io.InputStreamReader;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 import java.util.Iterator;
  35 import java.util.Objects;
  36 import java.util.ServiceConfigurationError;
  37 import java.util.ServiceLoader;
  38 import jdk.xml.internal.SecuritySupport;
  39 import org.xml.sax.SAXException;
  40 import org.xml.sax.XMLReader;
  41 
  42 
  43 /**
  44  * Factory for creating an XML reader.
  45  *
  46  * <p>This class contains static methods for creating an XML reader
  47  * from an explicit class name, or based on runtime defaults:
  48  *
  49  * <pre>
  50  * try {
  51  *   XMLReader myReader = XMLReaderFactory.createXMLReader();
  52  * } catch (SAXException e) {
  53  *   System.err.println(e.getMessage());
  54  * }
  55  * </pre>
  56  *
  57  * <p><strong>Note to Distributions bundled with parsers:</strong>
  58  * You should modify the implementation of the no-arguments
  59  * <em>createXMLReader</em> to handle cases where the external
  60  * configuration mechanisms aren't set up.  That method should do its
  61  * best to return a parser when one is in the class path, even when
  62  * nothing bound its class name to {@code org.xml.sax.driver} so
  63  * those configuration mechanisms would see it.
  64  *
  65  * @since 1.4, SAX 2.0
  66  * @author David Megginson, David Brownell
  67  * @version 2.0.1 (sax2r2)
  68  *
  69  * @deprecated It is recommended to use {@link javax.xml.parsers.SAXParserFactory}
  70  * instead.
  71  */
  72 @Deprecated(since="9")
  73 final public class XMLReaderFactory
  74 {
  75     /**
  76      * Private constructor.
  77      *
  78      * <p>This constructor prevents the class from being instantiated.
  79      */
  80     private XMLReaderFactory ()
  81     {
  82     }
  83 
  84     private static final String property = "org.xml.sax.driver";
  85 
  86     /**
  87      * Obtains a new instance of a {@link org.xml.sax.XMLReader}.
  88      * This method uses the following ordered lookup procedure to find and load
  89      * the {@link org.xml.sax.XMLReader} implementation class:
  90      * <ol>
  91      * <li>If the system property {@code org.xml.sax.driver}
  92      * has a value, that is used as an XMLReader class name. </li>
  93      * <li>
  94      * Use the service-provider loading facility, defined by the
  95      * {@link java.util.ServiceLoader} class, to attempt to locate and load an
  96      * implementation of the service {@link org.xml.sax.XMLReader} by using the
  97      * {@linkplain java.lang.Thread#getContextClassLoader() current thread's context class loader}.
  98      * If the context class loader is null, the
  99      * {@linkplain ClassLoader#getSystemClassLoader() system class loader} will
 100      * be used.
 101      * </li>
 102      * <li>
 103      * Deprecated. Look for a class name in the {@code META-INF/services/org.xml.sax.driver}
 104      * file in a jar file available to the runtime.</li>
 105      * <li>
 106      * <p>
 107      * Otherwise, the system-default implementation is returned.
 108      * </li>
 109      * </ol>
 110      *
 111      * @apiNote
 112      * The process that looks for a class name in the
 113      * {@code META-INF/services/org.xml.sax.driver} file in a jar file does not
 114      * conform to the specification of the service-provider loading facility
 115      * as defined in {@link java.util.ServiceLoader} and therefore does not
 116      * support modularization. It is deprecated as of Java SE 9 and subject to
 117      * removal in a future release.
 118      *
 119      * @return a new XMLReader.
 120      * @exception org.xml.sax.SAXException If no default XMLReader class
 121      *            can be identified and instantiated.
 122      * @see #createXMLReader(java.lang.String)
 123      */
 124     public static XMLReader createXMLReader ()
 125         throws SAXException
 126     {
 127         String          className = null;
 128         ClassLoader     cl = SecuritySupport.getClassLoader();
 129 
 130         // 1. try the JVM-instance-wide system property
 131         try {
 132             className = SecuritySupport.getSystemProperty(property);
 133         }
 134         catch (RuntimeException e) { /* continue searching */ }
 135 
 136         // 2. try the ServiceLoader
 137         if (className == null) {
 138             final XMLReader provider = findServiceProvider(XMLReader.class, cl);
 139             if (provider != null) {
 140                 return provider;
 141             }
 142         }
 143 
 144         // 3. try META-INF/services/org.xml.sax.driver. This old process allows
 145         // legacy providers to be found
 146         if (className == null) {
 147             className = jarLookup(cl);
 148         }
 149 
 150         // 4. Distro-specific fallback
 151         if (className == null) {
 152             return new com.sun.org.apache.xerces.internal.parsers.SAXParser();
 153         }
 154 
 155         return loadClass (cl, className);
 156     }
 157 
 158 
 159     /**
 160      * Attempt to create an XML reader from a class name.
 161      *
 162      * <p>Given a class name, this method attempts to load
 163      * and instantiate the class as an XML reader.
 164      *
 165      * <p>Note that this method will not be usable in environments where
 166      * the caller (perhaps an applet) is not permitted to load classes
 167      * dynamically.
 168      *
 169      * @param className a class name
 170      * @return A new XML reader.
 171      * @exception org.xml.sax.SAXException If the class cannot be
 172      *            loaded, instantiated, and cast to XMLReader.
 173      * @see #createXMLReader()
 174      */
 175     public static XMLReader createXMLReader (String className)
 176         throws SAXException
 177     {
 178         return loadClass (SecuritySupport.getClassLoader(), className);
 179     }
 180 
 181     private static XMLReader loadClass (ClassLoader loader, String className)
 182     throws SAXException
 183     {
 184         try {
 185             return NewInstance.newInstance (XMLReader.class, loader, className);
 186         } catch (ClassNotFoundException e1) {
 187             throw new SAXException("SAX2 driver class " + className +
 188                                    " not found", e1);
 189         } catch (IllegalAccessException e2) {
 190             throw new SAXException("SAX2 driver class " + className +
 191                                    " found but cannot be loaded", e2);
 192         } catch (InstantiationException e3) {
 193             throw new SAXException("SAX2 driver class " + className +
 194            " loaded but cannot be instantiated (no empty public constructor?)",
 195                                    e3);
 196         } catch (ClassCastException e4) {
 197             throw new SAXException("SAX2 driver class " + className +
 198                                    " does not implement XMLReader", e4);
 199         }
 200     }
 201 
 202     /**
 203      * Locates a provider by directly reading the jar service file.
 204      * @param loader the ClassLoader to be used to read the service file
 205      * @return the name of the provider, or null if nothing is found
 206      */
 207     private static String jarLookup(final ClassLoader loader) {
 208         final ClassLoader cl = Objects.requireNonNull(loader);
 209         String clsFromJar = null;
 210         String service = "META-INF/services/" + property;
 211         InputStream in;
 212         BufferedReader      reader;
 213 
 214         try {
 215             in = SecuritySupport.getResourceAsStream(cl, service);
 216 
 217             // If no provider found then try the current ClassLoader
 218             if (in == null) {
 219                 in = SecuritySupport.getResourceAsStream(null, service);
 220             }
 221 
 222             if (in != null) {
 223                 reader = new BufferedReader (new InputStreamReader (in, "UTF8"));
 224                 clsFromJar = reader.readLine ();
 225                 in.close ();
 226             }
 227         } catch (IOException e) {
 228         }
 229         return clsFromJar;
 230     }
 231 
 232     /*
 233      * Try to find provider using the ServiceLoader API
 234      *
 235      * @param type Base class / Service interface of the factory to find.
 236      *
 237      * @return instance of provider class if found or null
 238      */
 239     private static <T> T findServiceProvider(final Class<T> type, final ClassLoader loader)
 240             throws SAXException {
 241         ClassLoader cl = Objects.requireNonNull(loader);
 242         try {
 243             return AccessController.doPrivileged((PrivilegedAction<T>) () -> {
 244                 final ServiceLoader<T> serviceLoader;
 245                 serviceLoader = ServiceLoader.load(type, cl);
 246                 final Iterator<T> iterator = serviceLoader.iterator();
 247                 if (iterator.hasNext()) {
 248                     return iterator.next();
 249                 } else {
 250                     return null;
 251                 }
 252             });
 253         } catch(ServiceConfigurationError e) {
 254             final RuntimeException x = new RuntimeException(
 255                     "Provider for " + type + " cannot be created", e);
 256             throw new SAXException("Provider for " + type + " cannot be created", x);
 257 
 258           }
 259       }
 260 
 261 }