SchemaFactoryFinder.java revision 286
286N/A/*
286N/A * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
286N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
286N/A *
286N/A * This code is free software; you can redistribute it and/or modify it
286N/A * under the terms of the GNU General Public License version 2 only, as
286N/A * published by the Free Software Foundation. Oracle designates this
286N/A * particular file as subject to the "Classpath" exception as provided
286N/A * by Oracle in the LICENSE file that accompanied this code.
286N/A *
286N/A * This code is distributed in the hope that it will be useful, but WITHOUT
286N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
286N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
286N/A * version 2 for more details (a copy is included in the LICENSE file that
286N/A * accompanied this code).
286N/A *
286N/A * You should have received a copy of the GNU General Public License version
286N/A * 2 along with this work; if not, write to the Free Software Foundation,
286N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
286N/A *
286N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
286N/A * or visit www.oracle.com if you need additional information or have any
286N/A * questions.
286N/A */
286N/A
286N/Apackage javax.xml.validation;
286N/A
286N/Aimport java.io.BufferedReader;
286N/Aimport java.io.File;
286N/Aimport java.io.IOException;
286N/Aimport java.io.InputStream;
286N/Aimport java.io.InputStreamReader;
286N/Aimport java.lang.reflect.Method;
286N/Aimport java.lang.reflect.InvocationTargetException;
286N/Aimport java.net.URL;
286N/Aimport java.util.ArrayList;
286N/Aimport java.util.Enumeration;
286N/Aimport java.util.Iterator;
286N/Aimport java.util.NoSuchElementException;
286N/Aimport java.util.Properties;
286N/A
286N/A/**
286N/A * Implementation of {@link SchemaFactory#newInstance(String)}.
286N/A *
286N/A * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
286N/A * @version $Revision: 1.8 $, $Date: 2010-11-01 04:36:13 $
286N/A * @since 1.5
286N/A */
286N/Aclass SchemaFactoryFinder {
286N/A
286N/A /** debug support code. */
286N/A private static boolean debug = false;
286N/A /**
286N/A *<p> Take care of restrictions imposed by java security model </p>
286N/A */
286N/A private static SecuritySupport ss = new SecuritySupport();
286N/A /**
286N/A * <p>Cache properties for performance.</p>
286N/A */
286N/A private static Properties cacheProps = new Properties();
286N/A
286N/A /**
286N/A * <p>First time requires initialization overhead.</p>
286N/A */
286N/A private static volatile boolean firstTime = true;
286N/A
286N/A static {
286N/A // Use try/catch block to support applets
286N/A try {
286N/A debug = ss.getSystemProperty("jaxp.debug") != null;
286N/A } catch (Exception _) {
286N/A debug = false;
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * <p>Conditional debug printing.</p>
286N/A *
286N/A * @param msg to print
286N/A */
286N/A private static void debugPrintln(String msg) {
286N/A if (debug) {
286N/A System.err.println("JAXP: " + msg);
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * <p><code>ClassLoader</code> to use to find <code>SchemaFactory</code>.</p>
286N/A */
286N/A private final ClassLoader classLoader;
286N/A
286N/A /**
286N/A * <p>Constructor that specifies <code>ClassLoader</code> to use
286N/A * to find <code>SchemaFactory</code>.</p>
286N/A *
286N/A * @param loader
286N/A * to be used to load resource, {@link SchemaFactory}, and
286N/A * {@link SchemaFactoryLoader} implementations during
286N/A * the resolution process.
286N/A * If this parameter is null, the default system class loader
286N/A * will be used.
286N/A */
286N/A public SchemaFactoryFinder(ClassLoader loader) {
286N/A this.classLoader = loader;
286N/A if( debug ) {
286N/A debugDisplayClassLoader();
286N/A }
286N/A }
286N/A
286N/A private void debugDisplayClassLoader() {
286N/A try {
286N/A if( classLoader == ss.getContextClassLoader() ) {
286N/A debugPrintln("using thread context class loader ("+classLoader+") for search");
286N/A return;
286N/A }
286N/A } catch( Throwable _ ) {
286N/A ; // getContextClassLoader() undefined in JDK1.1
286N/A }
286N/A
286N/A if( classLoader==ClassLoader.getSystemClassLoader() ) {
286N/A debugPrintln("using system class loader ("+classLoader+") for search");
286N/A return;
286N/A }
286N/A
286N/A debugPrintln("using class loader ("+classLoader+") for search");
286N/A }
286N/A
286N/A /**
286N/A * <p>Creates a new {@link SchemaFactory} object for the specified
286N/A * schema language.</p>
286N/A *
286N/A * @param schemaLanguage
286N/A * See {@link SchemaFactory Schema Language} table in <code>SchemaFactory</code>
286N/A * for the list of available schema languages.
286N/A *
286N/A * @return <code>null</code> if the callee fails to create one.
286N/A *
286N/A * @throws NullPointerException
286N/A * If the <code>schemaLanguage</code> parameter is null.
286N/A */
286N/A public SchemaFactory newFactory(String schemaLanguage) {
286N/A if(schemaLanguage==null) throw new NullPointerException();
286N/A SchemaFactory f = _newFactory(schemaLanguage);
286N/A if (f != null) {
286N/A debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage);
286N/A } else {
286N/A debugPrintln("unable to find a factory for " + schemaLanguage);
286N/A }
286N/A return f;
286N/A }
286N/A
286N/A /**
286N/A * <p>Lookup a <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.</p>
286N/A *
286N/A * @param schemaLanguage Schema language to lookup <code>SchemaFactory</code> for.
286N/A *
286N/A * @return <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.
286N/A */
286N/A private SchemaFactory _newFactory(String schemaLanguage) {
286N/A SchemaFactory sf;
286N/A
286N/A String propertyName = SERVICE_CLASS.getName() + ":" + schemaLanguage;
286N/A
286N/A // system property look up
286N/A try {
286N/A debugPrintln("Looking up system property '"+propertyName+"'" );
286N/A String r = ss.getSystemProperty(propertyName);
286N/A if(r!=null) {
286N/A debugPrintln("The value is '"+r+"'");
286N/A sf = createInstance(r, true);
286N/A if(sf!=null) return sf;
286N/A } else
286N/A debugPrintln("The property is undefined.");
286N/A } catch( Throwable t ) {
286N/A if( debug ) {
286N/A debugPrintln("failed to look up system property '"+propertyName+"'" );
286N/A t.printStackTrace();
286N/A }
286N/A }
286N/A
286N/A String javah = ss.getSystemProperty( "java.home" );
286N/A String configFile = javah + File.separator +
286N/A "lib" + File.separator + "jaxp.properties";
286N/A
286N/A String factoryClassName = null ;
286N/A
286N/A // try to read from $java.home/lib/jaxp.properties
286N/A try {
286N/A if(firstTime){
286N/A synchronized(cacheProps){
286N/A if(firstTime){
286N/A File f=new File( configFile );
286N/A firstTime = false;
286N/A if(ss.doesFileExist(f)){
286N/A debugPrintln("Read properties file " + f);
286N/A cacheProps.load(ss.getFileInputStream(f));
286N/A }
286N/A }
286N/A }
286N/A }
286N/A factoryClassName = cacheProps.getProperty(propertyName);
286N/A debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
286N/A
286N/A if (factoryClassName != null) {
286N/A sf = createInstance(factoryClassName, true);
286N/A if(sf != null){
286N/A return sf;
286N/A }
286N/A }
286N/A } catch (Exception ex) {
286N/A if (debug) {
286N/A ex.printStackTrace();
286N/A }
286N/A }
286N/A
286N/A /**
286N/A // try to read from $java.home/lib/jaxp.properties
286N/A try {
286N/A String javah = ss.getSystemProperty( "java.home" );
286N/A String configFile = javah + File.separator +
286N/A "lib" + File.separator + "jaxp.properties";
286N/A File f = new File( configFile );
286N/A if( ss.doesFileExist(f)) {
286N/A sf = loadFromProperty(
286N/A propertyName,f.getAbsolutePath(), new FileInputStream(f));
286N/A if(sf!=null) return sf;
286N/A } else {
286N/A debugPrintln("Tried to read "+ f.getAbsolutePath()+", but it doesn't exist.");
286N/A }
286N/A } catch(Throwable e) {
286N/A if( debug ) {
286N/A debugPrintln("failed to read $java.home/lib/jaxp.properties");
286N/A e.printStackTrace();
286N/A }
286N/A }
286N/A */
286N/A
286N/A // try META-INF/services files
286N/A Iterator sitr = createServiceFileIterator();
286N/A while(sitr.hasNext()) {
286N/A URL resource = (URL)sitr.next();
286N/A debugPrintln("looking into " + resource);
286N/A try {
286N/A sf = loadFromService(schemaLanguage,resource.toExternalForm(),
286N/A ss.getURLInputStream(resource));
286N/A if(sf!=null) return sf;
286N/A } catch(IOException e) {
286N/A if( debug ) {
286N/A debugPrintln("failed to read "+resource);
286N/A e.printStackTrace();
286N/A }
286N/A }
286N/A }
286N/A
286N/A // platform default
286N/A if(schemaLanguage.equals("http://www.w3.org/2001/XMLSchema")) {
286N/A debugPrintln("attempting to use the platform default XML Schema validator");
286N/A return createInstance("com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory", true);
286N/A }
286N/A
286N/A debugPrintln("all things were tried, but none was found. bailing out.");
286N/A return null;
286N/A }
286N/A
286N/A /** <p>Create class using appropriate ClassLoader.</p>
286N/A *
286N/A * @param className Name of class to create.
286N/A * @return Created class or <code>null</code>.
286N/A */
286N/A private Class createClass(String className) {
286N/A Class clazz;
286N/A
286N/A // use approprite ClassLoader
286N/A try {
286N/A if (classLoader != null) {
286N/A clazz = classLoader.loadClass(className);
286N/A } else {
286N/A clazz = Class.forName(className);
286N/A }
286N/A } catch (Throwable t) {
286N/A if(debug) t.printStackTrace();
286N/A return null;
286N/A }
286N/A
286N/A return clazz;
286N/A }
286N/A
286N/A /**
286N/A * <p>Creates an instance of the specified and returns it.</p>
286N/A *
286N/A * @param className
286N/A * fully qualified class name to be instanciated.
286N/A *
286N/A * @return null
286N/A * if it fails. Error messages will be printed by this method.
286N/A */
286N/A SchemaFactory createInstance( String className ) {
286N/A return createInstance( className, false );
286N/A }
286N/A
286N/A SchemaFactory createInstance( String className, boolean useServicesMechanism ) {
286N/A SchemaFactory schemaFactory = null;
286N/A
286N/A debugPrintln("createInstance(" + className + ")");
286N/A
286N/A // get Class from className
286N/A Class clazz = createClass(className);
286N/A if (clazz == null) {
286N/A debugPrintln("failed to getClass(" + className + ")");
286N/A return null;
286N/A }
286N/A debugPrintln("loaded " + className + " from " + which(clazz));
286N/A
286N/A // instantiate Class as a SchemaFactory
286N/A try {
286N/A if (!useServicesMechanism) {
286N/A schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz);
286N/A }
286N/A if (schemaFactory == null) {
286N/A schemaFactory = (SchemaFactory) clazz.newInstance();
286N/A }
286N/A } catch (ClassCastException classCastException) {
286N/A debugPrintln("could not instantiate " + clazz.getName());
286N/A if (debug) {
286N/A classCastException.printStackTrace();
286N/A }
286N/A return null;
286N/A } catch (IllegalAccessException illegalAccessException) {
286N/A debugPrintln("could not instantiate " + clazz.getName());
286N/A if (debug) {
286N/A illegalAccessException.printStackTrace();
286N/A }
286N/A return null;
286N/A } catch (InstantiationException instantiationException) {
286N/A debugPrintln("could not instantiate " + clazz.getName());
286N/A if (debug) {
286N/A instantiationException.printStackTrace();
286N/A }
286N/A return null;
286N/A }
286N/A
286N/A return schemaFactory;
286N/A }
286N/A /**
286N/A * Try to construct using newTransformerFactoryNoServiceLoader
286N/A * method if available.
286N/A */
286N/A private static Object newInstanceNoServiceLoader(
286N/A Class<?> providerClass
286N/A ) {
286N/A // Retain maximum compatibility if no security manager.
286N/A if (System.getSecurityManager() == null) {
286N/A return null;
286N/A }
286N/A try {
286N/A Method creationMethod =
286N/A providerClass.getDeclaredMethod(
286N/A "newXMLSchemaFactoryNoServiceLoader"
286N/A );
286N/A return creationMethod.invoke(null, null);
286N/A } catch (NoSuchMethodException exc) {
286N/A return null;
286N/A } catch (Exception exc) {
286N/A return null;
286N/A }
286N/A }
286N/A
286N/A /** Iterator that lazily computes one value and returns it. */
286N/A private static abstract class SingleIterator implements Iterator {
286N/A private boolean seen = false;
286N/A
286N/A public final void remove() { throw new UnsupportedOperationException(); }
286N/A public final boolean hasNext() { return !seen; }
286N/A public final Object next() {
286N/A if(seen) throw new NoSuchElementException();
286N/A seen = true;
286N/A return value();
286N/A }
286N/A
286N/A protected abstract Object value();
286N/A }
286N/A
286N/A /**
286N/A * Looks up a value in a property file
286N/A * while producing all sorts of debug messages.
286N/A *
286N/A * @return null
286N/A * if there was an error.
286N/A */
286N/A private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in )
286N/A throws IOException {
286N/A debugPrintln("Reading "+resourceName );
286N/A
286N/A Properties props=new Properties();
286N/A props.load(in);
286N/A in.close();
286N/A String factoryClassName = props.getProperty(keyName);
286N/A if(factoryClassName != null){
286N/A debugPrintln("found "+keyName+" = " + factoryClassName);
286N/A return createInstance(factoryClassName);
286N/A } else {
286N/A debugPrintln(keyName+" is not in the property file");
286N/A return null;
286N/A }
286N/A }
286N/A
286N/A /**
286N/A * <p>Look up a value in a property file.</p>
286N/A *
286N/A * <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p>
286N/A *
286N/A * @param schemaLanguage Schema Language to support.
286N/A * @param inputName Name of <code>InputStream</code>.
286N/A * @param in <code>InputStream</code> of properties.
286N/A *
286N/A * @return <code>SchemaFactory</code> as determined by <code>keyName</code> value or <code>null</code> if there was an error.
286N/A *
286N/A * @throws IOException If IO error reading from <code>in</code>.
286N/A */
286N/A private SchemaFactory loadFromService(
286N/A String schemaLanguage,
286N/A String inputName,
286N/A InputStream in)
286N/A throws IOException {
286N/A
286N/A SchemaFactory schemaFactory = null;
286N/A final Class[] stringClassArray = {"".getClass()};
286N/A final Object[] schemaLanguageObjectArray = {schemaLanguage};
286N/A final String isSchemaLanguageSupportedMethod = "isSchemaLanguageSupported";
286N/A
286N/A debugPrintln("Reading " + inputName);
286N/A
286N/A // read from InputStream until a match is found
286N/A BufferedReader configFile = new BufferedReader(new InputStreamReader(in));
286N/A String line = null;
286N/A while ((line = configFile.readLine()) != null) {
286N/A // '#' is comment char
286N/A int comment = line.indexOf("#");
286N/A switch (comment) {
286N/A case -1: break; // no comment
286N/A case 0: line = ""; break; // entire line is a comment
286N/A default: line = line.substring(0, comment); break; // trim comment
286N/A }
286N/A
286N/A // trim whitespace
286N/A line = line.trim();
286N/A
286N/A // any content left on line?
286N/A if (line.length() == 0) {
286N/A continue;
286N/A }
286N/A
286N/A // line content is now the name of the class
286N/A Class clazz = createClass(line);
286N/A if (clazz == null) {
286N/A continue;
286N/A }
286N/A
286N/A // create an instance of the Class
286N/A try {
286N/A schemaFactory = (SchemaFactory) clazz.newInstance();
286N/A } catch (ClassCastException classCastExcpetion) {
286N/A schemaFactory = null;
286N/A continue;
286N/A } catch (InstantiationException instantiationException) {
286N/A schemaFactory = null;
286N/A continue;
286N/A } catch (IllegalAccessException illegalAccessException) {
286N/A schemaFactory = null;
286N/A continue;
286N/A }
286N/A
286N/A // does this Class support desired Schema?
286N/A try {
286N/A Method isSchemaLanguageSupported = clazz.getMethod(isSchemaLanguageSupportedMethod, stringClassArray);
286N/A Boolean supported = (Boolean) isSchemaLanguageSupported.invoke(schemaFactory, schemaLanguageObjectArray);
286N/A if (supported.booleanValue()) {
286N/A break;
286N/A }
286N/A } catch (NoSuchMethodException noSuchMethodException) {
286N/A
286N/A } catch (IllegalAccessException illegalAccessException) {
286N/A
286N/A } catch (InvocationTargetException invocationTargetException) {
286N/A
286N/A }
286N/A schemaFactory = null;
286N/A }
286N/A
286N/A // clean up
286N/A configFile.close();
286N/A
286N/A // return new instance of SchemaFactory or null
286N/A return schemaFactory;
286N/A }
286N/A
286N/A /**
286N/A * Returns an {@link Iterator} that enumerates all
286N/A * the META-INF/services files that we care.
286N/A */
286N/A private Iterator createServiceFileIterator() {
286N/A if (classLoader == null) {
286N/A return new SingleIterator() {
286N/A protected Object value() {
286N/A ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader();
286N/A //return (ClassLoader.getSystemResource( SERVICE_ID ));
286N/A return ss.getResourceAsURL(classLoader, SERVICE_ID);
286N/A }
286N/A };
286N/A } else {
286N/A try {
286N/A //final Enumeration e = classLoader.getResources(SERVICE_ID);
286N/A final Enumeration e = ss.getResources(classLoader, SERVICE_ID);
286N/A if(!e.hasMoreElements()) {
286N/A debugPrintln("no "+SERVICE_ID+" file was found");
286N/A }
286N/A
286N/A // wrap it into an Iterator.
286N/A return new Iterator() {
286N/A public void remove() {
286N/A throw new UnsupportedOperationException();
286N/A }
286N/A
286N/A public boolean hasNext() {
286N/A return e.hasMoreElements();
286N/A }
286N/A
286N/A public Object next() {
286N/A return e.nextElement();
286N/A }
286N/A };
286N/A } catch (IOException e) {
286N/A debugPrintln("failed to enumerate resources "+SERVICE_ID);
286N/A if(debug) e.printStackTrace();
286N/A return new ArrayList().iterator(); // empty iterator
286N/A }
286N/A }
286N/A }
286N/A
286N/A private static final Class SERVICE_CLASS = SchemaFactory.class;
286N/A private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
286N/A
286N/A
286N/A
286N/A private static String which( Class clazz ) {
286N/A return which( clazz.getName(), clazz.getClassLoader() );
286N/A }
286N/A
286N/A /**
286N/A * <p>Search the specified classloader for the given classname.</p>
286N/A *
286N/A * @param classname the fully qualified name of the class to search for
286N/A * @param loader the classloader to search
286N/A *
286N/A * @return the source location of the resource, or null if it wasn't found
286N/A */
286N/A private static String which(String classname, ClassLoader loader) {
286N/A
286N/A String classnameAsResource = classname.replace('.', '/') + ".class";
286N/A
286N/A if( loader==null ) loader = ClassLoader.getSystemClassLoader();
286N/A
286N/A //URL it = loader.getResource(classnameAsResource);
286N/A URL it = ss.getResourceAsURL(loader, classnameAsResource);
286N/A if (it != null) {
286N/A return it.toString();
286N/A } else {
286N/A return null;
286N/A }
286N/A }
286N/A}