src/javax/xml/xpath/XPathFactoryFinder.java

Print this page

        

@@ -23,51 +23,48 @@
  * questions.
  */
 
 package javax.xml.xpath;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
-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.Method;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Properties;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
 
 /**
  * Implementation of {@link XPathFactory#newInstance(String)}.
  *
  * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
  * @version $Revision: 1.7 $, $Date: 2010-11-01 04:36:14 $
  * @since 1.5
  */
 class XPathFactoryFinder  {
 
-    private static SecuritySupport ss = new SecuritySupport() ;
-    /** debug support code. */
+    private static final String DEFAULT_IMPL_NAME = "com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl";
+    private static SecuritySupport ss = new SecuritySupport();
+    /**
+     * debug support code.
+     */
     private static boolean debug = false;
+
     static {
         // Use try/catch block to support applets
         try {
             debug = ss.getSystemProperty("jaxp.debug") != null;
         } catch (Exception _) {
             debug = false;
         }
     }
-
     /**
      * <p>Cache properties for performance.</p>
      */
         private static Properties cacheProps = new Properties();
-
         /**
          * <p>First time requires initialization overhead.</p>
          */
         private volatile static boolean firstTime = true;
 

@@ -79,66 +76,65 @@
     private static void debugPrintln(String msg) {
         if (debug) {
             System.err.println("JAXP: " + msg);
         }
     }
-
     /**
-     * <p><code>ClassLoader</code> to use to find <code>XPathFactory</code>.</p>
+     * <p><code>ClassLoader</code> to use to find
+     * <code>XPathFactory</code>.</p>
      */
     private final ClassLoader classLoader;
 
     /**
-     * <p>Constructor that specifies <code>ClassLoader</code> to use
-     * to find <code>XPathFactory</code>.</p>
-     *
-     * @param loader
-     *      to be used to load resource, {@link XPathFactory}, and
-     *      {@link SchemaFactoryLoader} implementations during
-     *      the resolution process.
-     *      If this parameter is null, the default system class loader
-     *      will be used.
+     * <p>Constructor that specifies
+     * <code>ClassLoader</code> to use to find
+     * <code>XPathFactory</code>.</p>
+     *
+     * @param loader to be used to load resource, {@link XPathFactory}, and
+     * {@link SchemaFactoryLoader} implementations during the resolution
+     * process. If this parameter is null, the default system class loader will
+     * be used.
      */
     public XPathFactoryFinder(ClassLoader loader) {
         this.classLoader = loader;
-        if( debug ) {
+        if (debug) {
             debugDisplayClassLoader();
         }
     }
 
     private void debugDisplayClassLoader() {
         try {
-            if( classLoader == ss.getContextClassLoader() ) {
-                debugPrintln("using thread context class loader ("+classLoader+") for search");
+            if (classLoader == ss.getContextClassLoader()) {
+                debugPrintln("using thread context class loader (" + classLoader + ") for search");
                 return;
             }
-        } catch( Throwable _ ) {
+        } catch (Throwable _) {
             ; // getContextClassLoader() undefined in JDK1.1
         }
 
-        if( classLoader==ClassLoader.getSystemClassLoader() ) {
-            debugPrintln("using system class loader ("+classLoader+") for search");
+        if (classLoader == ClassLoader.getSystemClassLoader()) {
+            debugPrintln("using system class loader (" + classLoader + ") for search");
             return;
         }
 
-        debugPrintln("using class loader ("+classLoader+") for search");
+        debugPrintln("using class loader (" + classLoader + ") for search");
     }
 
     /**
-     * <p>Creates a new {@link XPathFactory} object for the specified
-     * schema language.</p>
+     * <p>Creates a new {@link XPathFactory} object for the specified schema
+     * language.</p>
      *
-     * @param uri
-     *       Identifies the underlying object model.
+     * @param uri Identifies the underlying object model.
      *
      * @return <code>null</code> if the callee fails to create one.
      *
-     * @throws NullPointerException
-     *      If the parameter is null.
+     * @throws NullPointerException If the parameter is null.
      */
-    public XPathFactory newFactory(String uri) {
-        if(uri==null)        throw new NullPointerException();
+    public XPathFactory newFactory(String uri) throws XPathFactoryConfigurationException {
+        if (uri == null) {
+            throw new NullPointerException();
+        }
         XPathFactory f = _newFactory(uri);
         if (f != null) {
             debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri);
         } else {
             debugPrintln("unable to find a factory for " + uri);

@@ -151,46 +147,49 @@
      *
      * @param uri identifies the object model.
      *
      * @return {@link XPathFactory} for the given object model.
      */
-    private XPathFactory _newFactory(String uri) {
+    private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException {
         XPathFactory xpathFactory;
 
         String propertyName = SERVICE_CLASS.getName() + ":" + uri;
 
         // system property look up
         try {
-            debugPrintln("Looking up system property '"+propertyName+"'" );
+            debugPrintln("Looking up system property '" + propertyName + "'");
             String r = ss.getSystemProperty(propertyName);
-            if(r!=null) {
-                debugPrintln("The value is '"+r+"'");
+            if (r != null) {
+                debugPrintln("The value is '" + r + "'");
                 xpathFactory = createInstance(r, true);
-                if(xpathFactory != null)    return xpathFactory;
-            } else
+                if (xpathFactory != null) {
+                    return xpathFactory;
+                }
+            } else {
                 debugPrintln("The property is undefined.");
-        } catch( Throwable t ) {
-            if( debug ) {
-                debugPrintln("failed to look up system property '"+propertyName+"'" );
+            }
+        } catch (Throwable t) {
+            if (debug) {
+                debugPrintln("failed to look up system property '" + propertyName + "'");
                 t.printStackTrace();
             }
         }
 
-        String javah = ss.getSystemProperty( "java.home" );
-        String configFile = javah + File.separator +
-        "lib" + File.separator + "jaxp.properties";
+        String javah = ss.getSystemProperty("java.home");
+        String configFile = javah + File.separator
+                + "lib" + File.separator + "jaxp.properties";
 
-        String factoryClassName = null ;
+        String factoryClassName = null;
 
         // try to read from $java.home/lib/jaxp.properties
         try {
-            if(firstTime){
-                synchronized(cacheProps){
-                    if(firstTime){
-                        File f=new File( configFile );
+            if (firstTime) {
+                synchronized (cacheProps) {
+                    if (firstTime) {
+                        File f = new File(configFile);
                         firstTime = false;
-                        if(ss.doesFileExist(f)){
+                        if (ss.doesFileExist(f)) {
                             debugPrintln("Read properties file " + f);
                             cacheProps.load(ss.getFileInputStream(f));
                         }
                     }
                 }

@@ -198,50 +197,38 @@
             factoryClassName = cacheProps.getProperty(propertyName);
             debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
 
             if (factoryClassName != null) {
                 xpathFactory = createInstance(factoryClassName, true);
-                if(xpathFactory != null){
+                if (xpathFactory != null) {
                     return xpathFactory;
                 }
             }
         } catch (Exception ex) {
             if (debug) {
                 ex.printStackTrace();
             }
         }
 
-        // try META-INF/services files
-        Iterator sitr = createServiceFileIterator();
-        while(sitr.hasNext()) {
-            URL resource = (URL)sitr.next();
-            debugPrintln("looking into " + resource);
-            try {
-                xpathFactory = loadFromService(uri, resource.toExternalForm(),
-                                                ss.getURLInputStream(resource));
+        // try finding a service provider
+        xpathFactory = findServiceProvider(uri, DEFAULT_IMPL_NAME);
                 if (xpathFactory != null) {
                     return xpathFactory;
                 }
-            } catch(IOException e) {
-                if( debug ) {
-                    debugPrintln("failed to read "+resource);
-                    e.printStackTrace();
-                }
-            }
-        }
 
         // platform default
-        if(uri.equals(XPathFactory.DEFAULT_OBJECT_MODEL_URI)) {
+        if (uri.equals(XPathFactory.DEFAULT_OBJECT_MODEL_URI)) {
             debugPrintln("attempting to use the platform default W3C DOM XPath lib");
-            return createInstance("com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl", true);
+            return createInstance(DEFAULT_IMPL_NAME, true);
         }
 
         debugPrintln("all things were tried, but none was found. bailing out.");
         return null;
     }
 
-    /** <p>Create class using appropriate ClassLoader.</p>
+    /**
+     * <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) {

@@ -253,30 +240,31 @@
                             clazz = classLoader.loadClass(className);
                     } 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.
+     * @param className fully qualified class name to be instanciated.
      *
-     * @return null
-     *      if it fails. Error messages will be printed by this method.
+     * @return null if it fails. Error messages will be printed by this method.
      */
-    XPathFactory createInstance( String className ) {
-        return createInstance( className, false );
+    XPathFactory createInstance(String className) {
+        return createInstance(className, false);
     }
-    XPathFactory createInstance( String className, boolean useServicesMechanism  ) {
+
+    XPathFactory createInstance(String className, boolean useServicesMechanism) {
         XPathFactory xPathFactory = null;
 
         debugPrintln("createInstance(" + className + ")");
 
         // get Class from className

@@ -315,214 +303,95 @@
                 return null;
         }
 
         return xPathFactory;
     }
+
     /**
-     * Try to construct using newXPathFactoryNoServiceLoader
-     *   method if available.
+     * Try to construct using newXPathFactoryNoServiceLoader method if
+     * available.
      */
     private static Object newInstanceNoServiceLoader(
-         Class<?> providerClass
-    ) {
+            Class<?> providerClass) {
         // Retain maximum compatibility if no security manager.
         if (System.getSecurityManager() == null) {
             return null;
         }
         try {
             Method creationMethod =
                 providerClass.getDeclaredMethod(
-                    "newXPathFactoryNoServiceLoader"
-                );
+                    "newXPathFactoryNoServiceLoader");
                 return creationMethod.invoke(null, null);
             } catch (NoSuchMethodException exc) {
                 return null;
             } catch (Exception exc) {
                 return null;
         }
     }
 
-    /**
-     * <p>Look up a value in a property file.</p>
+    /*
+     * Try to find a provider using Service Loader
      *
-     * <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p>
-     *
-     * @param objectModel URI of object model to support.
-     * @param inputName Name of <code>InputStream</code>.
-     * @param in <code>InputStream</code> of properties.
-     *
-     * @return <code>XPathFactory</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>.
+     * @return instance of provider class if found or null
      */
-    private XPathFactory loadFromService(
-            String objectModel,
-            String inputName,
-            InputStream in)
-            throws IOException {
-
-            XPathFactory xPathFactory = null;
-            final Class[] stringClassArray = {"".getClass()};
-            final Object[] objectModelObjectArray = {objectModel};
-            final String isObjectModelSupportedMethod = "isObjectModelSupported";
-
-            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
+    private XPathFactory findServiceProvider(final String objectModel, final String fallbackClassName)
+            throws XPathFactoryConfigurationException {
                     try {
-                            xPathFactory = (XPathFactory) clazz.newInstance();
-                    } catch (ClassCastException classCastExcpetion) {
-                            xPathFactory = null;
-                            continue;
-                    } catch (InstantiationException instantiationException) {
-                            xPathFactory = null;
-                            continue;
-                    } catch (IllegalAccessException illegalAccessException) {
-                            xPathFactory = null;
-                            continue;
+            return (XPathFactory) AccessController.doPrivileged(new PrivilegedAction() {
+                public XPathFactory run() {
+                    XPathFactory defaultProvider = null;
+                    for (XPathFactory factory : ServiceLoader.load(XPathFactory.class)) {
+                        if (factory.getClass().getName().equals(fallbackClassName)) {
+                            defaultProvider = factory;
+                        } else {
+                            if (isObjectModelSupported(factory, objectModel)) {
+                                return factory;
                     }
-
-                    // does this Class support desired object model?
-                    try {
-                            Method isObjectModelSupported = clazz.getMethod(isObjectModelSupportedMethod, stringClassArray);
-                            Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray);
-                            if (supported.booleanValue()) {
-                                    break;
                             }
-
-                    } catch (NoSuchMethodException noSuchMethodException) {
-
-                    } catch (IllegalAccessException illegalAccessException) {
-
-                    } catch (InvocationTargetException invocationTargetException) {
-
                     }
-                    xPathFactory = null;
+                    if (defaultProvider != null) {
+                        return defaultProvider;
             }
-
-            // clean up
-            configFile.close();
-
-            // return new instance of XPathFactory or null
-            return xPathFactory;
+                    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();
+            });
+        } catch (ServiceConfigurationError e) {
+            throw new XPathFactoryConfigurationException((Exception) e.getCause());
         }
-
-        protected abstract Object value();
     }
 
     /**
-     * Looks up a value in a property file
-     * while producing all sorts of debug messages.
+     * <p>Test if the specified schemas are supported by the provider.</p>
      *
-     * @return null
-     *      if there was an error.
-     */
-    private XPathFactory 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, true);
-        } else {
-            debugPrintln(keyName+" is not in the property file");
-            return null;
-        }
-    }
-
-    /**
-     * Returns an {@link Iterator} that enumerates all
-     * the META-INF/services files that we care.
+     * @param schemaFactory a Schema factory provider.
+     * @param schemaLanguage Schema Language to support.
+     *
+     * @return <code>true</code> if the Schema Language is supported by the
+     * provider.
      */
-    private Iterator createServiceFileIterator() {
-        if (classLoader == null) {
-            return new SingleIterator() {
-                protected Object value() {
-                    ClassLoader classLoader = XPathFactoryFinder.class.getClassLoader();
-                    return ss.getResourceAsURL(classLoader, SERVICE_ID);
-                    //return (ClassLoader.getSystemResource( SERVICE_ID ));
-                }
-            };
-        } else {
+    private static boolean isObjectModelSupported(XPathFactory xPathFactory, String objectModel) {
+        final Class[] stringClassArray = {"".getClass()};
+        final Object[] objectModelObjectArray = {objectModel};
+        final String isObjectModelSupportedMethod = "isObjectModelSupported";
+        // does this Class support desired Schema?
             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();
+            Method isObjectModelSupported = xPathFactory.getClass().getMethod(isObjectModelSupportedMethod, stringClassArray);
+            Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray);
+            if (supported.booleanValue()) {
+                return true;
                     }
-
-                    public boolean hasNext() {
-                        return e.hasMoreElements();
+        } catch (NoSuchMethodException noSuchMethodException) {
+        } catch (IllegalAccessException illegalAccessException) {
+        } catch (InvocationTargetException invocationTargetException) {
                     }
 
-                    public Object next() {
-                        return e.nextElement();
-                    }
-                };
-            } catch (IOException e) {
-                debugPrintln("failed to enumerate resources "+SERVICE_ID);
-                if(debug)   e.printStackTrace();
-                return new ArrayList().iterator();  // empty iterator
+        return false;
             }
-        }
-    }
-
     private static final Class SERVICE_CLASS = XPathFactory.class;
-    private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
 
-
-
-    private static String which( Class clazz ) {
-        return which( clazz.getName(), clazz.getClassLoader() );
+    private static String which(Class clazz) {
+        return which(clazz.getName(), clazz.getClassLoader());
     }
 
     /**
      * <p>Search the specified classloader for the given classname.</p>
      *

@@ -533,11 +402,13 @@
      */
     private static String which(String classname, ClassLoader loader) {
 
         String classnameAsResource = classname.replace('.', '/') + ".class";
 
-        if( loader==null )  loader = ClassLoader.getSystemClassLoader();
+        if (loader == null) {
+            loader = ClassLoader.getSystemClassLoader();
+        }
 
         //URL it = loader.getResource(classnameAsResource);
         URL it = ss.getResourceAsURL(loader, classnameAsResource);
         if (it != null) {
             return it.toString();