1
0

Improvements to the new LogFactory APIs, based on feedback from Costin and

Jon plus some additional thought about using it in a multi-class-loader
environment (like Tomcat):

* Changed newFactory() to getFactory(), and implemented a cache of
  previously created factory instances (one per class loader).  This
  avoids potentially expensive and redundant discovery operations.

* Added convenient static getLog() method so a typical application
  component can initialize it's Log instance like this:

    Log log = LogFactory.getLog("com.mycompany.mypackage.MyClass");

* Added variants of getInstance() and getLog() that take a Class
  parameter instead of a String.  LogSource had this convenience
  feature, and there's no reason not to keep it.

* Added release() and releaseAll() methods to instruct the factory
  instances to release any cached references to other LogFactory
  or Log instances.  This is important, for example, if you put
  commons-logging.jar in Tomcat's shared "lib" directory, and then
  use the application reload facility.  The references maintained
  here would otherwise prevent garbage collection of the old
  webapp class loader once a reload takes place.

* Added a note on getInstance() that you can make no assumptions
  about whether or not the actual Log instance you get back is
  shared or not.  The actual sharability is a feature of the
  LogFactory implementation you are using, and what kind of a
  class loader environment you ae installing.

* Deprecated LogSource, but left it there to ease transition of
  existing code using it.


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/logging/trunk@138858 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Craig R. McClanahan
2002-02-14 00:19:03 +00:00
parent 753843f275
commit 6a315e28ed
3 changed files with 189 additions and 39 deletions

View File

