/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * $Id$ */ package com.sun.org.apache.xml.internal.utils; import java.io.InputStream; import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.util.Properties; import java.io.BufferedReader; import java.io.InputStreamReader; /** * This class is duplicated for each JAXP subpackage so keep it in sync. * It is package private and therefore is not exposed as part of the JAXP * API. *

* This code is designed to implement the JAXP 1.1 spec pluggability * feature and is designed to run on JDK version 1.1 and * later, and to compile on JDK 1.2 and onward. * The code also runs both as part of an unbundled jar file and * when bundled as part of the JDK. *

* This class was moved from the javax.xml.parsers.ObjectFactory * class and modified to be used as a general utility for creating objects * dynamically. * * @version $Id: $ */ class ObjectFactory { // // Constants // // name of default properties file to look for in JDK's jre/lib directory private static final String DEFAULT_PROPERTIES_FILENAME = "xalan.properties"; private static final String SERVICES_PATH = "META-INF/services/"; /** Set to true for debugging */ private static final boolean DEBUG = false; /** cache the contents of the xalan.properties file. * Until an attempt has been made to read this file, this will * be null; if the file does not exist or we encounter some other error * during the read, this will be empty. */ private static Properties fXalanProperties = null; /*** * Cache the time stamp of the xalan.properties file so * that we know if it's been modified and can invalidate * the cache when necessary. */ private static long fLastModified = -1; // // Public static methods // /** * Finds the implementation Class object in the specified order. The * specified order is the following: *

    *
  1. query the system property using System.getProperty *
  2. read META-INF/services/factoryId file *
  3. use fallback classname *
* * @return instance of factory, never null * * @param factoryId Name of the factory to find, same as * a property name * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * @exception ObjectFactory.ConfigurationError */ static Object createObject(String factoryId, String fallbackClassName) throws ConfigurationError { return createObject(factoryId, null, fallbackClassName); } // createObject(String,String):Object /** * Finds the implementation Class object in the specified order. The * specified order is the following: *
    *
  1. query the system property using System.getProperty *
  2. read $java.home/lib/propertiesFilename file *
  3. read META-INF/services/factoryId file *
  4. use fallback classname *
* * @return instance of factory, never null * * @param factoryId Name of the factory to find, same as * a property name * @param propertiesFilename The filename in the $java.home/lib directory * of the properties file. If none specified, * ${java.home}/lib/xalan.properties will be used. * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * @exception ObjectFactory.ConfigurationError */ static Object createObject(String factoryId, String propertiesFilename, String fallbackClassName) throws ConfigurationError { Class factoryClass = lookUpFactoryClass(factoryId, propertiesFilename, fallbackClassName); if (factoryClass == null) { throw new ConfigurationError( "Provider for " + factoryId + " cannot be found", null); } try{ Object instance = factoryClass.newInstance(); debugPrintln("created new instance of factory " + factoryId); return instance; } catch (Exception x) { throw new ConfigurationError( "Provider for factory " + factoryId + " could not be instantiated: " + x, x); } } // createObject(String,String,String):Object /** * Finds the implementation Class object in the specified order. The * specified order is the following: *
    *
  1. query the system property using System.getProperty *
  2. read $java.home/lib/propertiesFilename file *
  3. read META-INF/services/factoryId file *
  4. use fallback classname *
* * @return Class object of factory, never null * * @param factoryId Name of the factory to find, same as * a property name * @param propertiesFilename The filename in the $java.home/lib directory * of the properties file. If none specified, * ${java.home}/lib/xalan.properties will be used. * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * @exception ObjectFactory.ConfigurationError */ static Class lookUpFactoryClass(String factoryId) throws ConfigurationError { return lookUpFactoryClass(factoryId, null, null); } // lookUpFactoryClass(String):Class /** * Finds the implementation Class object in the specified order. The * specified order is the following: *
    *
  1. query the system property using System.getProperty *
  2. read $java.home/lib/propertiesFilename file *
  3. read META-INF/services/factoryId file *
  4. use fallback classname *
* * @return Class object that provides factory service, never null * * @param factoryId Name of the factory to find, same as * a property name * @param propertiesFilename The filename in the $java.home/lib directory * of the properties file. If none specified, * ${java.home}/lib/xalan.properties will be used. * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * @exception ObjectFactory.ConfigurationError */ static Class lookUpFactoryClass(String factoryId, String propertiesFilename, String fallbackClassName) throws ConfigurationError { String factoryClassName = lookUpFactoryClassName(factoryId, propertiesFilename, fallbackClassName); ClassLoader cl = findClassLoader(); if (factoryClassName == null) { factoryClassName = fallbackClassName; } // assert(className != null); try{ Class providerClass = findProviderClass(factoryClassName, cl, true); debugPrintln("created new instance of " + providerClass + " using ClassLoader: " + cl); return providerClass; } catch (ClassNotFoundException x) { throw new ConfigurationError( "Provider " + factoryClassName + " not found", x); } catch (Exception x) { throw new ConfigurationError( "Provider "+factoryClassName+" could not be instantiated: "+x, x); } } // lookUpFactoryClass(String,String,String):Class /** * Finds the name of the required implementation class in the specified * order. The specified order is the following: *
    *
  1. query the system property using System.getProperty *
  2. read $java.home/lib/propertiesFilename file *
  3. read META-INF/services/factoryId file *
  4. use fallback classname *
* * @return name of class that provides factory service, never null * * @param factoryId Name of the factory to find, same as * a property name * @param propertiesFilename The filename in the $java.home/lib directory * of the properties file. If none specified, * ${java.home}/lib/xalan.properties will be used. * @param fallbackClassName Implementation class name, if nothing else * is found. Use null to mean no fallback. * * @exception ObjectFactory.ConfigurationError */ static String lookUpFactoryClassName(String factoryId, String propertiesFilename, String fallbackClassName) { SecuritySupport ss = SecuritySupport.getInstance(); // Use the system property first try { String systemProp = ss.getSystemProperty(factoryId); if (systemProp != null) { debugPrintln("found system property, value=" + systemProp); return systemProp; } } catch (SecurityException se) { // Ignore and continue w/ next location } // Try to read from propertiesFilename, or // $java.home/lib/xalan.properties String factoryClassName = null; // no properties file name specified; use // $JAVA_HOME/lib/xalan.properties: if (propertiesFilename == null) { File propertiesFile = null; boolean propertiesFileExists = false; try { String javah = ss.getSystemProperty("java.home"); propertiesFilename = javah + File.separator + "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME; propertiesFile = new File(propertiesFilename); propertiesFileExists = ss.getFileExists(propertiesFile); } catch (SecurityException e) { // try again... fLastModified = -1; fXalanProperties = null; } synchronized (ObjectFactory.class) { boolean loadProperties = false; try { // file existed last time if(fLastModified >= 0) { if(propertiesFileExists && (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) { loadProperties = true; } else { // file has stopped existing... if(!propertiesFileExists) { fLastModified = -1; fXalanProperties = null; } // else, file wasn't modified! } } else { // file has started to exist: if(propertiesFileExists) { loadProperties = true; fLastModified = ss.getLastModified(propertiesFile); } // else, nothing's changed } if(loadProperties) { // must never have attempted to read xalan.properties // before (or it's outdeated) fXalanProperties = new Properties(); FileInputStream fis = ss.getFileInputStream(propertiesFile); fXalanProperties.load(fis); fis.close(); } } catch (Exception x) { fXalanProperties = null; fLastModified = -1; // assert(x instanceof FileNotFoundException // || x instanceof SecurityException) // In both cases, ignore and continue w/ next location } } if(fXalanProperties != null) { factoryClassName = fXalanProperties.getProperty(factoryId); } } else { try { FileInputStream fis = ss.getFileInputStream(new File(propertiesFilename)); Properties props = new Properties(); props.load(fis); fis.close(); factoryClassName = props.getProperty(factoryId); } catch (Exception x) { // assert(x instanceof FileNotFoundException // || x instanceof SecurityException) // In both cases, ignore and continue w/ next location } } if (factoryClassName != null) { debugPrintln("found in " + propertiesFilename + ", value=" + factoryClassName); return factoryClassName; } // Try Jar Service Provider Mechanism return findJarServiceProviderName(factoryId); } // lookUpFactoryClass(String,String):String // // Private static methods // /** Prints a message to standard error if debugging is enabled. */ private static void debugPrintln(String msg) { if (DEBUG) { System.err.println("JAXP: " + msg); } } // debugPrintln(String) /** * Figure out which ClassLoader to use. For JDK 1.2 and later use * the context ClassLoader. */ static ClassLoader findClassLoader() throws ConfigurationError { SecuritySupport ss = SecuritySupport.getInstance(); // Figure out which ClassLoader to use for loading the provider // class. If there is a Context ClassLoader then use it. ClassLoader context = ss.getContextClassLoader(); ClassLoader system = ss.getSystemClassLoader(); ClassLoader chain = system; while (true) { if (context == chain) { // Assert: we are on JDK 1.1 or we have no Context ClassLoader // or any Context ClassLoader in chain of system classloader // (including extension ClassLoader) so extend to widest // ClassLoader (always look in system ClassLoader if Xalan // is in boot/extension/system classpath and in current // ClassLoader otherwise); normal classloaders delegate // back to system ClassLoader first so this widening doesn't // change the fact that context ClassLoader will be consulted ClassLoader current = ObjectFactory.class.getClassLoader(); chain = system; while (true) { if (current == chain) { // Assert: Current ClassLoader in chain of // boot/extension/system ClassLoaders return system; } if (chain == null) { break; } chain = ss.getParentClassLoader(chain); } // Assert: Current ClassLoader not in chain of // boot/extension/system ClassLoaders return current; } if (chain == null) { // boot ClassLoader reached break; } // Check for any extension ClassLoaders in chain up to // boot ClassLoader chain = ss.getParentClassLoader(chain); }; // Assert: Context ClassLoader not in chain of // boot/extension/system ClassLoaders return context; } // findClassLoader():ClassLoader /** * Create an instance of a class using the specified ClassLoader */ static Object newInstance(String className, ClassLoader cl, boolean doFallback) throws ConfigurationError { // assert(className != null); try{ Class providerClass = findProviderClass(className, cl, doFallback); Object instance = providerClass.newInstance(); debugPrintln("created new instance of " + providerClass + " using ClassLoader: " + cl); return instance; } catch (ClassNotFoundException x) { throw new ConfigurationError( "Provider " + className + " not found", x); } catch (Exception x) { throw new ConfigurationError( "Provider " + className + " could not be instantiated: " + x, x); } } /** * Find a Class using the specified ClassLoader */ static Class findProviderClass(String className, ClassLoader cl, boolean doFallback) throws ClassNotFoundException, ConfigurationError { //throw security exception if the calling thread is not allowed to access the //class. Restrict the access to the package classes as specified in java.security policy. SecurityManager security = System.getSecurityManager(); if (security != null){ final int lastDot = className.lastIndexOf("."); String packageName = className; if (lastDot != -1) packageName = className.substring(0, lastDot); security.checkPackageAccess(packageName); } Class providerClass; if (cl == null) { // XXX Use the bootstrap ClassLoader. There is no way to // load a class using the bootstrap ClassLoader that works // in both JDK 1.1 and Java 2. However, this should still // work b/c the following should be true: // // (cl == null) iff current ClassLoader == null // // Thus Class.forName(String) will use the current // ClassLoader which will be the bootstrap ClassLoader. providerClass = Class.forName(className); } else { try { providerClass = cl.loadClass(className); } catch (ClassNotFoundException x) { if (doFallback) { // Fall back to current classloader ClassLoader current = ObjectFactory.class.getClassLoader(); if (current == null) { providerClass = Class.forName(className); } else if (cl != current) { cl = current; providerClass = cl.loadClass(className); } else { throw x; } } else { throw x; } } } return providerClass; } /** * Find the name of service provider using Jar Service Provider Mechanism * * @return instance of provider class if found or null */ private static String findJarServiceProviderName(String factoryId) { SecuritySupport ss = SecuritySupport.getInstance(); String serviceId = SERVICES_PATH + factoryId; InputStream is = null; // First try the Context ClassLoader ClassLoader cl = findClassLoader(); is = ss.getResourceAsStream(cl, serviceId); // If no provider found then try the current ClassLoader if (is == null) { ClassLoader current = ObjectFactory.class.getClassLoader(); if (cl != current) { cl = current; is = ss.getResourceAsStream(cl, serviceId); } } if (is == null) { // No provider found return null; } debugPrintln("found jar resource=" + serviceId + " using ClassLoader: " + cl); // Read the service provider name in UTF-8 as specified in // the jar spec. Unfortunately this fails in Microsoft // VJ++, which does not implement the UTF-8 // encoding. Theoretically, we should simply let it fail in // that case, since the JVM is obviously broken if it // doesn't support such a basic standard. But since there // are still some users attempting to use VJ++ for // development, we have dropped in a fallback which makes a // second attempt using the platform's default encoding. In // VJ++ this is apparently ASCII, which is a subset of // UTF-8... and since the strings we'll be reading here are // also primarily limited to the 7-bit ASCII range (at // least, in English versions), this should work well // enough to keep us on the air until we're ready to // officially decommit from VJ++. [Edited comment from // jkesselm] BufferedReader rd; try { rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); } catch (java.io.UnsupportedEncodingException e) { rd = new BufferedReader(new InputStreamReader(is)); } String factoryClassName = null; try { // XXX Does not handle all possible input as specified by the // Jar Service Provider specification factoryClassName = rd.readLine(); rd.close(); } catch (IOException x) { // No provider found return null; } if (factoryClassName != null && ! "".equals(factoryClassName)) { debugPrintln("found in resource, value=" + factoryClassName); // Note: here we do not want to fall back to the current // ClassLoader because we want to avoid the case where the // resource file was found using one ClassLoader and the // provider class was instantiated using a different one. return factoryClassName; } // No provider found return null; } // // Classes // /** * A configuration error. */ static class ConfigurationError extends Error { // // Data // /** Exception. */ private Exception exception; // // Constructors // /** * Construct a new instance with the specified detail string and * exception. */ ConfigurationError(String msg, Exception x) { super(msg); this.exception = x; } // (String,Exception) // // Public methods // /** Returns the exception associated to this error. */ Exception getException() { return exception; } // getException():Exception } // class ConfigurationError } // class ObjectFactory