org.apache.commons.logging.LogFactory Maven / Gradle / Ivy
/*
* 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.
*/
package org.apache.commons.logging;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
/**
* Factory for creating {@link Log} instances, with discovery and
* configuration features similar to that employed by standard Java APIs
* such as JAXP.
*
* IMPLEMENTATION NOTE - This implementation is heavily
* based on the SAXParserFactory and DocumentBuilderFactory implementations
* (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
*
* @author Craig R. McClanahan
* @author Costin Manolache
* @author Richard A. Sitze
* @version $Revision: 1.27 $ $Date: 2004/06/06 21:15:12 $
*/
public abstract class LogFactory {
// ----------------------------------------------------- Manifest Constants
/**
* The name of the property used to identify the LogFactory implementation
* class name.
*/
public static final String FACTORY_PROPERTY =
"org.apache.commons.logging.LogFactory";
/**
* The fully qualified class name of the fallback LogFactory
* implementation class to use, if no other can be found.
*/
public static final String FACTORY_DEFAULT =
"org.apache.commons.logging.impl.LogFactoryImpl";
/**
* The name of the properties file to search for.
*/
public static final String FACTORY_PROPERTIES =
"commons-logging.properties";
/**
* JDK1.3+
* 'Service Provider' specification.
*
*/
protected static final String SERVICE_ID =
"META-INF/services/org.apache.commons.logging.LogFactory";
// ----------------------------------------------------------- Constructors
/**
* Protected constructor that is not available for public use.
*/
protected LogFactory() { }
// --------------------------------------------------------- Public Methods
/**
* Return the configuration attribute with the specified name (if any),
* or null
if there is no such attribute.
*
* @param name Name of the attribute to return
*/
public abstract Object getAttribute(String name);
/**
* Return an array containing the names of all currently defined
* configuration attributes. If there are no such attributes, a zero
* length array is returned.
*/
public abstract String[] getAttributeNames();
/**
* Convenience method to derive a name from the specified class and
* call getInstance(String)
with it.
*
* @param clazz Class for which a suitable Log name will be derived
*
* @exception LogConfigurationException if a suitable Log
* instance cannot be returned
*/
public abstract Log getInstance(Class clazz)
throws LogConfigurationException;
/**
* Construct (if necessary) and return a Log
instance,
* using the factory's current set of configuration attributes.
*
* NOTE - Depending upon the implementation of
* the LogFactory
you are using, the Log
* instance you are returned may or may not be local to the current
* application, and may or may not be returned again on a subsequent
* call with the same name argument.
*
* @param name Logical name of the Log
instance to be
* returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped)
*
* @exception LogConfigurationException if a suitable Log
* instance cannot be returned
*/
public abstract Log getInstance(String name)
throws LogConfigurationException;
/**
* Release any internal references to previously created {@link Log}
* instances returned by this factory. This is useful in environments
* like servlet containers, which implement application reloading by
* throwing away a ClassLoader. Dangling references to objects in that
* class loader would prevent garbage collection.
*/
public abstract void release();
/**
* Remove any configuration attribute associated with the specified name.
* If there is no such attribute, no action is taken.
*
* @param name Name of the attribute to remove
*/
public abstract void removeAttribute(String name);
/**
* Set the configuration attribute with the specified name. Calling
* this with a null
value is equivalent to calling
* removeAttribute(name)
.
*
* @param name Name of the attribute to set
* @param value Value of the attribute to set, or null
* to remove any setting for this attribute
*/
public abstract void setAttribute(String name, Object value);
// ------------------------------------------------------- Static Variables
/**
* The previously constructed LogFactory
instances, keyed by
* the ClassLoader
with which it was created.
*/
protected static Hashtable factories = new Hashtable();
// --------------------------------------------------------- Static Methods
/**
* Construct (if necessary) and return a LogFactory
* instance, using the following ordered lookup procedure to determine
* the name of the implementation class to be loaded.
*
* - The
org.apache.commons.logging.LogFactory
system
* property.
* - The JDK 1.3 Service Discovery mechanism
* - Use the properties file
commons-logging.properties
* file, if found in the class path of this class. The configuration
* file is in standard java.util.Properties
format and
* contains the fully qualified name of the implementation class
* with the key being the system property defined above.
* - Fall back to a default implementation class
* (
org.apache.commons.logging.impl.LogFactoryImpl
).
*
*
* NOTE - If the properties file method of identifying the
* LogFactory
implementation class is utilized, all of the
* properties defined in this file will be set as configuration attributes
* on the corresponding LogFactory
instance.
*
* @exception LogConfigurationException if the implementation class is not
* available or cannot be instantiated.
*/
public static LogFactory getFactory() throws LogConfigurationException {
// Identify the class loader we will be using
ClassLoader contextClassLoader =
(ClassLoader)AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
return getContextClassLoader();
}
});
// Return any previously registered factory for this class loader
LogFactory factory = getCachedFactory(contextClassLoader);
if (factory != null)
return factory;
// Load properties file.
// Will be used one way or another in the end.
Properties props=null;
try {
InputStream stream = getResourceAsStream(contextClassLoader,
FACTORY_PROPERTIES);
if (stream != null) {
props = new Properties();
props.load(stream);
stream.close();
}
} catch (IOException e) {
} catch (SecurityException e) {
}
// First, try the system property
try {
String factoryClass = System.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) {
factory = newFactory(factoryClass, contextClassLoader);
}
} catch (SecurityException e) {
; // ignore
}
// Second, try to find a service by using the JDK1.3 jar
// discovery mechanism. This will allow users to plug a logger
// by just placing it in the lib/ directory of the webapp ( or in
// CLASSPATH or equivalent ). This is similar to the second
// step, except that it uses the (standard?) jdk1.3 location in the jar.
if (factory == null) {
try {
InputStream is = getResourceAsStream(contextClassLoader,
SERVICE_ID);
if( is != null ) {
// This code is needed by EBCDIC and other strange systems.
// It's a fix for bugs reported in xerces
BufferedReader rd;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
} catch (java.io.UnsupportedEncodingException e) {
rd = new BufferedReader(new InputStreamReader(is));
}
String factoryClassName = rd.readLine();
rd.close();
if (factoryClassName != null &&
! "".equals(factoryClassName)) {
factory= newFactory( factoryClassName, contextClassLoader );
}
}
} catch( Exception ex ) {
;
}
}
// Third try a properties file.
// If the properties file exists, it'll be read and the properties
// used. IMHO ( costin ) System property and JDK1.3 jar service
// should be enough for detecting the class name. The properties
// should be used to set the attributes ( which may be specific to
// the webapp, even if a default logger is set at JVM level by a
// system property )
if (factory == null && props != null) {
String factoryClass = props.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) {
factory = newFactory(factoryClass, contextClassLoader);
}
}
// Fourth, try the fallback implementation class
if (factory == null) {
factory = newFactory(FACTORY_DEFAULT, LogFactory.class.getClassLoader());
}
if (factory != null) {
/**
* Always cache using context class loader.
*/
cacheFactory(contextClassLoader, factory);
if( props!=null ) {
Enumeration names = props.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String value = props.getProperty(name);
factory.setAttribute(name, value);
}
}
}
return factory;
}
/**
* Convenience method to return a named logger, without the application
* having to care about factories.
*
* @param clazz Class from which a log name will be derived
*
* @exception LogConfigurationException if a suitable Log
* instance cannot be returned
*/
public static Log getLog(Class clazz)
throws LogConfigurationException {
return (getFactory().getInstance(clazz));
}
/**
* Convenience method to return a named logger, without the application
* having to care about factories.
*
* @param name Logical name of the Log
instance to be
* returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped)
*
* @exception LogConfigurationException if a suitable Log
* instance cannot be returned
*/
public static Log getLog(String name)
throws LogConfigurationException {
return (getFactory().getInstance(name));
}
/**
* Release any internal references to previously created {@link LogFactory}
* instances that have been associated with the specified class loader
* (if any), after calling the instance method release()
on
* each of them.
*
* @param classLoader ClassLoader for which to release the LogFactory
*/
public static void release(ClassLoader classLoader) {
synchronized (factories) {
LogFactory factory = (LogFactory) factories.get(classLoader);
if (factory != null) {
factory.release();
factories.remove(classLoader);
}
}
}
/**
* Release any internal references to previously created {@link LogFactory}
* instances, after calling the instance method release()
on
* each of them. This is useful in environments like servlet containers,
* which implement application reloading by throwing away a ClassLoader.
* Dangling references to objects in that class loader would prevent
* garbage collection.
*/
public static void releaseAll() {
synchronized (factories) {
Enumeration elements = factories.elements();
while (elements.hasMoreElements()) {
LogFactory element = (LogFactory) elements.nextElement();
element.release();
}
factories.clear();
}
}
// ------------------------------------------------------ Protected Methods
/**
* Return the thread context class loader if available.
* Otherwise return null.
*
* The thread context class loader is available for JDK 1.2
* or later, if certain security conditions are met.
*
* @exception LogConfigurationException if a suitable class loader
* cannot be identified.
*/
protected static ClassLoader getContextClassLoader()
throws LogConfigurationException
{
ClassLoader classLoader = null;
try {
// Are we running on a JDK 1.2 or later system?
Method method = Thread.class.getMethod("getContextClassLoader", null);
// Get the thread context class loader (if there is one)
try {
classLoader = (ClassLoader)method.invoke(Thread.currentThread(), null);
} catch (IllegalAccessException e) {
throw new LogConfigurationException
("Unexpected IllegalAccessException", e);
} catch (InvocationTargetException e) {
/**
* InvocationTargetException is thrown by 'invoke' when
* the method being invoked (getContextClassLoader) throws
* an exception.
*
* getContextClassLoader() throws SecurityException when
* the context class loader isn't an ancestor of the
* calling class's class loader, or if security
* permissions are restricted.
*
* In the first case (not related), we want to ignore and
* keep going. We cannot help but also ignore the second
* with the logic below, but other calls elsewhere (to
* obtain a class loader) will trigger this exception where
* we can make a distinction.
*/
if (e.getTargetException() instanceof SecurityException) {
; // ignore
} else {
// Capture 'e.getTargetException()' exception for details
// alternate: log 'e.getTargetException()', and pass back 'e'.
throw new LogConfigurationException
("Unexpected InvocationTargetException", e.getTargetException());
}
}
} catch (NoSuchMethodException e) {
// Assume we are running on JDK 1.1
classLoader = LogFactory.class.getClassLoader();
}
// Return the selected class loader
return classLoader;
}
/**
* Check cached factories (keyed by contextClassLoader)
*/
private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
{
LogFactory factory = null;
if (contextClassLoader != null)
factory = (LogFactory) factories.get(contextClassLoader);
return factory;
}
private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
{
if (classLoader != null && factory != null)
factories.put(classLoader, factory);
}
/**
* Return a new instance of the specified LogFactory
* implementation class, loaded by the specified class loader.
* If that fails, try the class loader used to load this
* (abstract) LogFactory.
*
* @param factoryClass Fully qualified name of the LogFactory
* implementation class
* @param classLoader ClassLoader from which to load this class
*
* @exception LogConfigurationException if a suitable instance
* cannot be created
*/
protected static LogFactory newFactory(final String factoryClass,
final ClassLoader classLoader)
throws LogConfigurationException
{
Object result = AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
// This will be used to diagnose bad configurations
// and allow a useful message to be sent to the user
Class logFactoryClass = null;
try {
if (classLoader != null) {
try {
// First the given class loader param (thread class loader)
// Warning: must typecast here & allow exception
// to be generated/caught & recast properly.
logFactoryClass = classLoader.loadClass(factoryClass);
return (LogFactory) logFactoryClass.newInstance();
} catch (ClassNotFoundException ex) {
if (classLoader == LogFactory.class.getClassLoader()) {
// Nothing more to try, onwards.
throw ex;
}
// ignore exception, continue
} catch (NoClassDefFoundError e) {
if (classLoader == LogFactory.class.getClassLoader()) {
// Nothing more to try, onwards.
throw e;
}
} catch(ClassCastException e){
if (classLoader == LogFactory.class.getClassLoader()) {
// Nothing more to try, onwards (bug in loader implementation).
throw e;
}
}
// Ignore exception, continue
}
/* At this point, either classLoader == null, OR
* classLoader was unable to load factoryClass.
* Try the class loader that loaded this class:
* LogFactory.getClassLoader().
*
* Notes:
* a) LogFactory.class.getClassLoader() may return 'null'
* if LogFactory is loaded by the bootstrap classloader.
* b) The Java endorsed library mechanism is instead
* Class.forName(factoryClass);
*/
// Warning: must typecast here & allow exception
// to be generated/caught & recast properly.
logFactoryClass = Class.forName(factoryClass);
return (LogFactory) logFactoryClass.newInstance();
} catch (Exception e) {
// Check to see if we've got a bad configuration
if (logFactoryClass != null
&& !LogFactory.class.isAssignableFrom(logFactoryClass)) {
return new LogConfigurationException(
"The chosen LogFactory implementation does not extend LogFactory."
+ " Please check your configuration.",
e);
}
return new LogConfigurationException(e);
}
}
});
if (result instanceof LogConfigurationException)
throw (LogConfigurationException)result;
return (LogFactory)result;
}
private static InputStream getResourceAsStream(final ClassLoader loader,
final String name)
{
return (InputStream)AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
if (loader != null) {
return loader.getResourceAsStream(name);
} else {
return ClassLoader.getSystemResourceAsStream(name);
}
}
});
}
}