/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you 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 1225583 2011-12-29 16:08:00Z mrglavas $ | |
*/ | |
package org.apache.xml.serializer; | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.util.Properties; | |
/** | |
* 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. | |
* | |
* @xsl.usage internal | |
*/ | |
final 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) | |
{ | |
// Use the system property first | |
try { | |
String systemProp = SecuritySupport.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 = SecuritySupport.getSystemProperty("java.home"); | |
propertiesFilename = javah + File.separator + | |
"lib" + File.separator + DEFAULT_PROPERTIES_FILENAME; | |
propertiesFile = new File(propertiesFilename); | |
propertiesFileExists = SecuritySupport.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 = SecuritySupport.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 = SecuritySupport.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 = SecuritySupport.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 = SecuritySupport.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 | |
{ | |
// Now we just return the ClassLoader which loads this class | |
// to work in OSGI platform | |
return ObjectFactory.class.getClassLoader(); | |
/* | |
// 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 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 = 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 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) | |
{ | |
String serviceId = SERVICES_PATH + 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; | |
} | |
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 = 8859254254255146542L; | |
// | |
// 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 |