blob: d2e6ca00af38a82c6006745a352bb1a8d0b71a68 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Standards for Technology in Automotive Retail
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Carver - STAR - bug 224197 - initial API and implementation
* based on work from Apache Xalan 2.7.0
*******************************************************************************/
/*
* 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: ObjectFactory.java,v 1.2 2008/03/28 02:38:18 dacarver Exp $
*/
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.extensions;
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.
* <p>
* 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.
* <p>
* This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
* class and modified to be used as a general utility for creating objects
* dynamically.
*
* @version $Id: ObjectFactory.java,v 1.2 2008/03/28 02:38:18 dacarver Exp $
*/
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:
* <ol>
* <li>query the system property using <code>System.getProperty</code>
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
* <li>use fallback classname
* </ol>
*
* @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:
* <ol>
* <li>query the system property using <code>System.getProperty</code>
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
* <li>use fallback classname
* </ol>
*
* @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:
* <ol>
* <li>query the system property using <code>System.getProperty</code>
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
* <li>use fallback classname
* </ol>
*
* @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:
* <ol>
* <li>query the system property using <code>System.getProperty</code>
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
* <li>use fallback classname
* </ol>
*
* @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:
* <ol>
* <li>query the system property using <code>System.getProperty</code>
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
* <li>use fallback classname
* </ol>
*
* @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;
FileInputStream fis = null;
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();
fis = ss.getFileInputStream(propertiesFile);
fXalanProperties.load(fis);
}
} catch (Exception x) {
fXalanProperties = null;
fLastModified = -1;
// assert(x instanceof FileNotFoundException
// || x instanceof SecurityException)
// In both cases, ignore and continue w/ next location
} finally {
// try to close the input stream if one was opened.
if (fis != null) {
try {
fis.close();
}
// Ignore the exception.
catch (IOException exc) {
}
}
}
}
if (fXalanProperties != null) {
factoryClassName = fXalanProperties.getProperty(factoryId);
}
} else {
FileInputStream fis = null;
try {
fis = ss.getFileInputStream(new File(propertiesFilename));
Properties props = new Properties();
props.load(fis);
factoryClassName = props.getProperty(factoryId);
} catch (Exception x) {
// assert(x instanceof FileNotFoundException
// || x instanceof SecurityException)
// In both cases, ignore and continue w/ next location
} finally {
// try to close the input stream if one was opened.
if (fis != null) {
try {
fis.close();
}
// Ignore the exception.
catch (IOException exc) {
}
}
}
}
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();
try {
if (security != null) {
final int lastDot = className.lastIndexOf(".");
String packageName = className;
if (lastDot != -1)
packageName = className.substring(0, lastDot);
security.checkPackageAccess(packageName);
}
} catch (SecurityException e) {
throw e;
}
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();
} catch (IOException x) {
// No provider found
return null;
} finally {
try {
// try to close the reader.
rd.close();
}
// Ignore the exception.
catch (IOException exc) {
}
}
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 {
static final long serialVersionUID = 8564305128443551853L;
//
// 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;
} // <init>(String,Exception)
//
// Public methods
//
/** Returns the exception associated to this error. */
Exception getException() {
return exception;
} // getException():Exception
} // class ConfigurationError
} // class ObjectFactory