--- old/src/javax/xml/validation/SchemaFactory.java 2013-01-09 12:44:07.000000000 +0100 +++ new/src/javax/xml/validation/SchemaFactory.java 2013-01-09 12:44:06.000000000 +0100 @@ -27,15 +27,14 @@ import java.io.File; import java.net.URL; - import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; - import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; /** * Factory that creates {@link Schema} objects. Entry-point to @@ -79,7 +78,7 @@ * and has a significant effect on the parsing process, it is impossible * to define the DTD validation as a process independent from parsing. * For this reason, this specification does not define the semantics for - * the XML DTD. This doesn't prohibit implentors from implementing it + * the XML DTD. This doesn't prohibit implementors from implementing it * in a way they see fit, but users are warned that any DTD * validation implemented on this interface necessarily deviate from * the XML DTD semantics as defined in the XML 1.0. @@ -147,14 +146,17 @@ * is looked for. If present, the value is processed just like above. * *
  • - *

    The class loader is asked for service provider provider-configuration files matching - * javax.xml.validation.SchemaFactory in the resource directory META-INF/services. - * See the JAR File Specification for file format and parsing rules. - * Each potential service provider is required to implement the method:

    - *
    -     *        {@link #isSchemaLanguageSupported(String schemaLanguage)}
    -     *     
    - * The first service provider found in class loader order that supports the specified schema language is returned. + * Use the service-provider loading facilities, defined by the + * {@link java.util.ServiceLoader} class, to attempt to locate and load an + * implementation of the service.
    + * Each potential service provider is required to implement the method + * {@link #isSchemaLanguageSupported(String schemaLanguage)}. + *
    + * The first service provider found that supports the specified schema + * language is returned. + *
    + * In case of {@link java.util.ServiceConfigurationError} a + * {@link SchemaFactoryConfigurationError} will be thrown. *
  • *
  • * Platform default SchemaFactory is located @@ -186,10 +188,12 @@ * If no implementation of the schema language is available. * @throws NullPointerException * If the schemaLanguage parameter is null. + * @throws SchemaFactoryConfigurationError + * If a configuration error is encountered. * * @see #newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) */ - public static final SchemaFactory newInstance(String schemaLanguage) { + public static SchemaFactory newInstance(String schemaLanguage) { ClassLoader cl; cl = ss.getContextClassLoader(); @@ -275,19 +279,19 @@ } - /** - *

    Is specified schema supported by this SchemaFactory?

    - * - * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand. + /** + *

    Is specified schema supported by this SchemaFactory?

    + * + * @param schemaLanguage Specifies the schema language which the returned SchemaFactory will understand. * schemaLanguage must specify a valid schema language. - * - * @return true if SchemaFactory supports schemaLanguage, else false. - * - * @throws NullPointerException If schemaLanguage is null. - * @throws IllegalArgumentException If schemaLanguage.length() == 0 - * or schemaLanguage does not specify a valid schema language. - */ - public abstract boolean isSchemaLanguageSupported(String schemaLanguage); + * + * @return true if SchemaFactory supports schemaLanguage, else false. + * + * @throws NullPointerException If schemaLanguage is null. + * @throws IllegalArgumentException If schemaLanguage.length() == 0 + * or schemaLanguage does not specify a valid schema language. + */ + public abstract boolean isSchemaLanguageSupported(String schemaLanguage); /** * Look up the value of a feature flag. --- 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() ); } --- /dev/null 2013-01-09 12:44:11.000000000 +0100 +++ new/src/javax/xml/validation/SchemaFactoryConfigurationError.java 2013-01-09 12:44:10.000000000 +0100 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.xml.validation; + +/** + * Thrown when a problem with configuration with the Schema Factories + * exists. This error will typically be thrown when the class of a + * schema factory specified in the system properties cannot be found + * or instantiated. + * @since 1.8 + */ +public class SchemaFactoryConfigurationError extends Error { + + static final long serialVersionUID = 3531438703147750126L; + + /** + * Create a new SchemaFactoryConfigurationError with no + * detail message. + */ + public SchemaFactoryConfigurationError() { + } + + + /** + * Create a new SchemaFactoryConfigurationError with + * the String specified as an error message. + * + * @param message The error message for the exception. + */ + public SchemaFactoryConfigurationError(String message) { + super(message); + } + + /** + * Create a new SchemaFactoryConfigurationError with the + * given Throwable base cause. + * + * @param cause The exception or error to be encapsulated in a + * SchemaFactoryConfigurationError. + */ + public SchemaFactoryConfigurationError(Throwable cause) { + super(cause); + } + + /** + * Create a new SchemaFactoryConfigurationError with the + * given Throwable base cause and detail message. + * + * @param cause The exception or error to be encapsulated in a + * SchemaFactoryConfigurationError. + * @param message The detail message. + */ + public SchemaFactoryConfigurationError(String message, Throwable cause) { + super(message, cause); + } + +}