@@ -1,7 +1,7 @@
/* /*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//logging/src/java/org/apache/commons/logging/LogFactory.java,v 1.1 2002/02/13 02:18:11 craigmcc Exp $ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//logging/src/java/org/apache/commons/logging/LogFactory.java,v 1.2 2002/02/14 00:19:03 craigmcc Exp $
* $Revision: 1.1 $ * $Revision: 1.2 $
* $Date: 2002/02/13 02:18:11 $ * $Date: 2002/02/14 00:19:03 $
* *
* ==================================================================== * ====================================================================
* *
@@ -67,6 +67,7 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties; import java.util.Properties;
@@ -81,7 +82,7 @@ import java.util.Properties;
* *
* @author Craig R. McClanahan * @author Craig R. McClanahan
* @author Costin Manolache * @author Costin Manolache
* @version $Revision: 1.1 $ $Date: 2002/02/13 02:18:11 $ * @version $Revision: 1.2 $ $Date: 2002/02/14 00:19:03 $
*/ */
public abstract class LogFactory { public abstract class LogFactory {
@@ -142,10 +143,29 @@ public abstract class LogFactory {
public abstract String[] getAttributeNames(); public abstract String[] getAttributeNames();
/**
* Convenience method to derive a name from the specified class and
* call <code>getInstance(String)</code> with it.
*
* @param clazz Class for which a suitable Log name will be derived
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public abstract Log getInstance(Class clazz)
throws LogConfigurationException;
/** /**
* <p>Construct (if necessary) and return a <code>Log</code> instance, * <p>Construct (if necessary) and return a <code>Log</code> instance,
* using the factory's current set of configuration attributes.</p> * using the factory's current set of configuration attributes.</p>
* *
* <p><strong>NOTE</strong> - Depending upon the implementation of
* the <code>LogFactory</code> you are using, the <code>Log</code>
* 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.</p>
*
* @param name Logical name of the <code>Log</code> instance to be * @param name Logical name of the <code>Log</code> instance to be
* returned (the meaning of this name is only known to the underlying * returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped) * logging implementation that is being wrapped)
@@ -157,6 +177,16 @@ public abstract class LogFactory {
throws LogConfigurationException; throws LogConfigurationException;
/**
* Release any internal references to previously created {@link Log}
* instances returned by this factory. This is useful 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. * Remove any configuration attribute associated with the specified name.
* If there is no such attribute, no action is taken. * If there is no such attribute, no action is taken.
@@ -178,13 +208,23 @@ public abstract class LogFactory {
public abstract void setAttribute(String name, Object value); public abstract void setAttribute(String name, Object value);
// ------------------------------------------------------- Static Variables
/**
* The previously constructed <code>LogFactory</code> instances, keyed by
* the <code>ClassLoader</code> with which it was created.
*/
protected static Hashtable factories = new Hashtable();
// --------------------------------------------------------- Static Methods // --------------------------------------------------------- Static Methods
/** /**
* <p>Construct and return a new <code>LogFactory</code> instance, using * <p>Construct (if necessary) and return a <code>LogFactory</code>
* the following ordered lookup procedure to determine the name of the * instance, using the following ordered lookup procedure to determine
* implementation class to be loaded:</p> * the name of the implementation class to be loaded:</p>
* <ul> * <ul>
* <li>The <code>org.apache.commons.logging.LogFactory</code> system * <li>The <code>org.apache.commons.logging.LogFactory</code> system
* property.</li> * property.</li>
@@ -205,22 +245,29 @@ public abstract class LogFactory {
* @exception LogConfigurationException if the implementation class is not * @exception LogConfigurationException if the implementation class is not
* available or cannot be instantiated. * available or cannot be instantiated.
*/ */
public static LogFactory newFactory() throws LogConfigurationException { public static LogFactory getFactory() throws LogConfigurationException {
// Identify the class loader we will be using // Identify the class loader we will be using
ClassLoader classLoader = findClassLoader(); ClassLoader classLoader = findClassLoader();
// Return any previously registered factory for this class loader
LogFactory factory = (LogFactory) factories.get(classLoader);
if (factory != null) {
return (factory);
}
// First, try the system property // First, try the system property
try { try {
String factoryClass = System.getProperty(FACTORY_PROPERTY); String factoryClass = System.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) { if (factoryClass != null) {
return (newInstance(factoryClass, classLoader)); factory = newFactory(factoryClass, classLoader);
} }
} catch (SecurityException e) { } catch (SecurityException e) {
; ;
} }
// Second, try a properties file // Second, try a properties file
if (factory == null) {
try { try {
InputStream stream = InputStream stream =
classLoader.getResourceAsStream(FACTORY_PROPERTIES); classLoader.getResourceAsStream(FACTORY_PROPERTIES);
@@ -230,23 +277,86 @@ public abstract class LogFactory {
stream.close(); stream.close();
String factoryClass = props.getProperty(FACTORY_PROPERTY); String factoryClass = props.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) { if (factoryClass != null) {
LogFactory instance = factory = newFactory(factoryClass, classLoader);
newInstance(factoryClass, classLoader);
Enumeration names = props.propertyNames(); Enumeration names = props.propertyNames();
while (names.hasMoreElements()) { while (names.hasMoreElements()) {
String name = (String) names.nextElement(); String name = (String) names.nextElement();
String value = props.getProperty(name); String value = props.getProperty(name);
instance.setAttribute(name, value); factory.setAttribute(name, value);
} }
return (instance);
} }
} }
} catch (IOException e) { } catch (IOException e) {
} catch (SecurityException e) { } catch (SecurityException e) {
} }
}
// Third, try the fallback implementation class // Third, try the fallback implementation class
return (newInstance(FACTORY_DEFAULT, classLoader)); if (factory == null) {
factory = newFactory(FACTORY_DEFAULT, classLoader);
}
// Cache and return the new factory instance
factories.put(classLoader, factory);
return (factory);
}
/**
* Convenience method to return a named logger, without the application
* having to care about factories.
*
* @param clazz Class for which a log name will be derived
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* 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 <code>Log</code> 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 <code>Log</code>
* 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, after calling the instance method <code>release()</code> on
* each of them. This is useful 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();
}
} }
@@ -308,7 +418,7 @@ public abstract class LogFactory {
* @exception LogConfigurationException if a suitable instance * @exception LogConfigurationException if a suitable instance
* cannot be created * cannot be created
*/ */
private static LogFactory newInstance(String factoryClass, protected static LogFactory newFactory(String factoryClass,
ClassLoader classLoader) ClassLoader classLoader)
throws LogConfigurationException { throws LogConfigurationException {

View File

@@ -1,7 +1,7 @@
/* /*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//logging/src/java/org/apache/commons/logging/LogSource.java,v 1.12 2002/02/03 01:31:54 sanders Exp $ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//logging/src/java/org/apache/commons/logging/LogSource.java,v 1.13 2002/02/14 00:19:03 craigmcc Exp $
* $Revision: 1.12 $ * $Revision: 1.13 $
* $Date: 2002/02/03 01:31:54 $ * $Date: 2002/02/14 00:19:03 $
* *
* ==================================================================== * ====================================================================
* *
@@ -93,8 +93,11 @@ import org.apache.commons.logging.impl.NoOpLog;
* <li>At runtime, call <code>LogSource.setLogImplementation()</code>.</li> * <li>At runtime, call <code>LogSource.setLogImplementation()</code>.</li>
* </ul> * </ul>
* *
* @deprecated Use {@link LogFactory} instead - The default factory
* implementation performs exactly the same algorithm as this class did
*
* @author Rod Waldhoff * @author Rod Waldhoff
* @version $Id: LogSource.java,v 1.12 2002/02/03 01:31:54 sanders Exp $ * @version $Id: LogSource.java,v 1.13 2002/02/14 00:19:03 craigmcc Exp $
*/ */
public class LogSource { public class LogSource {

View File

@@ -1,7 +1,7 @@
/* /*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//logging/src/java/org/apache/commons/logging/impl/LogFactoryImpl.java,v 1.1 2002/02/13 02:18:11 craigmcc Exp $ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//logging/src/java/org/apache/commons/logging/impl/LogFactoryImpl.java,v 1.2 2002/02/14 00:19:03 craigmcc Exp $
* $Revision: 1.1 $ * $Revision: 1.2 $
* $Date: 2002/02/13 02:18:11 $ * $Date: 2002/02/14 00:19:03 $
* *
* ==================================================================== * ====================================================================
* *
@@ -101,7 +101,7 @@ import org.apache.commons.logging.LogSource;
* *
* @author Rod Waldhoff * @author Rod Waldhoff
* @author Craig R. McClanahan * @author Craig R. McClanahan
* @version $Revision: 1.1 $ $Date: 2002/02/13 02:18:11 $ * @version $Revision: 1.2 $ $Date: 2002/02/14 00:19:03 $
*/ */
public class LogFactoryImpl extends LogFactory { public class LogFactoryImpl extends LogFactory {
@@ -228,10 +228,33 @@ public class LogFactoryImpl extends LogFactory {
} }
/**
* Convenience method to derive a name from the specified class and
* call <code>getInstance(String)</code> with it.
*
* @param clazz Class for which a suitable Log name will be derived
*
* @exception LogConfigurationException if a suitable <code>Log</code>
* instance cannot be returned
*/
public Log getInstance(Class clazz)
throws LogConfigurationException {
return (getInstance(clazz.getName()));
}
/** /**
* <p>Construct (if necessary) and return a <code>Log</code> instance, * <p>Construct (if necessary) and return a <code>Log</code> instance,
* using the factory's current set of configuration attributes.</p> * using the factory's current set of configuration attributes.</p>
* *
* <p><strong>NOTE</strong> - Depending upon the implementation of
* the <code>LogFactory</code> you are using, the <code>Log</code>
* 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.</p>
*
* @param name Logical name of the <code>Log</code> instance to be * @param name Logical name of the <code>Log</code> instance to be
* returned (the meaning of this name is only known to the underlying * returned (the meaning of this name is only known to the underlying
* logging implementation that is being wrapped) * logging implementation that is being wrapped)
@@ -252,6 +275,20 @@ public class LogFactoryImpl extends LogFactory {
} }
/**
* Release any internal references to previously created {@link Log}
* instances returned by this factory. This is useful 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 void release() {
instances.clear();
}
/** /**
* Remove any configuration attribute associated with the specified name. * Remove any configuration attribute associated with the specified name.
* If there is no such attribute, no action is taken. * If there is no such attribute, no action is taken.