diff -r fd16c54261b3 -r 7f561c08de6b jaxp/src/share/classes/javax/xml/validation/SchemaFactoryFinder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jaxp/src/share/classes/javax/xml/validation/SchemaFactoryFinder.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,538 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any 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.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 SchemaFactory#newInstance(String)}. + * + * @author Kohsuke Kawaguchi + * @since 1.5 + */ +class SchemaFactoryFinder { + + /** debug support code. */ + private static boolean debug = false; + /** + *

Take care of restrictions imposed by java security model

+ */ + private static SecuritySupport ss = new SecuritySupport(); + /** + *

Cache properties for performance.

+ */ + private static Properties cacheProps = new Properties(); + + /** + *

First time requires initialization overhead.

+ */ + private static boolean firstTime = true; + + static { + // Use try/catch block to support applets + try { + debug = ss.getSystemProperty("jaxp.debug") != null; + } catch (Exception _) { + debug = false; + } + } + + /** + *

Conditional debug printing.

+ * + * @param msg to print + */ + private static void debugPrintln(String msg) { + if (debug) { + System.err.println("JAXP: " + msg); + } + } + + /** + *

ClassLoader to use to find SchemaFactory.

+ */ + private final ClassLoader classLoader; + + /** + *

Constructor that specifies ClassLoader to use + * to find SchemaFactory.

+ * + * @param loader + * to be used to load resource, {@link SchemaFactory}, and + * {@link SchemaFactoryLoader} implementations during + * the resolution process. + * If this parameter is null, the default system class loader + * will be used. + */ + public SchemaFactoryFinder(ClassLoader loader) { + this.classLoader = loader; + if( debug ) { + debugDisplayClassLoader(); + } + } + + private void debugDisplayClassLoader() { + try { + if( classLoader == ss.getContextClassLoader() ) { + debugPrintln("using thread context class loader ("+classLoader+") for search"); + return; + } + } catch( Throwable _ ) { + ; // getContextClassLoader() undefined in JDK1.1 + } + + if( classLoader==ClassLoader.getSystemClassLoader() ) { + debugPrintln("using system class loader ("+classLoader+") for search"); + return; + } + + debugPrintln("using class loader ("+classLoader+") for search"); + } + + /** + *

Creates a new {@link SchemaFactory} object for the specified + * schema language.

+ * + * @param schemaLanguage + * See {@link SchemaFactory Schema Language} table in SchemaFactory + * for the list of available schema languages. + * + * @return null if the callee fails to create one. + * + * @throws NullPointerException + * If the schemaLanguage parameter is null. + */ + public SchemaFactory newFactory(String schemaLanguage) { + 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); + } + return f; + } + + /** + *

Lookup a SchemaFactory for the given schemaLanguage.

+ * + * @param schemaLanguage Schema language to lookup SchemaFactory for. + * + * @return SchemaFactory for the given schemaLanguage. + */ + private SchemaFactory _newFactory(String schemaLanguage) { + SchemaFactory sf; + + String propertyName = SERVICE_CLASS.getName() + ":" + schemaLanguage; + + // system property look up + try { + debugPrintln("Looking up system property '"+propertyName+"'" ); + String r = ss.getSystemProperty(propertyName); + if(r!=null) { + debugPrintln("The value is '"+r+"'"); + sf = createInstance(r); + if(sf!=null) return sf; + } else + debugPrintln("The property is undefined."); + } 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 factoryClassName = null ; + + // try to read from $java.home/lib/jaxp.properties + try { + if(firstTime){ + synchronized(cacheProps){ + if(firstTime){ + File f=new File( configFile ); + firstTime = false; + if(ss.doesFileExist(f)){ + debugPrintln("Read properties file " + f); + cacheProps.load(ss.getFileInputStream(f)); + } + } + } + } + factoryClassName = cacheProps.getProperty(propertyName); + debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); + + if (factoryClassName != null) { + sf = createInstance(factoryClassName); + if(sf != null){ + return sf; + } + } + } catch (Exception ex) { + 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(); + } + } + } + + // platform default + if(schemaLanguage.equals("http://www.w3.org/2001/XMLSchema")) { + debugPrintln("attempting to use the platform default XML Schema validator"); + return createInstance("com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory"); + } + + debugPrintln("all things were tried, but none was found. bailing out."); + return null; + } + + /**

Create class using appropriate ClassLoader.

+ * + * @param className Name of class to create. + * @return Created class or null. + */ + 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; + } + + return clazz; + } + + /** + *

Creates an instance of the specified and returns it.

+ * + * @param className + * fully qualified class name to be instanciated. + * + * @return null + * if it fails. Error messages will be printed by this method. + */ + SchemaFactory createInstance( String className ) { + SchemaFactory schemaFactory = 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 SchemaFactory + try { + schemaFactory = (SchemaFactory) clazz.newInstance(); + } catch (ClassCastException classCastException) { + debugPrintln("could not instantiate " + clazz.getName()); + if (debug) { + classCastException.printStackTrace(); + } + return null; + } catch (IllegalAccessException illegalAccessException) { + debugPrintln("could not instantiate " + clazz.getName()); + if (debug) { + illegalAccessException.printStackTrace(); + } + return null; + } catch (InstantiationException instantiationException) { + debugPrintln("could not instantiate " + clazz.getName()); + if (debug) { + instantiationException.printStackTrace(); + } + return null; + } + + return schemaFactory; + } + + /** 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 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"); + 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; + } + + /** + * 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(); + } + }; + } 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 = SchemaFactory.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() ); + } + + /** + *

Search the specified classloader for the given classname.

+ * + * @param classname the fully qualified name of the class to search for + * @param loader the classloader to search + * + * @return the source location of the resource, or null if it wasn't found + */ + private static String which(String classname, ClassLoader loader) { + + String classnameAsResource = classname.replace('.', '/') + ".class"; + + if( loader==null ) loader = ClassLoader.getSystemClassLoader(); + + //URL it = loader.getResource(classnameAsResource); + URL it = ss.getResourceAsURL(loader, classnameAsResource); + if (it != null) { + return it.toString(); + } else { + return null; + } + } +}