--- old/src/javax/xml/stream/FactoryFinder.java 2013-01-09 17:34:34.000000000 +0100 +++ new/src/javax/xml/stream/FactoryFinder.java 2013-01-09 17:34:34.000000000 +0100 @@ -25,14 +25,13 @@ package javax.xml.stream; -import java.io.InputStream; -import java.io.IOException; import java.io.File; -import java.io.FileInputStream; - +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Iterator; import java.util.Properties; -import java.io.BufferedReader; -import java.io.InputStreamReader; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** *

Implements pluggable Datatypes.

@@ -52,19 +51,19 @@ /** * Cache for properties in java.home/lib/jaxp.properties */ - static Properties cacheProps = new Properties(); + final private static Properties cacheProps = new Properties(); /** * Flag indicating if properties from java.home/lib/jaxp.properties * have been cached. */ - static volatile boolean firstTime = true; + private static volatile boolean firstTime = true; /** * Security support class use to check access control before * getting certain system resources. */ - static SecuritySupport ss = new SecuritySupport(); + final private static SecuritySupport ss = new SecuritySupport(); // Define system property "jaxp.debug" to get output static { @@ -105,17 +104,17 @@ 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; @@ -127,6 +126,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 * @@ -136,24 +138,29 @@ * @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 { + assert type != null; try { - Class providerClass = getProviderClass(className, cl, doFallback); + Class providerClass = getProviderClass(className, cl, doFallback); + 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( + throw new FactoryConfigurationError( "Provider " + className + " not found", x); } catch (Exception x) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider " + className + " could not be instantiated: " + x, x); } @@ -164,17 +171,18 @@ * * @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 { - return find(factoryId, null, fallbackClassName); + return find(type, type.getName(), null, fallbackClassName); } /** @@ -182,6 +190,9 @@ * entry point. * @return Class object of factory, never null * + * @param type Base class / Service interface of the + * factory to find. + * * @param factoryId Name of the factory to find, same as * a property name * @@ -193,8 +204,8 @@ * * Package private so this code can be shared. */ - static Object find(String factoryId, ClassLoader cl, String fallbackClassName) - throws ConfigurationError + static T find(Class type, String factoryId, ClassLoader cl, String fallbackClassName) + throws FactoryConfigurationError { dPrint("find factoryId =" + factoryId); @@ -203,7 +214,9 @@ String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { dPrint("found system property, value=" + systemProp); - return newInstance(systemProp, null, true); + // There's a bug here - because 'cl' is ignored. + // This will be handled separately. + return newInstance(type, systemProp, null, true); } } catch (SecurityException se) { @@ -214,7 +227,7 @@ // $java.home/lib/jaxp.properties if former not present String configFile = null; try { - String factoryClassName = null; + String factoryClassName; if (firstTime) { synchronized (cacheProps) { if (firstTime) { @@ -242,117 +255,70 @@ if (factoryClassName != null) { dPrint("found in " + configFile + " value=" + factoryClassName); - return newInstance(factoryClassName, null, true); + // There's a bug here - because 'cl' is ignored. + // This will be handled separately. + return newInstance(type, factoryClassName, null, true); } } catch (Exception ex) { if (debug) ex.printStackTrace(); } - // Try Jar Service Provider Mechanism - Object provider = findJarServiceProvider(factoryId); - if (provider != null) { - return provider; + if (type.getName().equals(factoryId)) { + // Try Jar Service Provider Mechanism + final T provider = findServiceProvider(type); + if (provider != null) { + return provider; + } + } else { + // We're in the case where a 'custom' factoryId was provided, + // and in every case where that happens, we expect that + // fallbackClassName will be null. + assert fallbackClassName == null; } + if (fallbackClassName == null) { - throw new ConfigurationError( + throw new FactoryConfigurationError( "Provider for " + factoryId + " cannot be found", null); } dPrint("loaded from fallback value: " + fallbackClassName); - return newInstance(fallbackClassName, cl, true); + return newInstance(type, fallbackClassName, cl, 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(); - 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); - } - } else { - // No Context ClassLoader, try the current ClassLoader - cl = FactoryFinder.class.getClassLoader(); - is = ss.getResourceAsStream(cl, serviceId); - } - - 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); - } - - // 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; } }