src/javax/xml/parsers/FactoryFinder.java

Print this page

        

@@ -23,47 +23,44 @@
  * questions.
  */
 
 package javax.xml.parsers;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
 
 /**
  * <p>Implements pluggable Datatypes.</p>
  *
- * <p>This class is duplicated for each JAXP subpackage so keep it in
- * sync.  It is package private for secure class loading.</p>
+ * <p>This class is duplicated for each JAXP subpackage so keep it in sync. It
+ * is package private for secure class loading.</p>
  *
  * @author Santiago.PericasGeertsen@sun.com
  * @author Huizhe.Wang@oracle.com
  */
 class FactoryFinder {
 
     /**
      * Internal debug flag.
      */
     private static boolean debug = false;
-
     /**
      * Cache for properties in java.home/lib/jaxp.properties
      */
     static Properties cacheProps = new Properties();
-
     /**
-     * Flag indicating if properties from java.home/lib/jaxp.properties
-     * have been cached.
+     * Flag indicating if properties from java.home/lib/jaxp.properties have
+     * been cached.
      */
     static volatile boolean firstTime = true;
-
     /**
-     * Security support class use to check access control before
-     * getting certain system resources.
+     * Security support class use to check access control before getting certain
+     * system resources.
      */
     static SecuritySupport ss = new SecuritySupport();
 
     // Define system property "jaxp.debug" to get output
     static {

@@ -71,12 +68,11 @@
         // SecurityException out of this code.
         try {
             String val = ss.getSystemProperty("jaxp.debug");
             // Allow simply setting the prop to turn on debug
             debug = val != null && !"false".equals(val);
-        }
-        catch (SecurityException se) {
+        } catch (SecurityException se) {
             debug = false;
         }
     }
 
     private static void dPrint(String msg) {

@@ -88,147 +84,141 @@
     /**
      * Attempt to load a class using the class loader supplied. If that fails
      * and fall back is enabled, the current (i.e. bootstrap) class loader is
      * tried.
      *
-     * If the class loader supplied is <code>null</code>, first try using the
-     * context class loader followed by the current (i.e. bootstrap) class
-     * loader.
+     * If the class loader supplied is
+     * <code>null</code>, first try using the context class loader followed by
+     * the current (i.e. bootstrap) class loader.
      *
      * Use bootstrap classLoader if cl = null and useBSClsLoader is true
      */
     static private Class getProviderClass(String className, ClassLoader cl,
-            boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
-    {
+            boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException {
         try {
             if (cl == null) {
                 if (useBSClsLoader) {
                     return Class.forName(className, true, FactoryFinder.class.getClassLoader());
                 } else {
                     cl = ss.getContextClassLoader();
                     if (cl == null) {
                         throw new ClassNotFoundException();
-                    }
-                    else {
+                    } else {
                         return cl.loadClass(className);
                     }
                 }
-            }
-            else {
+            } else {
                 return cl.loadClass(className);
             }
-        }
-        catch (ClassNotFoundException e1) {
+        } catch (ClassNotFoundException e1) {
             if (doFallback) {
                 // Use current class loader - should always be bootstrap CL
                 return Class.forName(className, true, FactoryFinder.class.getClassLoader());
-            }
-            else {
+            } else {
                 throw e1;
             }
         }
     }
 
     /**
      * Create an instance of a class. Delegates to method
      * <code>getProviderClass()</code> in order to load the class.
      *
-     * @param className Name of the concrete class corresponding to the
-     * service provider
+     * @param className Name of the concrete class corresponding to the service
+     * provider
      *
-     * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>
-     * current <code>Thread</code>'s context classLoader is used to load the factory class.
+     * @param cl <code>ClassLoader</code> used to load the factory class.
+     * If <code>null</code> current <code>Thread</code>'s context classLoader is
+     * used to load the factory class.
      *
-     * @param doFallback True if the current ClassLoader should be tried as
-     * a fallback if the class is not found using cl
+     * @param doFallback True if the current ClassLoader should be tried as a
+     * fallback if the class is not found using cl
      */
     static Object newInstance(String className, ClassLoader cl, boolean doFallback)
-        throws ConfigurationError
-    {
+            throws FactoryConfigurationError {
         return newInstance(className, cl, doFallback, false);
     }
 
     /**
      * Create an instance of a class. Delegates to method
      * <code>getProviderClass()</code> in order to load the class.
      *
-     * @param className Name of the concrete class corresponding to the
-     * service provider
-     *
-     * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>
-     * current <code>Thread</code>'s context classLoader is used to load the factory class.
-     *
-     * @param doFallback True if the current ClassLoader should be tried as
-     * a fallback if the class is not found using cl
+     * @param className Name of the concrete class corresponding to the service
+     * provider
      *
-     * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
-     * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
+     * @param cl <code>ClassLoader</code> used to load the factory class.
+     * If <code>null</code> current <code>Thread</code>'s context classLoader is
+     * used to load the factory class.
+     *
+     * @param doFallback True if the current ClassLoader should be tried as a
+     * fallback if the class is not found using cl
+     *
+     * @param useBSClsLoader True if cl=null actually meant bootstrap
+     * classLoader. This parameter is needed since
+     * DocumentBuilderFactory/SAXParserFactory defined null as context
+     * classLoader.
      */
     static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader)
-        throws ConfigurationError
-    {
+            throws FactoryConfigurationError {
         try {
             Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
             Object instance = providerClass.newInstance();
             if (debug) {    // Extra check to avoid computing cl strings
-                dPrint("created new instance of " + providerClass +
-                       " using ClassLoader: " + cl);
+                dPrint("created new instance of " + providerClass
+                        + " using ClassLoader: " + cl);
             }
             return instance;
-        }
-        catch (ClassNotFoundException x) {
-            throw new ConfigurationError(
-                "Provider " + className + " not found", x);
-        }
-        catch (Exception x) {
-            throw new ConfigurationError(
-                "Provider " + className + " could not be instantiated: " + x,
-                x);
+        } catch (ClassNotFoundException x) {
+            throw new FactoryConfigurationError(x,
+                    "Provider " + className + " not found");
+        } catch (Exception x) {
+            throw new FactoryConfigurationError(x,
+                    "Provider " + className + " could not be instantiated: " + x);
         }
     }
 
     /**
-     * Finds the implementation Class object in the specified order.  Main
-     * entry point.
+     * Finds the implementation Class object in the specified order. Main entry
+     * point.
+     *
      * @return Class object of factory, never null
      *
-     * @param factoryId             Name of the factory to find, same as
-     *                              a property name
-     * @param fallbackClassName     Implementation class name, if nothing else
-     *                              is found.  Use null to mean no fallback.
+     * @param factoryId Name of the factory to find, same as a property name
+     * @param fallbackClassName Implementation class name, if nothing else is
+     * found. Use null to mean no fallback.
      *
      * Package private so this code can be shared.
      */
-    static Object find(String factoryId, String fallbackClassName)
-        throws ConfigurationError
-    {
+    static Object find(Class factoryClass, String factoryId, String fallbackClassName)
+            throws FactoryConfigurationError {
         dPrint("find factoryId =" + factoryId);
 
         // Use the system property first
         try {
             String systemProp = ss.getSystemProperty(factoryId);
             if (systemProp != null) {
                 dPrint("found system property, value=" + systemProp);
                 return newInstance(systemProp, null, true);
             }
+        } catch (SecurityException se) {
+            if (debug) {
+                se.printStackTrace();
         }
-        catch (SecurityException se) {
-            if (debug) se.printStackTrace();
         }
 
         // try to read from $java.home/lib/jaxp.properties
         try {
             String factoryClassName = null;
             if (firstTime) {
                 synchronized (cacheProps) {
                     if (firstTime) {
-                        String configFile = ss.getSystemProperty("java.home") + File.separator +
-                            "lib" + File.separator + "jaxp.properties";
+                        String configFile = ss.getSystemProperty("java.home") + File.separator
+                                + "lib" + File.separator + "jaxp.properties";
                         File f = new File(configFile);
                         firstTime = false;
                         if (ss.doesFileExist(f)) {
-                            dPrint("Read properties file "+f);
+                            dPrint("Read properties file " + f);
                             cacheProps.load(ss.getFileInputStream(f));
                         }
                     }
                 }
             }

@@ -236,121 +226,59 @@
 
             if (factoryClassName != null) {
                 dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
                 return newInstance(factoryClassName, null, true);
             }
+        } catch (Exception ex) {
+            if (debug) {
+                ex.printStackTrace();
         }
-        catch (Exception ex) {
-            if (debug) ex.printStackTrace();
         }
 
-        // Try Jar Service Provider Mechanism
-        Object provider = findJarServiceProvider(factoryId);
+        // Try service-provider loading facilities
+        try {
+            Object provider = findServiceProvider(factoryClass, fallbackClassName);
         if (provider != null) {
             return provider;
         }
+        } catch (FactoryConfigurationError e) {
+            throw e;
+        }
+
+        // Last, check if fallback is requested
         if (fallbackClassName == null) {
-            throw new ConfigurationError(
-                "Provider for " + factoryId + " cannot be found", null);
+            throw new FactoryConfigurationError("Provider for " + factoryId + " cannot be found");
         }
 
         dPrint("loaded from fallback value: " + fallbackClassName);
         return newInstance(fallbackClassName, null, true);
     }
 
     /*
-     * Try to find provider using Jar Service Provider Mechanism
+     * Try to find a provider using Service Loader
      *
      * @return instance of provider class if found or null
      */
-    private static Object findJarServiceProvider(String factoryId)
-        throws ConfigurationError
-    {
-        String serviceId = "META-INF/services/" + factoryId;
-        InputStream is = null;
-
-        // First try the Context ClassLoader
-        ClassLoader cl = ss.getContextClassLoader();
-        boolean useBSClsLoader = false;
-        if (cl != null) {
-            is = ss.getResourceAsStream(cl, serviceId);
-
-            // If no provider found then try the current ClassLoader
-            if (is == null) {
-                cl = FactoryFinder.class.getClassLoader();
-                is = ss.getResourceAsStream(cl, serviceId);
-                useBSClsLoader = true;
-            }
-        } else {
-            // No Context ClassLoader, try the current ClassLoader
-            cl = FactoryFinder.class.getClassLoader();
-            is = ss.getResourceAsStream(cl, serviceId);
-            useBSClsLoader = true;
-        }
-
-        if (is == null) {
-            // No provider found
-            return null;
-        }
-
-        if (debug) {    // Extra check to avoid computing cl strings
-            dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
-        }
-
-        BufferedReader rd;
+    private static Object findServiceProvider(final Class factoryClass, final String fallbackClassName)
+            throws FactoryConfigurationError {
         try {
-            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
-        }
-        catch (java.io.UnsupportedEncodingException e) {
-            rd = new BufferedReader(new InputStreamReader(is));
+            return AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    Object defaultProvider = null;
+                    for (Object factory : ServiceLoader.load(factoryClass)) {
+                        if (factory.getClass().getName().equals(fallbackClassName)) {
+                            defaultProvider = factory;
+                        } else {
+                            return factory;
         }
-
-        String factoryClassName = null;
-        try {
-            // XXX Does not handle all possible input as specified by the
-            // Jar Service Provider specification
-            factoryClassName = rd.readLine();
-            rd.close();
-        } catch (IOException x) {
-            // No provider found
-            return null;
         }
-
-        if (factoryClassName != null && !"".equals(factoryClassName)) {
-            dPrint("found in resource, value=" + factoryClassName);
-
-            // Note: here we do not want to fall back to the current
-            // ClassLoader because we want to avoid the case where the
-            // resource file was found using one ClassLoader and the
-            // provider class was instantiated using a different one.
-            return newInstance(factoryClassName, cl, false, useBSClsLoader);
+                    if (defaultProvider != null) {
+                        return defaultProvider;
         }
-
-        // No provider found
         return null;
     }
-
-    static class ConfigurationError extends Error {
-        private Exception exception;
-
-        /**
-         * Construct a new instance with the specified detail string and
-         * exception.
-         */
-        ConfigurationError(String msg, Exception x) {
-            super(msg);
-            this.exception = x;
-        }
-
-        Exception getException() {
-            return exception;
-        }
-        /**
-        * use the exception chaining mechanism of JDK1.4
-        */
-        @Override
-        public Throwable getCause() {
-            return exception;
+            });
+        } catch (ServiceConfigurationError e) {
+            throw new FactoryConfigurationError((Exception) e.getCause(), e.getMessage());
         }
     }
-
 }