/*
* Copyright (c) 2011-2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* Copyright 2001-2005 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.
*/
package com.sun.org.apache.xerces.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.
*
*
* @version $Id: ObjectFactory.java,v 1.6 2010/04/23 01:44:34 joehw Exp $
*/
public final class ObjectFactory {
//
// Constants
//
private static final String DEFAULT_INTERNAL_CLASSES = "com.sun.org.apache.";
// name of default properties file to look for in JDK's jre/lib directory
private static final String DEFAULT_PROPERTIES_FILENAME = "xerces.properties";
/** Set to true for debugging */
private static final boolean DEBUG = isDebugEnabled();
/**
* Default columns per line.
*/
private static final int DEFAULT_LINE_LENGTH = 80;
/** cache the contents of the xerces.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 fXercesProperties = null;
/***
* Cache the time stamp of the xerces.properties file so
* that we know if it's been modified and can invalidate
* the cache when necessary.
*/
private static long fLastModified = -1;
//
// static methods
//
/**
* Finds the implementation Class object in the specified order. The
* specified order is the following:
*
* - query the system property using
System.getProperty
* - read
META-INF/services/factoryId
file
* - use fallback classname
*
*
* @return Class object 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
*/
public 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:
*
* - query the system property using
System.getProperty
* - read
$java.home/lib/propertiesFilename
file
* - read
META-INF/services/factoryId
file
* - 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/xerces.properties will be used.
* @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback.
*
* @exception ObjectFactory.ConfigurationError
*/
public static Object createObject(String factoryId,
String propertiesFilename,
String fallbackClassName)
throws ConfigurationError
{
if (DEBUG) debugPrintln("debug is on");
ClassLoader cl = findClassLoader();
// Use the system property first
try {
String systemProp = SecuritySupport.getSystemProperty(factoryId);
if (systemProp != null && systemProp.length() > 0) {
if (DEBUG) debugPrintln("found system property, value=" + systemProp);
return newInstance(systemProp, cl, true);
}
} catch (SecurityException se) {
// Ignore and continue w/ next location
}
// JAXP specific change
// always use fallback class to avoid the expense of constantly
// "stat"ing a non-existent "xerces.properties" and jar SPI entry
// see CR 6400863: Expensive creating of SAX parser in Mustang
if (fallbackClassName == null) {
throw new ConfigurationError(
"Provider for " + factoryId + " cannot be found", null);
}
if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
return newInstance(fallbackClassName, cl, true);
} // createObject(String,String,String):Object
//
// Private static methods
//
/** Returns true if debug has been enabled. */
private static boolean isDebugEnabled() {
try {
String val = SecuritySupport.getSystemProperty("xerces.debug");
// Allow simply setting the prop to turn on debug
return (val != null && (!"false".equals(val)));
}
catch (SecurityException se) {}
return false;
} // isDebugEnabled()
/** Prints a message to standard error if debugging is enabled. */
private static void debugPrintln(String msg) {
if (DEBUG) {
System.err.println("XERCES: " + msg);
}
} // debugPrintln(String)
/**
* Figure out which ClassLoader to use. For JDK 1.2 and later use
* the context ClassLoader.
*/
public static ClassLoader findClassLoader()
throws ConfigurationError
{
if (System.getSecurityManager()!=null) {
//this will ensure bootclassloader is used
return null;
}
// Figure out which ClassLoader to use for loading the provider
// class. If there is a Context ClassLoader then use it.
ClassLoader context = SecuritySupport.getContextClassLoader();
ClassLoader system = SecuritySupport.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 Xerces
// 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 = SecuritySupport.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 = SecuritySupport.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 same classloader for the ObjectFactory by default
* or bootclassloader when Security Manager is in place
*/
public static Object newInstance(String className, boolean doFallback)
throws ConfigurationError
{
if (System.getSecurityManager()!=null) {
return newInstance(className, null, doFallback);
} else {
return newInstance(className,
findClassLoader (), doFallback);
}
}
/**
* Create an instance of a class using the specified ClassLoader
*/
public 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();
if (DEBUG) 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 same classloader for the ObjectFactory by default
* or bootclassloader when Security Manager is in place
*/
public static Class findProviderClass(String className, boolean doFallback)
throws ClassNotFoundException, ConfigurationError
{
if (System.getSecurityManager()!=null) {
return Class.forName(className);
} else {
return findProviderClass (className,
findClassLoader (), doFallback);
}
}
/**
* Find a Class using the specified ClassLoader
*/
public 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 package
//restrict the access to package as speicified in java.security policy
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (className.startsWith(DEFAULT_INTERNAL_CLASSES)) {
cl = null;
} else {
final int lastDot = className.lastIndexOf(".");
String packageName = className;
if (lastDot != -1) packageName = className.substring(0, lastDot);
security.checkPackageAccess(packageName);
}
}
Class providerClass;
if (cl == null) {
//use 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;
}
/*
* Try to find provider using Jar Service Provider Mechanism
*
* @return instance of provider class if found or null
*/
private static Object findJarServiceProvider(String factoryId)
throws ConfigurationError
{
String serviceId = "META-INF/services/" + factoryId;
InputStream is = null;
// First try the Context ClassLoader
ClassLoader cl = findClassLoader();
is = SecuritySupport.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 = SecuritySupport.getResourceAsStream(cl, serviceId);
}
}
if (is == null) {
// No provider found
return null;
}
if (DEBUG) 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"), DEFAULT_LINE_LENGTH);
} catch (java.io.UnsupportedEncodingException e) {
rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
}
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)) {
if (DEBUG) 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 newInstance(factoryClassName, cl, false);
}
// No provider found
return null;
}
} // class ObjectFactory