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>