--- old/src/javax/xml/validation/SchemaFactoryFinder.java 2013-01-09 12:44:09.000000000 +0100 +++ new/src/javax/xml/validation/SchemaFactoryFinder.java 2013-01-09 12:44:08.000000000 +0100 @@ -25,19 +25,16 @@ package javax.xml.validation; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Properties; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; /** * Implementation of {@link SchemaFactory#newInstance(String)}. @@ -53,16 +50,15 @@ /** *

Take care of restrictions imposed by java security model

*/ - private static SecuritySupport ss = new SecuritySupport(); + private static final SecuritySupport ss = new SecuritySupport(); /** *

Cache properties for performance.

*/ - private static Properties cacheProps = new Properties(); - - /** - *

First time requires initialization overhead.

- */ - private static volatile boolean firstTime = true; + private static final Properties cacheProps = new Properties(); + /** + *

First time requires initialization overhead.

+ */ + private static volatile boolean firstTime = true; static { // Use try/catch block to support applets @@ -114,7 +110,7 @@ return; } } catch( Throwable _ ) { - ; // getContextClassLoader() undefined in JDK1.1 + // getContextClassLoader() undefined in JDK1.1 } if( classLoader==ClassLoader.getSystemClassLoader() ) { @@ -137,9 +133,13 @@ * * @throws NullPointerException * If the schemaLanguage parameter is null. + * @throws SchemaFactoryConfigurationError + * If a configuration error is encountered. */ public SchemaFactory newFactory(String schemaLanguage) { - if(schemaLanguage==null) throw new NullPointerException(); + if(schemaLanguage==null) { + throw new NullPointerException(); + } SchemaFactory f = _newFactory(schemaLanguage); if (f != null) { debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage); @@ -182,7 +182,7 @@ String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties"; - String factoryClassName = null ; + String factoryClassName; // try to read from $java.home/lib/jaxp.properties try { @@ -213,43 +213,11 @@ } } - /** - // try to read from $java.home/lib/jaxp.properties - try { - String javah = ss.getSystemProperty( "java.home" ); - String configFile = javah + File.separator + - "lib" + File.separator + "jaxp.properties"; - File f = new File( configFile ); - if( ss.doesFileExist(f)) { - sf = loadFromProperty( - propertyName,f.getAbsolutePath(), new FileInputStream(f)); - if(sf!=null) return sf; - } else { - debugPrintln("Tried to read "+ f.getAbsolutePath()+", but it doesn't exist."); - } - } catch(Throwable e) { - if( debug ) { - debugPrintln("failed to read $java.home/lib/jaxp.properties"); - e.printStackTrace(); - } - } - */ - - // try META-INF/services files - Iterator sitr = createServiceFileIterator(); - while(sitr.hasNext()) { - URL resource = (URL)sitr.next(); - debugPrintln("looking into " + resource); - try { - sf = loadFromService(schemaLanguage,resource.toExternalForm(), - ss.getURLInputStream(resource)); - if(sf!=null) return sf; - } catch(IOException e) { - if( debug ) { - debugPrintln("failed to read "+resource); - e.printStackTrace(); - } - } + // Try with ServiceLoader + final SchemaFactory factoryImpl = findServiceProvider(schemaLanguage); + assert factoryImpl == null || factoryImpl.isSchemaLanguageSupported(schemaLanguage); + if (factoryImpl != null) { + return factoryImpl; } // platform default @@ -267,29 +235,31 @@ * @param className Name of class to create. * @return Created class or null. */ - private Class createClass(String className) { - Class clazz; + private Class createClass(String className) { + Class clazz; - // use approprite ClassLoader - try { - if (classLoader != null) { - clazz = classLoader.loadClass(className); - } else { - clazz = Class.forName(className); - } - } catch (Throwable t) { - if(debug) t.printStackTrace(); - return null; + // use approprite ClassLoader + try { + if (classLoader != null) { + clazz = Class.forName(className, false, classLoader); + } else { + clazz = Class.forName(className); + } + } catch (Throwable t) { + if (debug) { + t.printStackTrace(); } + return null; + } - return clazz; + return clazz; } /** *

Creates an instance of the specified and returns it.

* * @param className - * fully qualified class name to be instanciated. + * fully qualified class name to be instantiated. * * @return null * if it fails. Error messages will be printed by this method. @@ -304,7 +274,7 @@ debugPrintln("createInstance(" + className + ")"); // get Class from className - Class clazz = createClass(className); + Class clazz = createClass(className); if (clazz == null) { debugPrintln("failed to getClass(" + className + ")"); return null; @@ -313,9 +283,13 @@ // instantiate Class as a SchemaFactory try { - if (!useServicesMechanism) { - schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz); - } + if (!SchemaFactory.class.isAssignableFrom(clazz)) { + throw new ClassCastException(clazz.getName() + + " cannot be cast to " + SchemaFactory.class); + } + if (!useServicesMechanism) { + schemaFactory = newInstanceNoServiceLoader(clazz); + } if (schemaFactory == null) { schemaFactory = (SchemaFactory) clazz.newInstance(); } @@ -341,11 +315,12 @@ return schemaFactory; } + /** - * Try to construct using newTransformerFactoryNoServiceLoader + * Try to construct using newXMLSchemaFactoryNoServiceLoader * method if available. */ - private static Object newInstanceNoServiceLoader( + private static SchemaFactory newInstanceNoServiceLoader( Class providerClass ) { // Retain maximum compatibility if no security manager. @@ -353,196 +328,87 @@ return null; } try { - Method creationMethod = + final Method creationMethod = providerClass.getDeclaredMethod( "newXMLSchemaFactoryNoServiceLoader" ); - return creationMethod.invoke(null, (Object[])null); - } catch (NoSuchMethodException exc) { - return null; - } catch (Exception exc) { - return null; - } - } - - /** Iterator that lazily computes one value and returns it. */ - private static abstract class SingleIterator implements Iterator { - private boolean seen = false; - - public final void remove() { throw new UnsupportedOperationException(); } - public final boolean hasNext() { return !seen; } - public final Object next() { - if(seen) throw new NoSuchElementException(); - seen = true; - return value(); - } + final int modifiers = creationMethod.getModifiers(); - protected abstract Object value(); - } - - /** - * Looks up a value in a property file - * while producing all sorts of debug messages. - * - * @return null - * if there was an error. - */ - private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in ) - throws IOException { - debugPrintln("Reading "+resourceName ); - - Properties props=new Properties(); - props.load(in); - in.close(); - String factoryClassName = props.getProperty(keyName); - if(factoryClassName != null){ - debugPrintln("found "+keyName+" = " + factoryClassName); - return createInstance(factoryClassName); - } else { - debugPrintln(keyName+" is not in the property file"); + // Do not call the method if it's not public static. + if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { + return null; + } + + // Only calls "newXMLSchemaFactoryNoServiceLoader" if it's + // declared to return an instance of SchemaFactory. + final Class returnType = creationMethod.getReturnType(); + if (SERVICE_CLASS.isAssignableFrom(returnType)) { + return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null)); + } else { + // Should not happen since + // XMLSchemaFactory.newXMLSchemaFactoryNoServiceLoader is + // declared to return XMLSchemaFactory. + throw new ClassCastException(returnType + + " cannot be cast to " + SERVICE_CLASS); + } + } catch(ClassCastException e) { + throw new SchemaFactoryConfigurationError(e.getMessage(), e); + } catch (NoSuchMethodException exc) { + return null; + } catch (Exception exc) { return null; } } - /** - *

Look up a value in a property file.

- * - *

Set debug to true to trace property evaluation.

- * - * @param schemaLanguage Schema Language to support. - * @param inputName Name of InputStream. - * @param in InputStream of properties. - * - * @return SchemaFactory as determined by keyName value or null if there was an error. - * - * @throws IOException If IO error reading from in. - */ - private SchemaFactory loadFromService( - String schemaLanguage, - String inputName, - InputStream in) - throws IOException { - - SchemaFactory schemaFactory = null; - final Class[] stringClassArray = {"".getClass()}; - final Object[] schemaLanguageObjectArray = {schemaLanguage}; - final String isSchemaLanguageSupportedMethod = "isSchemaLanguageSupported"; - - debugPrintln("Reading " + inputName); - - // read from InputStream until a match is found - BufferedReader configFile = new BufferedReader(new InputStreamReader(in)); - String line = null; - while ((line = configFile.readLine()) != null) { - // '#' is comment char - int comment = line.indexOf("#"); - switch (comment) { - case -1: break; // no comment - case 0: line = ""; break; // entire line is a comment - default: line = line.substring(0, comment); break; // trim comment - } - - // trim whitespace - line = line.trim(); - - // any content left on line? - if (line.length() == 0) { - continue; - } - - // line content is now the name of the class - Class clazz = createClass(line); - if (clazz == null) { - continue; - } - - // create an instance of the Class - try { - schemaFactory = (SchemaFactory) clazz.newInstance(); - } catch (ClassCastException classCastExcpetion) { - schemaFactory = null; - continue; - } catch (InstantiationException instantiationException) { - schemaFactory = null; - continue; - } catch (IllegalAccessException illegalAccessException) { - schemaFactory = null; - continue; - } - - // does this Class support desired Schema? - try { - Method isSchemaLanguageSupported = clazz.getMethod(isSchemaLanguageSupportedMethod, stringClassArray); - Boolean supported = (Boolean) isSchemaLanguageSupported.invoke(schemaFactory, schemaLanguageObjectArray); - if (supported.booleanValue()) { - break; - } - } catch (NoSuchMethodException noSuchMethodException) { - - } catch (IllegalAccessException illegalAccessException) { - - } catch (InvocationTargetException invocationTargetException) { - - } - schemaFactory = null; - } - - // clean up - configFile.close(); - - // return new instance of SchemaFactory or null - return schemaFactory; + // Call isSchemaLanguageSupported with initial context. + private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory, + final String schemaLanguage, + AccessControlContext acc) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return factory.isSchemaLanguageSupported(schemaLanguage); + } + }, acc); } /** - * Returns an {@link Iterator} that enumerates all - * the META-INF/services files that we care. - */ - private Iterator createServiceFileIterator() { - if (classLoader == null) { - return new SingleIterator() { - protected Object value() { - ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader(); - //return (ClassLoader.getSystemResource( SERVICE_ID )); - return ss.getResourceAsURL(classLoader, SERVICE_ID); - } - }; - } else { - try { - //final Enumeration e = classLoader.getResources(SERVICE_ID); - final Enumeration e = ss.getResources(classLoader, SERVICE_ID); - if(!e.hasMoreElements()) { - debugPrintln("no "+SERVICE_ID+" file was found"); - } - - // wrap it into an Iterator. - return new Iterator() { - public void remove() { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() { - return e.hasMoreElements(); - } - - public Object next() { - return e.nextElement(); + * Finds a service provider subclass of SchemaFactory that supports the + * given schema language using the ServiceLoader. + * + * @param schemaLanguage The schema language for which we seek a factory. + * @return A SchemaFactory supporting the specified schema language, or null + * if none is found. + * @throws SchemaFactoryConfigurationError if a configuration error is found. + */ + private SchemaFactory findServiceProvider(final String schemaLanguage) { + assert schemaLanguage != null; + // store current context. + final AccessControlContext acc = AccessController.getContext(); + try { + return AccessController.doPrivileged(new PrivilegedAction() { + public SchemaFactory run() { + final ServiceLoader loader = + ServiceLoader.load(SERVICE_CLASS); + for (SchemaFactory factory : loader) { + // restore initial context to call + // factory.isSchemaLanguageSupported + if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) { + return factory; + } } - }; - } catch (IOException e) { - debugPrintln("failed to enumerate resources "+SERVICE_ID); - if(debug) e.printStackTrace(); - return new ArrayList().iterator(); // empty iterator - } + return null; // no factory found. + } + }); + } catch (ServiceConfigurationError error) { + throw new SchemaFactoryConfigurationError( + "Provider for " + SERVICE_CLASS + " cannot be created", error); } } - private static final Class SERVICE_CLASS = SchemaFactory.class; - private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName(); - + private static final Class SERVICE_CLASS = SchemaFactory.class; - private static String which( Class clazz ) { + private static String which( Class clazz ) { return which( clazz.getName(), clazz.getClassLoader() ); }