src/javax/xml/validation/SchemaFactoryFinder.java

Print this page

        

@@ -23,23 +23,20 @@
  * questions.
  */
 
 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)}.
  *
  * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>

@@ -51,16 +48,15 @@
     /** debug support code. */
     private static boolean debug = false;
     /**
      *<p> Take care of restrictions imposed by java security model </p>
      */
-    private static SecuritySupport ss = new SecuritySupport();
+    private static final SecuritySupport ss = new SecuritySupport();
     /**
      * <p>Cache properties for performance.</p>
      */
-        private static Properties cacheProps = new Properties();
-
+    private static final Properties cacheProps = new Properties();
         /**
          * <p>First time requires initialization overhead.</p>
          */
         private static volatile boolean firstTime = true;
 

@@ -112,11 +108,11 @@
             if( classLoader == ss.getContextClassLoader() ) {
                 debugPrintln("using thread context class loader ("+classLoader+") for search");
                 return;
             }
         } catch( Throwable _ ) {
-            ; // getContextClassLoader() undefined in JDK1.1
+            // getContextClassLoader() undefined in JDK1.1
         }
 
         if( classLoader==ClassLoader.getSystemClassLoader() ) {
             debugPrintln("using system class loader ("+classLoader+") for search");
             return;

@@ -135,13 +131,17 @@
      *
      * @return <code>null</code> if the callee fails to create one.
      *
      * @throws NullPointerException
      *      If the <code>schemaLanguage</code> 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);
         } else {
             debugPrintln("unable to find a factory for " + schemaLanguage);

@@ -180,11 +180,11 @@
 
         String javah = ss.getSystemProperty( "java.home" );
         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 {
             if(firstTime){
                 synchronized(cacheProps){

@@ -211,47 +211,15 @@
             if (debug) {
                 ex.printStackTrace();
             }
         }
 
-        /**
-        // 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
         if(schemaLanguage.equals("http://www.w3.org/2001/XMLSchema")) {
             debugPrintln("attempting to use the platform default XML Schema validator");

@@ -265,33 +233,35 @@
     /** <p>Create class using appropriate ClassLoader.</p>
      *
      * @param className Name of class to create.
      * @return Created class or <code>null</code>.
      */
-    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);
+                clazz = Class.forName(className, false, classLoader);
                     } else {
                             clazz = Class.forName(className);
                     }
             } catch (Throwable t) {
-                if(debug)   t.printStackTrace();
+            if (debug) {
+                t.printStackTrace();
+            }
                     return null;
             }
 
             return clazz;
     }
 
     /**
      * <p>Creates an instance of the specified and returns it.</p>
      *
      * @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.
      */
     SchemaFactory createInstance( String className ) {

@@ -302,21 +272,25 @@
         SchemaFactory schemaFactory = null;
 
         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;
         }
         debugPrintln("loaded " + className + " from " + which(clazz));
 
         // instantiate Class as a SchemaFactory
         try {
+                if (!SchemaFactory.class.isAssignableFrom(clazz)) {
+                    throw new ClassCastException(clazz.getName()
+                                + " cannot be cast to " + SchemaFactory.class);
+                }
             if (!useServicesMechanism) {
-                schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz);
+                    schemaFactory = newInstanceNoServiceLoader(clazz);
             }
                 if (schemaFactory == null) {
                     schemaFactory = (SchemaFactory) clazz.newInstance();
                 }
         } catch (ClassCastException classCastException) {

@@ -339,212 +313,104 @@
                 return null;
         }
 
         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.
         if (System.getSecurityManager() == null) {
             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();
-        }
-
-        protected abstract Object value();
-    }
+            final int modifiers = creationMethod.getModifiers();
 
-    /**
-     * 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;
         }
-    }
 
-    /**
-     * <p>Look up a value in a property file.</p>
-     *
-     * <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p>
-     *
-     * @param schemaLanguage Schema Language to support.
-     * @param inputName Name of <code>InputStream</code>.
-     * @param in <code>InputStream</code> of properties.
-     *
-     * @return <code>SchemaFactory</code> as determined by <code>keyName</code> value or <code>null</code> if there was an error.
-     *
-     * @throws IOException If IO error reading from <code>in</code>.
-     */
-    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;
+            // 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);
                     }
-
-                    // 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;
+        } catch(ClassCastException e) {
+            throw new SchemaFactoryConfigurationError(e.getMessage(), e);
+        } catch (NoSuchMethodException exc) {
+            return null;
+        } catch (Exception exc) {
+            return null;
                     }
-
-                    // 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) {
 
+    // Call isSchemaLanguageSupported with initial context.
+    private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory,
+            final String schemaLanguage,
+            AccessControlContext acc) {
+        return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+            public Boolean run() {
+                return factory.isSchemaLanguageSupported(schemaLanguage);
                     }
-                    schemaFactory = null;
-            }
-
-            // clean up
-            configFile.close();
-
-            // return new instance of SchemaFactory or null
-            return schemaFactory;
+        }, acc);
     }
 
     /**
-     * Returns an {@link Iterator} that enumerates all
-     * the META-INF/services files that we care.
+     * 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 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 {
+    private SchemaFactory findServiceProvider(final String schemaLanguage) {
+        assert schemaLanguage != null;
+        // store current context. 
+        final AccessControlContext acc = AccessController.getContext();
             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();
+            return AccessController.doPrivileged(new PrivilegedAction<SchemaFactory>() {
+                public SchemaFactory run() {
+                    final ServiceLoader<SchemaFactory> loader =
+                            ServiceLoader.load(SERVICE_CLASS);
+                    for (SchemaFactory factory : loader) {
+                        // restore initial context to call 
+                        // factory.isSchemaLanguageSupported
+                        if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) {
+                            return factory;
                     }
-
-                    public boolean hasNext() {
-                        return e.hasMoreElements();
                     }
-
-                    public Object next() {
-                        return e.nextElement();
+                    return null; // no factory found.
                     }
-                };
-            } catch (IOException e) {
-                debugPrintln("failed to enumerate resources "+SERVICE_ID);
-                if(debug)   e.printStackTrace();
-                return new ArrayList().iterator();  // empty iterator
+            });
+        } 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<SchemaFactory> SERVICE_CLASS = SchemaFactory.class;
 
 
-    private static String which( Class clazz ) {
+    private static String which( Class<?> clazz ) {
         return which( clazz.getName(), clazz.getClassLoader() );
     }
 
     /**
      * <p>Search the specified classloader for the given classname.</p>