src/javax/xml/xpath/XPathFactoryFinder.java
Print this page
rev 406 : 8005954: JAXP Plugability Layer should use java.util.ServiceLoader
Summary: This fix replaces manual processing of files under META-INF/services in JAXP factories by calls to java.util.ServiceLoader.
Reviewed-by: alanb, joehw, mchung
*** 1,7 ****
/*
! * Copyright (c) 2004, 2005, 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
--- 1,7 ----
/*
! * Copyright (c) 2004, 2013, 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
*** 23,46 ****
* 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.net.URL;
! import java.util.ArrayList;
! import java.util.Enumeration;
! import java.util.Iterator;
! import java.util.NoSuchElementException;
import java.util.Properties;
/**
* Implementation of {@link XPathFactory#newInstance(String)}.
*
* @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
--- 23,42 ----
* questions.
*/
package javax.xml.xpath;
import java.io.File;
import java.lang.reflect.Method;
! import java.lang.reflect.Modifier;
import java.net.URL;
! 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 XPathFactory#newInstance(String)}.
*
* @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
*** 48,58 ****
* @since 1.5
*/
class XPathFactoryFinder {
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal";
! private static SecuritySupport ss = new SecuritySupport() ;
/** debug support code. */
private static boolean debug = false;
static {
// Use try/catch block to support applets
try {
--- 44,54 ----
* @since 1.5
*/
class XPathFactoryFinder {
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal";
! private static final SecuritySupport ss = new SecuritySupport() ;
/** debug support code. */
private static boolean debug = false;
static {
// Use try/catch block to support applets
try {
*** 63,73 ****
}
/**
* <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;
--- 59,69 ----
}
/**
* <p>Cache properties for performance.</p>
*/
! private static final Properties cacheProps = new Properties();
/**
* <p>First time requires initialization overhead.</p>
*/
private volatile static boolean firstTime = true;
*** 91,103 ****
/**
* <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;
--- 87,98 ----
/**
* <p>Constructor that specifies <code>ClassLoader</code> to use
* to find <code>XPathFactory</code>.</p>
*
* @param loader
! * to be used to load resource and {@link XPathFactory}
! * 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;
*** 111,121 ****
if( classLoader == ss.getContextClassLoader() ) {
debugPrintln("using thread context class loader ("+classLoader+") for search");
return;
}
} catch( Throwable unused ) {
! ; // getContextClassLoader() undefined in JDK1.1
}
if( classLoader==ClassLoader.getSystemClassLoader() ) {
debugPrintln("using system class loader ("+classLoader+") for search");
return;
--- 106,116 ----
if( classLoader == ss.getContextClassLoader() ) {
debugPrintln("using thread context class loader ("+classLoader+") for search");
return;
}
} catch( Throwable unused ) {
! // getContextClassLoader() undefined in JDK1.1
}
if( classLoader==ClassLoader.getSystemClassLoader() ) {
debugPrintln("using system class loader ("+classLoader+") for search");
return;
*** 124,145 ****
debugPrintln("using class loader ("+classLoader+") for search");
}
/**
* <p>Creates a new {@link XPathFactory} object for the specified
! * schema language.</p>
*
* @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.
*/
! public XPathFactory newFactory(String uri) {
! 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);
--- 119,142 ----
debugPrintln("using class loader ("+classLoader+") for search");
}
/**
* <p>Creates a new {@link XPathFactory} object for the specified
! * object model.</p>
*
* @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.
*/
! 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);
*** 152,174 ****
*
* @param uri identifies the object model.
*
* @return {@link XPathFactory} for the given object model.
*/
! private XPathFactory _newFactory(String uri) {
! XPathFactory xpathFactory;
String propertyName = SERVICE_CLASS.getName() + ":" + uri;
// system property look up
try {
debugPrintln("Looking up system property '"+propertyName+"'" );
String r = ss.getSystemProperty(propertyName);
if(r!=null) {
debugPrintln("The value is '"+r+"'");
xpathFactory = createInstance(r, true);
! if(xpathFactory != null) return xpathFactory;
} else
debugPrintln("The property is undefined.");
} catch( Throwable t ) {
if( debug ) {
debugPrintln("failed to look up system property '"+propertyName+"'" );
--- 149,173 ----
*
* @param uri identifies the object model.
*
* @return {@link XPathFactory} for the given object model.
*/
! private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException {
! XPathFactory xpathFactory = null;
String propertyName = SERVICE_CLASS.getName() + ":" + uri;
// system property look up
try {
debugPrintln("Looking up system property '"+propertyName+"'" );
String r = ss.getSystemProperty(propertyName);
if(r!=null) {
debugPrintln("The value is '"+r+"'");
xpathFactory = createInstance(r, true);
! if (xpathFactory != null) {
! return xpathFactory;
! }
} else
debugPrintln("The property is undefined.");
} catch( Throwable t ) {
if( debug ) {
debugPrintln("failed to look up system property '"+propertyName+"'" );
*** 178,189 ****
String javah = ss.getSystemProperty( "java.home" );
String configFile = javah + File.separator +
"lib" + File.separator + "jaxp.properties";
- String factoryClassName = null ;
-
// try to read from $java.home/lib/jaxp.properties
try {
if(firstTime){
synchronized(cacheProps){
if(firstTime){
--- 177,186 ----
*** 194,204 ****
cacheProps.load(ss.getFileInputStream(f));
}
}
}
}
! factoryClassName = cacheProps.getProperty(propertyName);
debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
xpathFactory = createInstance(factoryClassName, true);
if(xpathFactory != null){
--- 191,201 ----
cacheProps.load(ss.getFileInputStream(f));
}
}
}
}
! final String factoryClassName = cacheProps.getProperty(propertyName);
debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) {
xpathFactory = createInstance(factoryClassName, true);
if(xpathFactory != null){
*** 209,236 ****
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));
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)) {
debugPrintln("attempting to use the platform default W3C DOM XPath lib");
return createInstance("com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl", true);
--- 206,226 ----
if (debug) {
ex.printStackTrace();
}
}
! // Try with ServiceLoader
! assert xpathFactory == null;
! xpathFactory = findServiceProvider(uri);
!
! // The following assertion should always be true.
! // Uncomment it, recompile, and run with -ea in case of doubts:
! // assert xpathFactory == null || xpathFactory.isObjectModelSupported(uri);
!
if (xpathFactory != null) {
return xpathFactory;
}
// platform default
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);
*** 243,253 ****
/** <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;
// make sure we have access to restricted packages
boolean internal = false;
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
--- 233,243 ----
/** <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;
// make sure we have access to restricted packages
boolean internal = false;
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
*** 256,306 ****
}
// use approprite ClassLoader
try {
if (classLoader != null && !internal) {
! clazz = classLoader.loadClass(className);
} else {
clazz = Class.forName(className);
}
} catch (Throwable t) {
! 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.
*
* @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, boolean useServicesMechanism ) {
XPathFactory xPathFactory = null;
debugPrintln("createInstance(" + className + ")");
// get Class from 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 XPathFactory
try {
if (!useServicesMechanism) {
! xPathFactory = (XPathFactory) newInstanceNoServiceLoader(clazz);
}
if (xPathFactory == null) {
xPathFactory = (XPathFactory) clazz.newInstance();
}
} catch (ClassCastException classCastException) {
--- 246,303 ----
}
// use approprite ClassLoader
try {
if (classLoader != null && !internal) {
! clazz = Class.forName(className, false, classLoader);
} else {
clazz = Class.forName(className);
}
} catch (Throwable t) {
! 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 instantiated.
*
* @return null
* if it fails. Error messages will be printed by this method.
*/
! XPathFactory createInstance( String className )
! throws XPathFactoryConfigurationException
! {
return createInstance( className, false );
}
!
! XPathFactory createInstance( String className, boolean useServicesMechanism )
! throws XPathFactoryConfigurationException
! {
XPathFactory xPathFactory = null;
debugPrintln("createInstance(" + className + ")");
// get Class from 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 XPathFactory
try {
if (!useServicesMechanism) {
! xPathFactory = newInstanceNoServiceLoader(clazz);
}
if (xPathFactory == null) {
xPathFactory = (XPathFactory) clazz.newInstance();
}
} catch (ClassCastException classCastException) {
*** 327,533 ****
}
/**
* Try to construct using newXPathFactoryNoServiceLoader
* method if available.
*/
! private static Object newInstanceNoServiceLoader(
Class<?> providerClass
! ) {
// Retain maximum compatibility if no security manager.
if (System.getSecurityManager() == null) {
return null;
}
try {
Method creationMethod =
providerClass.getDeclaredMethod(
"newXPathFactoryNoServiceLoader"
);
! return creationMethod.invoke(null, (Object[])null);
! } catch (NoSuchMethodException exc) {
! return null;
! } catch (Exception exc) {
! 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 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>.
! */
! 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
! try {
! xPathFactory = (XPathFactory) clazz.newInstance();
! } catch (ClassCastException classCastExcpetion) {
! xPathFactory = null;
! continue;
! } catch (InstantiationException instantiationException) {
! xPathFactory = null;
! continue;
! } catch (IllegalAccessException illegalAccessException) {
! xPathFactory = null;
! continue;
! }
!
! // 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;
}
-
- // clean up
- configFile.close();
-
- // return new instance of XPathFactory or null
- return xPathFactory;
}
! /** 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();
}
/**
! * Looks up a value in a property file
! * while producing all sorts of debug messages.
*
! * @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.
! */
! 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 {
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();
}
- };
- } catch (IOException e) {
- debugPrintln("failed to enumerate resources "+SERVICE_ID);
- if(debug) e.printStackTrace();
- return new ArrayList().iterator(); // empty iterator
}
- }
- }
-
- 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() );
}
--- 324,421 ----
}
/**
* Try to construct using newXPathFactoryNoServiceLoader
* method if available.
*/
! private static XPathFactory newInstanceNoServiceLoader(
Class<?> providerClass
! ) throws XPathFactoryConfigurationException {
// Retain maximum compatibility if no security manager.
if (System.getSecurityManager() == null) {
return null;
}
try {
Method creationMethod =
providerClass.getDeclaredMethod(
"newXPathFactoryNoServiceLoader"
);
! final int modifiers = creationMethod.getModifiers();
! // Do not call "newXPathFactoryNoServiceLoader" if it's
! // not public static.
! if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
! return null;
}
! // Only calls "newXPathFactoryNoServiceLoader" if it's
! // declared to return an instance of XPathFactory.
! final Class<?> returnType = creationMethod.getReturnType();
! if (SERVICE_CLASS.isAssignableFrom(returnType)) {
! return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null));
! } else {
! // Should not happen since
! // XPathFactoryImpl.newXPathFactoryNoServiceLoader is
! // declared to return XPathFactory.
! throw new ClassCastException(returnType
! + " cannot be cast to " + SERVICE_CLASS);
}
! } catch (ClassCastException e) {
! throw new XPathFactoryConfigurationException(e);
! } catch (NoSuchMethodException exc) {
! return null;
! } catch (Exception exc) {
! return null;
}
}
! // Call isObjectModelSupportedBy with initial context.
! private boolean isObjectModelSupportedBy(final XPathFactory factory,
! final String objectModel,
! AccessControlContext acc) {
! return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
! public Boolean run() {
! return factory.isObjectModelSupported(objectModel);
}
! }, acc);
}
/**
! * Finds a service provider subclass of XPathFactory that supports the
! * given object model using the ServiceLoader.
*
! * @param objectModel URI of object model to support.
! * @return An XPathFactory supporting the specified object model, or null
! * if none is found.
! * @throws XPathFactoryConfigurationException if a configuration error is found.
*/
! private XPathFactory findServiceProvider(final String objectModel)
! throws XPathFactoryConfigurationException {
! assert objectModel != null;
! // store current context.
! final AccessControlContext acc = AccessController.getContext();
try {
! return AccessController.doPrivileged(new PrivilegedAction<XPathFactory>() {
! public XPathFactory run() {
! final ServiceLoader<XPathFactory> loader =
! ServiceLoader.load(SERVICE_CLASS);
! for (XPathFactory factory : loader) {
! // restore initial context to call
! // factory.isObjectModelSupportedBy
! if (isObjectModelSupportedBy(factory, objectModel, acc)) {
! return factory;
}
}
! return null; // no factory found.
}
! });
! } catch (ServiceConfigurationError error) {
! throw new XPathFactoryConfigurationException(error);
}
}
+ private static final Class<XPathFactory> SERVICE_CLASS = XPathFactory.class;
private static String which( Class clazz ) {
return which( clazz.getName(), clazz.getClassLoader() );
}