--- old/src/javax/xml/parsers/FactoryFinder.java 2012-12-17 15:40:00.000000000 +0100 +++ new/src/javax/xml/parsers/FactoryFinder.java 2012-12-17 15:40:00.000000000 +0100 @@ -25,15 +25,16 @@ 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; /** - *

Implements pluggable Datatypes.

+ *

Implements pluggable Parsers.

* *

This class is duplicated for each JAXP subpackage so keep it in * sync. It is package private for secure class loading.

@@ -51,7 +52,7 @@ /** * 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 @@ -63,7 +64,7 @@ * 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 { @@ -96,7 +97,7 @@ * * 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 { @@ -109,18 +110,18 @@ throw new ClassNotFoundException(); } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } } else { - return cl.loadClass(className); + return Class.forName(className, false, cl); } } catch (ClassNotFoundException e1) { if (doFallback) { // Use current class loader - should always be bootstrap CL - return Class.forName(className, true, FactoryFinder.class.getClassLoader()); + return Class.forName(className, false, FactoryFinder.class.getClassLoader()); } else { throw e1; @@ -132,6 +133,9 @@ * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -141,16 +145,20 @@ * @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 + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback) + throws FactoryConfigurationError { - return newInstance(className, cl, doFallback, false); + return newInstance(type, className, cl, doFallback, false); } /** * Create an instance of a class. Delegates to method * getProviderClass() in order to load the class. * + * @param type Base class / Service interface of the factory to + * instantiate. + * * @param className Name of the concrete class corresponding to the * service provider * @@ -163,26 +171,30 @@ * @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 + static T newInstance(Class type, String className, ClassLoader cl, + boolean doFallback, boolean useBSClsLoader) + throws FactoryConfigurationError { + assert type != null; try { - Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); + if (!type.isAssignableFrom(providerClass)) { + throw new ClassCastException(className + " cannot be cast to " + type.getName()); + } Object instance = providerClass.newInstance(); if (debug) { // Extra check to avoid computing cl strings dPrint("created new instance of " + providerClass + " using ClassLoader: " + cl); } - return instance; + return type.cast(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); } } @@ -191,16 +203,17 @@ * 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 find(Class type, String fallbackClassName) + throws FactoryConfigurationError { + final String factoryId = type.getName(); dPrint("find factoryId =" + factoryId); // Use the system property first @@ -208,7 +221,7 @@ String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true); + return newInstance(type, systemProp, null, true); } } catch (SecurityException se) { @@ -217,7 +230,7 @@ // try to read from $java.home/lib/jaxp.properties try { - String factoryClassName = null; + String factoryClassName; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -236,7 +249,7 @@ if (factoryClassName != null) { dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); - return newInstance(factoryClassName, null, true); + return newInstance(type, factoryClassName, null, true); } } catch (Exception ex) { @@ -244,112 +257,52 @@ } // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); + T provider = findServiceProvider(type); 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 newInstance(type, fallbackClassName, null, true); } /* - * Try to find provider using Jar Service Provider Mechanism + * Try to find provider using the ServiceLoader API + * + * @param type Base class / Service interface of the factory to find. * * @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 findServiceProvider(final Class type) { 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); - } - - // 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; + return AccessController.doPrivileged(new PrivilegedAction() { + public T run() { + final ServiceLoader serviceLoader = ServiceLoader.load(type); + final Iterator iterator = serviceLoader.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } else { + return null; + } + } + }); + } 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; } }