src/javax/xml/parsers/FactoryFinder.java

Print this page

        

@@ -23,19 +23,20 @@
  * 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.Iterator;
 import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
 
 /**
- * <p>Implements pluggable Datatypes.</p>
+ * <p>Implements pluggable Parsers.</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

@@ -49,11 +50,11 @@
     private static boolean debug = false;
 
     /**
      * Cache for properties in java.home/lib/jaxp.properties
      */
-    static Properties cacheProps = new Properties();
+    private static final Properties cacheProps = new Properties();
 
     /**
      * Flag indicating if properties from java.home/lib/jaxp.properties
      * have been cached.
      */

@@ -61,11 +62,11 @@
 
     /**
      * Security support class use to check access control before
      * getting certain system resources.
      */
-    static SecuritySupport ss = new SecuritySupport();
+    private static final SecuritySupport ss = new SecuritySupport();
 
     // Define system property "jaxp.debug" to get output
     static {
         // Use try/catch block to support applets, which throws
         // SecurityException out of this code.

@@ -94,11 +95,11 @@
      * 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,
+    static private Class<?> getProviderClass(String className, ClassLoader cl,
             boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
     {
         try {
             if (cl == null) {
                 if (useBSClsLoader) {

@@ -107,16 +108,16 @@
                     cl = ss.getContextClassLoader();
                     if (cl == null) {
                         throw new ClassNotFoundException();
                     }
                     else {
-                        return cl.loadClass(className);
+                        return Class.forName(className, true, cl);
                     }
                 }
             }
             else {
-                return cl.loadClass(className);
+                return Class.forName(className, true, cl);
             }
         }
         catch (ClassNotFoundException e1) {
             if (doFallback) {
                 // Use current class loader - should always be bootstrap CL

@@ -140,11 +141,11 @@
      *
      * @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);
     }
 
     /**

@@ -162,55 +163,55 @@
      *
      * @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);
+            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);
             }
             return instance;
         }
         catch (ClassNotFoundException x) {
-            throw new ConfigurationError(
-                "Provider " + className + " not found", x);
+            throw new FactoryConfigurationError(x,
+                "Provider " + className + " not found");
         }
         catch (Exception x) {
-            throw new ConfigurationError(
-                "Provider " + className + " could not be instantiated: " + x,
-                x);
+            throw new FactoryConfigurationError(x,
+                "Provider " + className + " could not be instantiated: " + x);
         }
     }
 
     /**
      * 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 type                  Base class / Service interface  of the
+     *                              factory to find.
      * @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 <T> T find(Class<T> type, String fallbackClassName)
+        throws FactoryConfigurationError
     {
+        final String factoryId = type.getName();
         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);
+                return type.cast(newInstance(systemProp, null, true));
             }
         }
         catch (SecurityException se) {
             if (debug) se.printStackTrace();
         }

@@ -234,123 +235,70 @@
             }
             factoryClassName = cacheProps.getProperty(factoryId);
 
             if (factoryClassName != null) {
                 dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
-                return newInstance(factoryClassName, null, true);
+                return type.cast(newInstance(factoryClassName, null, true));
             }
         }
         catch (Exception ex) {
             if (debug) ex.printStackTrace();
         }
 
         // Try Jar Service Provider Mechanism
-        Object provider = findJarServiceProvider(factoryId);
+        T provider = findServiceProvider(type, fallbackClassName);
         if (provider != null) {
             return provider;
         }
         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);
+        return type.cast(newInstance(fallbackClassName, null, true));
     }
 
     /*
-     * Try to find provider using Jar Service Provider Mechanism
+     * Try to find provider using the ServiceLoader API
      *
      * @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;
-        try {
-            rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
-        }
-        catch (java.io.UnsupportedEncodingException e) {
-            rd = new BufferedReader(new InputStreamReader(is));
-        }
-
-        String factoryClassName = null;
+    private static <T> T findServiceProvider(Class<T> type, final String defaultImplementationClassName) {
         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;
+            final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
+            return AccessController.doPrivileged(new PrivilegedAction<T>() {
+                public T run() {
+                    T defaultProvider = null;
+                    for (T provider : serviceLoader) {
+                         if (provider.getClass().getName().equals(defaultImplementationClassName)) {
+                            // This is the default implementation - we will return
+                            // the first default provider encountered if no third
+                            // party provider was found.
+                            if (defaultProvider == null) {
+                                defaultProvider = provider;
         }
-
-        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);
+                            continue;
         }
-
-        // 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;
+                        // We have found a non default provider: return it.
+                        return provider;
         }
-
-        Exception getException() {
-            return exception;
+                    return defaultProvider;
         }
-        /**
-        * use the exception chaining mechanism of JDK1.4
-        */
-        @Override
-        public Throwable getCause() {
-            return exception;
+            });
+        } catch(ServiceConfigurationError e) {
+            // It is not possible to wrap an error directly in
+            // FactoryConfigurationError - so we need to wrap the
+            // ServiceConfigurationError in a RuntimeException.
+            // The alternative would be to modify the logic in
+            // FactoryConfigurationError to allow setting a
+            // Throwable as the cause, but that could cause
+            // compatibility issues down the road.
+            final RuntimeException x = new RuntimeException(
+                    "Provider for " + type + " cannot be created", e);
+            final FactoryConfigurationError error =
+                    new FactoryConfigurationError(x, x.getMessage());
+            throw error;
         }
     }
 
 }