0001: /*
0002: * Copyright 2001-2006 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.commons.logging;
0018:
0019: import java.io.BufferedReader;
0020: import java.io.FileOutputStream;
0021: import java.io.IOException;
0022: import java.io.InputStream;
0023: import java.io.InputStreamReader;
0024: import java.io.PrintStream;
0025: import java.lang.reflect.InvocationTargetException;
0026: import java.lang.reflect.Method;
0027: import java.net.URL;
0028: import java.security.AccessController;
0029: import java.security.PrivilegedAction;
0030: import java.util.Enumeration;
0031: import java.util.Hashtable;
0032: import java.util.Properties;
0033:
0034: /**
0035: * <p>Factory for creating {@link Log} instances, with discovery and
0036: * configuration features similar to that employed by standard Java APIs
0037: * such as JAXP.</p>
0038: *
0039: * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
0040: * based on the SAXParserFactory and DocumentBuilderFactory implementations
0041: * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
0042: *
0043: * @author Craig R. McClanahan
0044: * @author Costin Manolache
0045: * @author Richard A. Sitze
0046: * @version $Revision: 399431 $ $Date: 2006-05-03 21:58:34 +0100 (Wed, 03 May 2006) $
0047: */
0048:
0049: public abstract class LogFactory {
0050:
0051: // ----------------------------------------------------- Manifest Constants
0052:
0053: /**
0054: * The name (<code>priority</code>) of the key in the config file used to
0055: * specify the priority of that particular config file. The associated value
0056: * is a floating-point number; higher values take priority over lower values.
0057: */
0058: public static final String PRIORITY_KEY = "priority";
0059:
0060: /**
0061: * The name (<code>use_tccl</code>) of the key in the config file used
0062: * to specify whether logging classes should be loaded via the thread
0063: * context class loader (TCCL), or not. By default, the TCCL is used.
0064: */
0065: public static final String TCCL_KEY = "use_tccl";
0066:
0067: /**
0068: * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property
0069: * used to identify the LogFactory implementation
0070: * class name. This can be used as a system property, or as an entry in a
0071: * configuration properties file.
0072: */
0073: public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory";
0074:
0075: /**
0076: * The fully qualified class name of the fallback <code>LogFactory</code>
0077: * implementation class to use, if no other can be found.
0078: */
0079: public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl";
0080:
0081: /**
0082: * The name (<code>commons-logging.properties</code>) of the properties file to search for.
0083: */
0084: public static final String FACTORY_PROPERTIES = "commons-logging.properties";
0085:
0086: /**
0087: * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
0088: * 'Service Provider' specification</a>.
0089: *
0090: */
0091: protected static final String SERVICE_ID = "META-INF/services/org.apache.commons.logging.LogFactory";
0092:
0093: /**
0094: * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
0095: * of the property used to enable internal commons-logging
0096: * diagnostic output, in order to get information on what logging
0097: * implementations are being discovered, what classloaders they
0098: * are loaded through, etc.
0099: * <p>
0100: * If a system property of this name is set then the value is
0101: * assumed to be the name of a file. The special strings
0102: * STDOUT or STDERR (case-sensitive) indicate output to
0103: * System.out and System.err respectively.
0104: * <p>
0105: * Diagnostic logging should be used only to debug problematic
0106: * configurations and should not be set in normal production use.
0107: */
0108: public static final String DIAGNOSTICS_DEST_PROPERTY = "org.apache.commons.logging.diagnostics.dest";
0109:
0110: /**
0111: * When null (the usual case), no diagnostic output will be
0112: * generated by LogFactory or LogFactoryImpl. When non-null,
0113: * interesting events will be written to the specified object.
0114: */
0115: private static PrintStream diagnosticsStream = null;
0116:
0117: /**
0118: * A string that gets prefixed to every message output by the
0119: * logDiagnostic method, so that users can clearly see which
0120: * LogFactory class is generating the output.
0121: */
0122: private static String diagnosticPrefix;
0123:
0124: /**
0125: * <p>Setting this system property
0126: * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>)
0127: * value allows the <code>Hashtable</code> used to store
0128: * classloaders to be substituted by an alternative implementation.
0129: * </p>
0130: * <p>
0131: * <strong>Note:</strong> <code>LogFactory</code> will print:
0132: * <code><pre>
0133: * [ERROR] LogFactory: Load of custom hashtable failed</em>
0134: * </pre></code>
0135: * to system error and then continue using a standard Hashtable.
0136: * </p>
0137: * <p>
0138: * <strong>Usage:</strong> Set this property when Java is invoked
0139: * and <code>LogFactory</code> will attempt to load a new instance
0140: * of the given implementation class.
0141: * For example, running the following ant scriplet:
0142: * <code><pre>
0143: * <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}">
0144: * ...
0145: * <sysproperty
0146: * key="org.apache.commons.logging.LogFactory.HashtableImpl"
0147: * value="org.apache.commons.logging.AltHashtable"/>
0148: * </java>
0149: * </pre></code>
0150: * will mean that <code>LogFactory</code> will load an instance of
0151: * <code>org.apache.commons.logging.AltHashtable</code>.
0152: * </p>
0153: * <p>
0154: * A typical use case is to allow a custom
0155: * Hashtable implementation using weak references to be substituted.
0156: * This will allow classloaders to be garbage collected without
0157: * the need to release them (on 1.3+ JVMs only, of course ;)
0158: * </p>
0159: */
0160: public static final String HASHTABLE_IMPLEMENTATION_PROPERTY = "org.apache.commons.logging.LogFactory.HashtableImpl";
0161: /** Name used to load the weak hashtable implementation by names */
0162: private static final String WEAK_HASHTABLE_CLASSNAME = "org.apache.commons.logging.impl.WeakHashtable";
0163:
0164: /**
0165: * A reference to the classloader that loaded this class. This is the
0166: * same as LogFactory.class.getClassLoader(). However computing this
0167: * value isn't quite as simple as that, as we potentially need to use
0168: * AccessControllers etc. It's more efficient to compute it once and
0169: * cache it here.
0170: */
0171: private static ClassLoader this ClassLoader;
0172:
0173: // ----------------------------------------------------------- Constructors
0174:
0175: /**
0176: * Protected constructor that is not available for public use.
0177: */
0178: protected LogFactory() {
0179: }
0180:
0181: // --------------------------------------------------------- Public Methods
0182:
0183: /**
0184: * Return the configuration attribute with the specified name (if any),
0185: * or <code>null</code> if there is no such attribute.
0186: *
0187: * @param name Name of the attribute to return
0188: */
0189: public abstract Object getAttribute(String name);
0190:
0191: /**
0192: * Return an array containing the names of all currently defined
0193: * configuration attributes. If there are no such attributes, a zero
0194: * length array is returned.
0195: */
0196: public abstract String[] getAttributeNames();
0197:
0198: /**
0199: * Convenience method to derive a name from the specified class and
0200: * call <code>getInstance(String)</code> with it.
0201: *
0202: * @param clazz Class for which a suitable Log name will be derived
0203: *
0204: * @exception LogConfigurationException if a suitable <code>Log</code>
0205: * instance cannot be returned
0206: */
0207: public abstract Log getInstance(Class clazz)
0208: throws LogConfigurationException;
0209:
0210: /**
0211: * <p>Construct (if necessary) and return a <code>Log</code> instance,
0212: * using the factory's current set of configuration attributes.</p>
0213: *
0214: * <p><strong>NOTE</strong> - Depending upon the implementation of
0215: * the <code>LogFactory</code> you are using, the <code>Log</code>
0216: * instance you are returned may or may not be local to the current
0217: * application, and may or may not be returned again on a subsequent
0218: * call with the same name argument.</p>
0219: *
0220: * @param name Logical name of the <code>Log</code> instance to be
0221: * returned (the meaning of this name is only known to the underlying
0222: * logging implementation that is being wrapped)
0223: *
0224: * @exception LogConfigurationException if a suitable <code>Log</code>
0225: * instance cannot be returned
0226: */
0227: public abstract Log getInstance(String name)
0228: throws LogConfigurationException;
0229:
0230: /**
0231: * Release any internal references to previously created {@link Log}
0232: * instances returned by this factory. This is useful in environments
0233: * like servlet containers, which implement application reloading by
0234: * throwing away a ClassLoader. Dangling references to objects in that
0235: * class loader would prevent garbage collection.
0236: */
0237: public abstract void release();
0238:
0239: /**
0240: * Remove any configuration attribute associated with the specified name.
0241: * If there is no such attribute, no action is taken.
0242: *
0243: * @param name Name of the attribute to remove
0244: */
0245: public abstract void removeAttribute(String name);
0246:
0247: /**
0248: * Set the configuration attribute with the specified name. Calling
0249: * this with a <code>null</code> value is equivalent to calling
0250: * <code>removeAttribute(name)</code>.
0251: *
0252: * @param name Name of the attribute to set
0253: * @param value Value of the attribute to set, or <code>null</code>
0254: * to remove any setting for this attribute
0255: */
0256: public abstract void setAttribute(String name, Object value);
0257:
0258: // ------------------------------------------------------- Static Variables
0259:
0260: /**
0261: * The previously constructed <code>LogFactory</code> instances, keyed by
0262: * the <code>ClassLoader</code> with which it was created.
0263: */
0264: protected static Hashtable factories = null;
0265:
0266: /**
0267: * Prevously constructed <code>LogFactory</code> instance as in the
0268: * <code>factories</code> map, but for the case where
0269: * <code>getClassLoader</code> returns <code>null</code>.
0270: * This can happen when:
0271: * <ul>
0272: * <li>using JDK1.1 and the calling code is loaded via the system
0273: * classloader (very common)</li>
0274: * <li>using JDK1.2+ and the calling code is loaded via the boot
0275: * classloader (only likely for embedded systems work).</li>
0276: * </ul>
0277: * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
0278: * and hashtables don't allow null as a key.
0279: */
0280: protected static LogFactory nullClassLoaderFactory = null;
0281:
0282: /**
0283: * Create the hashtable which will be used to store a map of
0284: * (context-classloader -> logfactory-object). Version 1.2+ of Java
0285: * supports "weak references", allowing a custom Hashtable class
0286: * to be used which uses only weak references to its keys. Using weak
0287: * references can fix memory leaks on webapp unload in some cases (though
0288: * not all). Version 1.1 of Java does not support weak references, so we
0289: * must dynamically determine which we are using. And just for fun, this
0290: * code also supports the ability for a system property to specify an
0291: * arbitrary Hashtable implementation name.
0292: * <p>
0293: * Note that the correct way to ensure no memory leaks occur is to ensure
0294: * that LogFactory.release(contextClassLoader) is called whenever a
0295: * webapp is undeployed.
0296: */
0297: private static final Hashtable createFactoryStore() {
0298: Hashtable result = null;
0299: String storeImplementationClass = System
0300: .getProperty(HASHTABLE_IMPLEMENTATION_PROPERTY);
0301: if (storeImplementationClass == null) {
0302: storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
0303: }
0304: try {
0305: Class implementationClass = Class
0306: .forName(storeImplementationClass);
0307: result = (Hashtable) implementationClass.newInstance();
0308:
0309: } catch (Throwable t) {
0310: // ignore
0311: if (!WEAK_HASHTABLE_CLASSNAME
0312: .equals(storeImplementationClass)) {
0313: // if the user's trying to set up a custom implementation, give a clue
0314: if (isDiagnosticsEnabled()) {
0315: // use internal logging to issue the warning
0316: logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
0317: } else {
0318: // we *really* want this output, even if diagnostics weren't
0319: // explicitly enabled by the user.
0320: System.err
0321: .println("[ERROR] LogFactory: Load of custom hashtable failed");
0322: }
0323: }
0324: }
0325: if (result == null) {
0326: result = new Hashtable();
0327: }
0328: return result;
0329: }
0330:
0331: // --------------------------------------------------------- Static Methods
0332:
0333: /**
0334: * <p>Construct (if necessary) and return a <code>LogFactory</code>
0335: * instance, using the following ordered lookup procedure to determine
0336: * the name of the implementation class to be loaded.</p>
0337: * <ul>
0338: * <li>The <code>org.apache.commons.logging.LogFactory</code> system
0339: * property.</li>
0340: * <li>The JDK 1.3 Service Discovery mechanism</li>
0341: * <li>Use the properties file <code>commons-logging.properties</code>
0342: * file, if found in the class path of this class. The configuration
0343: * file is in standard <code>java.util.Properties</code> format and
0344: * contains the fully qualified name of the implementation class
0345: * with the key being the system property defined above.</li>
0346: * <li>Fall back to a default implementation class
0347: * (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
0348: * </ul>
0349: *
0350: * <p><em>NOTE</em> - If the properties file method of identifying the
0351: * <code>LogFactory</code> implementation class is utilized, all of the
0352: * properties defined in this file will be set as configuration attributes
0353: * on the corresponding <code>LogFactory</code> instance.</p>
0354: *
0355: * <p><em>NOTE</em> - In a multithreaded environment it is possible
0356: * that two different instances will be returned for the same
0357: * classloader environment.
0358: * </p>
0359: *
0360: * @exception LogConfigurationException if the implementation class is not
0361: * available or cannot be instantiated.
0362: */
0363: public static LogFactory getFactory()
0364: throws LogConfigurationException {
0365: // Identify the class loader we will be using
0366: ClassLoader contextClassLoader = getContextClassLoader();
0367:
0368: if (contextClassLoader == null) {
0369: // This is an odd enough situation to report about. This
0370: // output will be a nuisance on JDK1.1, as the system
0371: // classloader is null in that environment.
0372: if (isDiagnosticsEnabled()) {
0373: logDiagnostic("Context classloader is null.");
0374: }
0375: }
0376:
0377: // Return any previously registered factory for this class loader
0378: LogFactory factory = getCachedFactory(contextClassLoader);
0379: if (factory != null) {
0380: return factory;
0381: }
0382:
0383: if (isDiagnosticsEnabled()) {
0384: logDiagnostic("[LOOKUP] LogFactory implementation requested for the first time for context classloader "
0385: + objectId(contextClassLoader));
0386: logHierarchy("[LOOKUP] ", contextClassLoader);
0387: }
0388:
0389: // Load properties file.
0390: //
0391: // If the properties file exists, then its contents are used as
0392: // "attributes" on the LogFactory implementation class. One particular
0393: // property may also control which LogFactory concrete subclass is
0394: // used, but only if other discovery mechanisms fail..
0395: //
0396: // As the properties file (if it exists) will be used one way or
0397: // another in the end we may as well look for it first.
0398:
0399: Properties props = getConfigurationFile(contextClassLoader,
0400: FACTORY_PROPERTIES);
0401:
0402: // Determine whether we will be using the thread context class loader to
0403: // load logging classes or not by checking the loaded properties file (if any).
0404: ClassLoader baseClassLoader = contextClassLoader;
0405: if (props != null) {
0406: String useTCCLStr = props.getProperty(TCCL_KEY);
0407: if (useTCCLStr != null) {
0408: // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
0409: // is required for Java 1.2 compatability.
0410: if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
0411: // Don't use current context classloader when locating any
0412: // LogFactory or Log classes, just use the class that loaded
0413: // this abstract class. When this class is deployed in a shared
0414: // classpath of a container, it means webapps cannot deploy their
0415: // own logging implementations. It also means that it is up to the
0416: // implementation whether to load library-specific config files
0417: // from the TCCL or not.
0418: baseClassLoader = this ClassLoader;
0419: }
0420: }
0421: }
0422:
0423: // Determine which concrete LogFactory subclass to use.
0424: // First, try a global system property
0425: if (isDiagnosticsEnabled()) {
0426: logDiagnostic("[LOOKUP] Looking for system property ["
0427: + FACTORY_PROPERTY
0428: + "] to define the LogFactory subclass to use...");
0429: }
0430:
0431: try {
0432: String factoryClass = System.getProperty(FACTORY_PROPERTY);
0433: if (factoryClass != null) {
0434: if (isDiagnosticsEnabled()) {
0435: logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '"
0436: + factoryClass
0437: + "' as specified by system property "
0438: + FACTORY_PROPERTY);
0439: }
0440:
0441: factory = newFactory(factoryClass, baseClassLoader,
0442: contextClassLoader);
0443: } else {
0444: if (isDiagnosticsEnabled()) {
0445: logDiagnostic("[LOOKUP] No system property ["
0446: + FACTORY_PROPERTY + "] defined.");
0447: }
0448: }
0449: } catch (SecurityException e) {
0450: if (isDiagnosticsEnabled()) {
0451: logDiagnostic("[LOOKUP] A security exception occurred while trying to create an"
0452: + " instance of the custom factory class"
0453: + ": ["
0454: + e.getMessage().trim()
0455: + "]. Trying alternative implementations...");
0456: }
0457: ; // ignore
0458: } catch (RuntimeException e) {
0459: // This is not consistent with the behaviour when a bad LogFactory class is
0460: // specified in a services file.
0461: //
0462: // One possible exception that can occur here is a ClassCastException when
0463: // the specified class wasn't castable to this LogFactory type.
0464: if (isDiagnosticsEnabled()) {
0465: logDiagnostic("[LOOKUP] An exception occurred while trying to create an"
0466: + " instance of the custom factory class"
0467: + ": ["
0468: + e.getMessage().trim()
0469: + "] as specified by a system property.");
0470: }
0471: throw e;
0472: }
0473:
0474: // Second, try to find a service by using the JDK1.3 class
0475: // discovery mechanism, which involves putting a file with the name
0476: // of an interface class in the META-INF/services directory, where the
0477: // contents of the file is a single line specifying a concrete class
0478: // that implements the desired interface.
0479:
0480: if (factory == null) {
0481: if (isDiagnosticsEnabled()) {
0482: logDiagnostic("[LOOKUP] Looking for a resource file of name ["
0483: + SERVICE_ID
0484: + "] to define the LogFactory subclass to use...");
0485: }
0486: try {
0487: InputStream is = getResourceAsStream(
0488: contextClassLoader, SERVICE_ID);
0489:
0490: if (is != null) {
0491: // This code is needed by EBCDIC and other strange systems.
0492: // It's a fix for bugs reported in xerces
0493: BufferedReader rd;
0494: try {
0495: rd = new BufferedReader(new InputStreamReader(
0496: is, "UTF-8"));
0497: } catch (java.io.UnsupportedEncodingException e) {
0498: rd = new BufferedReader(new InputStreamReader(
0499: is));
0500: }
0501:
0502: String factoryClassName = rd.readLine();
0503: rd.close();
0504:
0505: if (factoryClassName != null
0506: && !"".equals(factoryClassName)) {
0507: if (isDiagnosticsEnabled()) {
0508: logDiagnostic("[LOOKUP] Creating an instance of LogFactory class "
0509: + factoryClassName
0510: + " as specified by file '"
0511: + SERVICE_ID
0512: + "' which was present in the path of the context"
0513: + " classloader.");
0514: }
0515: factory = newFactory(factoryClassName,
0516: baseClassLoader, contextClassLoader);
0517: }
0518: } else {
0519: // is == null
0520: if (isDiagnosticsEnabled()) {
0521: logDiagnostic("[LOOKUP] No resource file with name '"
0522: + SERVICE_ID + "' found.");
0523: }
0524: }
0525: } catch (Exception ex) {
0526: // note: if the specified LogFactory class wasn't compatible with LogFactory
0527: // for some reason, a ClassCastException will be caught here, and attempts will
0528: // continue to find a compatible class.
0529: if (isDiagnosticsEnabled()) {
0530: logDiagnostic("[LOOKUP] A security exception occurred while trying to create an"
0531: + " instance of the custom factory class"
0532: + ": ["
0533: + ex.getMessage().trim()
0534: + "]. Trying alternative implementations...");
0535: }
0536: ; // ignore
0537: }
0538: }
0539:
0540: // Third try looking into the properties file read earlier (if found)
0541:
0542: if (factory == null) {
0543: if (props != null) {
0544: if (isDiagnosticsEnabled()) {
0545: logDiagnostic("[LOOKUP] Looking in properties file for entry with key '"
0546: + FACTORY_PROPERTY
0547: + "' to define the LogFactory subclass to use...");
0548: }
0549: String factoryClass = props
0550: .getProperty(FACTORY_PROPERTY);
0551: if (factoryClass != null) {
0552: if (isDiagnosticsEnabled()) {
0553: logDiagnostic("[LOOKUP] Properties file specifies LogFactory subclass '"
0554: + factoryClass + "'");
0555: }
0556: factory = newFactory(factoryClass, baseClassLoader,
0557: contextClassLoader);
0558:
0559: // TODO: think about whether we need to handle exceptions from newFactory
0560: } else {
0561: if (isDiagnosticsEnabled()) {
0562: logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
0563: }
0564: }
0565: } else {
0566: if (isDiagnosticsEnabled()) {
0567: logDiagnostic("[LOOKUP] No properties file available to determine"
0568: + " LogFactory subclass from..");
0569: }
0570: }
0571: }
0572:
0573: // Fourth, try the fallback implementation class
0574:
0575: if (factory == null) {
0576: if (isDiagnosticsEnabled()) {
0577: logDiagnostic("[LOOKUP] Loading the default LogFactory implementation '"
0578: + FACTORY_DEFAULT
0579: + "' via the same classloader that loaded this LogFactory"
0580: + " class (ie not looking in the context classloader).");
0581: }
0582:
0583: // Note: unlike the above code which can try to load custom LogFactory
0584: // implementations via the TCCL, we don't try to load the default LogFactory
0585: // implementation via the context classloader because:
0586: // * that can cause problems (see comments in newFactory method)
0587: // * no-one should be customising the code of the default class
0588: // Yes, we do give up the ability for the child to ship a newer
0589: // version of the LogFactoryImpl class and have it used dynamically
0590: // by an old LogFactory class in the parent, but that isn't
0591: // necessarily a good idea anyway.
0592: factory = newFactory(FACTORY_DEFAULT, this ClassLoader,
0593: contextClassLoader);
0594: }
0595:
0596: if (factory != null) {
0597: /**
0598: * Always cache using context class loader.
0599: */
0600: cacheFactory(contextClassLoader, factory);
0601:
0602: if (props != null) {
0603: Enumeration names = props.propertyNames();
0604: while (names.hasMoreElements()) {
0605: String name = (String) names.nextElement();
0606: String value = props.getProperty(name);
0607: factory.setAttribute(name, value);
0608: }
0609: }
0610: }
0611:
0612: return factory;
0613: }
0614:
0615: /**
0616: * Convenience method to return a named logger, without the application
0617: * having to care about factories.
0618: *
0619: * @param clazz Class from which a log name will be derived
0620: *
0621: * @exception LogConfigurationException if a suitable <code>Log</code>
0622: * instance cannot be returned
0623: */
0624: public static Log getLog(Class clazz)
0625: throws LogConfigurationException {
0626:
0627: return (getFactory().getInstance(clazz));
0628:
0629: }
0630:
0631: /**
0632: * Convenience method to return a named logger, without the application
0633: * having to care about factories.
0634: *
0635: * @param name Logical name of the <code>Log</code> instance to be
0636: * returned (the meaning of this name is only known to the underlying
0637: * logging implementation that is being wrapped)
0638: *
0639: * @exception LogConfigurationException if a suitable <code>Log</code>
0640: * instance cannot be returned
0641: */
0642: public static Log getLog(String name)
0643: throws LogConfigurationException {
0644:
0645: return (getFactory().getInstance(name));
0646:
0647: }
0648:
0649: /**
0650: * Release any internal references to previously created {@link LogFactory}
0651: * instances that have been associated with the specified class loader
0652: * (if any), after calling the instance method <code>release()</code> on
0653: * each of them.
0654: *
0655: * @param classLoader ClassLoader for which to release the LogFactory
0656: */
0657: public static void release(ClassLoader classLoader) {
0658:
0659: if (isDiagnosticsEnabled()) {
0660: logDiagnostic("Releasing factory for classloader "
0661: + objectId(classLoader));
0662: }
0663: synchronized (factories) {
0664: if (classLoader == null) {
0665: if (nullClassLoaderFactory != null) {
0666: nullClassLoaderFactory.release();
0667: nullClassLoaderFactory = null;
0668: }
0669: } else {
0670: LogFactory factory = (LogFactory) factories
0671: .get(classLoader);
0672: if (factory != null) {
0673: factory.release();
0674: factories.remove(classLoader);
0675: }
0676: }
0677: }
0678:
0679: }
0680:
0681: /**
0682: * Release any internal references to previously created {@link LogFactory}
0683: * instances, after calling the instance method <code>release()</code> on
0684: * each of them. This is useful in environments like servlet containers,
0685: * which implement application reloading by throwing away a ClassLoader.
0686: * Dangling references to objects in that class loader would prevent
0687: * garbage collection.
0688: */
0689: public static void releaseAll() {
0690:
0691: if (isDiagnosticsEnabled()) {
0692: logDiagnostic("Releasing factory for all classloaders.");
0693: }
0694: synchronized (factories) {
0695: Enumeration elements = factories.elements();
0696: while (elements.hasMoreElements()) {
0697: LogFactory element = (LogFactory) elements
0698: .nextElement();
0699: element.release();
0700: }
0701: factories.clear();
0702:
0703: if (nullClassLoaderFactory != null) {
0704: nullClassLoaderFactory.release();
0705: nullClassLoaderFactory = null;
0706: }
0707: }
0708:
0709: }
0710:
0711: // ------------------------------------------------------ Protected Methods
0712:
0713: /**
0714: * Safely get access to the classloader for the specified class.
0715: * <p>
0716: * Theoretically, calling getClassLoader can throw a security exception,
0717: * and so should be done under an AccessController in order to provide
0718: * maximum flexibility. However in practice people don't appear to use
0719: * security policies that forbid getClassLoader calls. So for the moment
0720: * all code is written to call this method rather than Class.getClassLoader,
0721: * so that we could put AccessController stuff in this method without any
0722: * disruption later if we need to.
0723: * <p>
0724: * Even when using an AccessController, however, this method can still
0725: * throw SecurityException. Commons-logging basically relies on the
0726: * ability to access classloaders, ie a policy that forbids all
0727: * classloader access will also prevent commons-logging from working:
0728: * currently this method will throw an exception preventing the entire app
0729: * from starting up. Maybe it would be good to detect this situation and
0730: * just disable all commons-logging? Not high priority though - as stated
0731: * above, security policies that prevent classloader access aren't common.
0732: *
0733: * @since 1.1
0734: */
0735: protected static ClassLoader getClassLoader(Class clazz) {
0736: try {
0737: return clazz.getClassLoader();
0738: } catch (SecurityException ex) {
0739: if (isDiagnosticsEnabled()) {
0740: logDiagnostic("Unable to get classloader for class '"
0741: + clazz + "' due to security restrictions - "
0742: + ex.getMessage());
0743: }
0744: throw ex;
0745: }
0746: }
0747:
0748: /**
0749: * Calls LogFactory.directGetContextClassLoader under the control of an
0750: * AccessController class. This means that java code running under a
0751: * security manager that forbids access to ClassLoaders will still work
0752: * if this class is given appropriate privileges, even when the caller
0753: * doesn't have such privileges. Without using an AccessController, the
0754: * the entire call stack must have the privilege before the call is
0755: * allowed.
0756: *
0757: * @return the context classloader associated with the current thread,
0758: * or null if security doesn't allow it.
0759: *
0760: * @throws LogConfigurationException if there was some weird error while
0761: * attempting to get the context classloader.
0762: *
0763: * @throws SecurityException if the current java security policy doesn't
0764: * allow this class to access the context classloader.
0765: */
0766: protected static ClassLoader getContextClassLoader()
0767: throws LogConfigurationException {
0768:
0769: return (ClassLoader) AccessController
0770: .doPrivileged(new PrivilegedAction() {
0771: public Object run() {
0772: return directGetContextClassLoader();
0773: }
0774: });
0775: }
0776:
0777: /**
0778: * Return the thread context class loader if available; otherwise return
0779: * null.
0780: * <p>
0781: * Most/all code should call getContextClassLoader rather than calling
0782: * this method directly.
0783: * <p>
0784: * The thread context class loader is available for JDK 1.2
0785: * or later, if certain security conditions are met.
0786: * <p>
0787: * Note that no internal logging is done within this method because
0788: * this method is called every time LogFactory.getLogger() is called,
0789: * and we don't want too much output generated here.
0790: *
0791: * @exception LogConfigurationException if a suitable class loader
0792: * cannot be identified.
0793: *
0794: * @exception SecurityException if the java security policy forbids
0795: * access to the context classloader from one of the classes in the
0796: * current call stack.
0797: * @since 1.1
0798: */
0799: protected static ClassLoader directGetContextClassLoader()
0800: throws LogConfigurationException {
0801: ClassLoader classLoader = null;
0802:
0803: try {
0804: // Are we running on a JDK 1.2 or later system?
0805: Method method = Thread.class.getMethod(
0806: "getContextClassLoader", (Class[]) null);
0807:
0808: // Get the thread context class loader (if there is one)
0809: try {
0810: classLoader = (ClassLoader) method.invoke(Thread
0811: .currentThread(), (Object[]) null);
0812: } catch (IllegalAccessException e) {
0813: throw new LogConfigurationException(
0814: "Unexpected IllegalAccessException", e);
0815: } catch (InvocationTargetException e) {
0816: /**
0817: * InvocationTargetException is thrown by 'invoke' when
0818: * the method being invoked (getContextClassLoader) throws
0819: * an exception.
0820: *
0821: * getContextClassLoader() throws SecurityException when
0822: * the context class loader isn't an ancestor of the
0823: * calling class's class loader, or if security
0824: * permissions are restricted.
0825: *
0826: * In the first case (not related), we want to ignore and
0827: * keep going. We cannot help but also ignore the second
0828: * with the logic below, but other calls elsewhere (to
0829: * obtain a class loader) will trigger this exception where
0830: * we can make a distinction.
0831: */
0832: if (e.getTargetException() instanceof SecurityException) {
0833: ; // ignore
0834: } else {
0835: // Capture 'e.getTargetException()' exception for details
0836: // alternate: log 'e.getTargetException()', and pass back 'e'.
0837: throw new LogConfigurationException(
0838: "Unexpected InvocationTargetException", e
0839: .getTargetException());
0840: }
0841: }
0842: } catch (NoSuchMethodException e) {
0843: // Assume we are running on JDK 1.1
0844: classLoader = getClassLoader(LogFactory.class);
0845:
0846: // We deliberately don't log a message here to outputStream;
0847: // this message would be output for every call to LogFactory.getLog()
0848: // when running on JDK1.1
0849: //
0850: // if (outputStream != null) {
0851: // outputStream.println(
0852: // "Method Thread.getContextClassLoader does not exist;"
0853: // + " assuming this is JDK 1.1, and that the context"
0854: // + " classloader is the same as the class that loaded"
0855: // + " the concrete LogFactory class.");
0856: // }
0857:
0858: }
0859:
0860: // Return the selected class loader
0861: return classLoader;
0862: }
0863:
0864: /**
0865: * Check cached factories (keyed by contextClassLoader)
0866: *
0867: * @param contextClassLoader is the context classloader associated
0868: * with the current thread. This allows separate LogFactory objects
0869: * per component within a container, provided each component has
0870: * a distinct context classloader set. This parameter may be null
0871: * in JDK1.1, and in embedded systems where jcl-using code is
0872: * placed in the bootclasspath.
0873: *
0874: * @return the factory associated with the specified classloader if
0875: * one has previously been created, or null if this is the first time
0876: * we have seen this particular classloader.
0877: */
0878: private static LogFactory getCachedFactory(
0879: ClassLoader contextClassLoader) {
0880: LogFactory factory = null;
0881:
0882: if (contextClassLoader == null) {
0883: // We have to handle this specially, as factories is a Hashtable
0884: // and those don't accept null as a key value.
0885: //
0886: // nb: nullClassLoaderFactory might be null. That's ok.
0887: factory = nullClassLoaderFactory;
0888: } else {
0889: factory = (LogFactory) factories.get(contextClassLoader);
0890: }
0891:
0892: return factory;
0893: }
0894:
0895: /**
0896: * Remember this factory, so later calls to LogFactory.getCachedFactory
0897: * can return the previously created object (together with all its
0898: * cached Log objects).
0899: *
0900: * @param classLoader should be the current context classloader. Note that
0901: * this can be null under some circumstances; this is ok.
0902: *
0903: * @param factory should be the factory to cache. This should never be null.
0904: */
0905: private static void cacheFactory(ClassLoader classLoader,
0906: LogFactory factory) {
0907: // Ideally we would assert(factory != null) here. However reporting
0908: // errors from within a logging implementation is a little tricky!
0909:
0910: if (factory != null) {
0911: if (classLoader == null) {
0912: nullClassLoaderFactory = factory;
0913: } else {
0914: factories.put(classLoader, factory);
0915: }
0916: }
0917: }
0918:
0919: /**
0920: * Return a new instance of the specified <code>LogFactory</code>
0921: * implementation class, loaded by the specified class loader.
0922: * If that fails, try the class loader used to load this
0923: * (abstract) LogFactory.
0924: * <p>
0925: * <h2>ClassLoader conflicts</h2>
0926: * Note that there can be problems if the specified ClassLoader is not the
0927: * same as the classloader that loaded this class, ie when loading a
0928: * concrete LogFactory subclass via a context classloader.
0929: * <p>
0930: * The problem is the same one that can occur when loading a concrete Log
0931: * subclass via a context classloader.
0932: * <p>
0933: * The problem occurs when code running in the context classloader calls
0934: * class X which was loaded via a parent classloader, and class X then calls
0935: * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
0936: * class X was loaded via the parent, it binds to LogFactory loaded via
0937: * the parent. When the code in this method finds some LogFactoryYYYY
0938: * class in the child (context) classloader, and there also happens to be a
0939: * LogFactory class defined in the child classloader, then LogFactoryYYYY
0940: * will be bound to LogFactory@childloader. It cannot be cast to
0941: * LogFactory@parentloader, ie this method cannot return the object as
0942: * the desired type. Note that it doesn't matter if the LogFactory class
0943: * in the child classloader is identical to the LogFactory class in the
0944: * parent classloader, they are not compatible.
0945: * <p>
0946: * The solution taken here is to simply print out an error message when
0947: * this occurs then throw an exception. The deployer of the application
0948: * must ensure they remove all occurrences of the LogFactory class from
0949: * the child classloader in order to resolve the issue. Note that they
0950: * do not have to move the custom LogFactory subclass; that is ok as
0951: * long as the only LogFactory class it can find to bind to is in the
0952: * parent classloader.
0953: * <p>
0954: * @param factoryClass Fully qualified name of the <code>LogFactory</code>
0955: * implementation class
0956: * @param classLoader ClassLoader from which to load this class
0957: * @param contextClassLoader is the context that this new factory will
0958: * manage logging for.
0959: *
0960: * @exception LogConfigurationException if a suitable instance
0961: * cannot be created
0962: * @since 1.1
0963: */
0964: protected static LogFactory newFactory(final String factoryClass,
0965: final ClassLoader classLoader,
0966: final ClassLoader contextClassLoader)
0967: throws LogConfigurationException {
0968: // Note that any unchecked exceptions thrown by the createFactory
0969: // method will propagate out of this method; in particular a
0970: // ClassCastException can be thrown.
0971: Object result = AccessController
0972: .doPrivileged(new PrivilegedAction() {
0973: public Object run() {
0974: return createFactory(factoryClass, classLoader);
0975: }
0976: });
0977:
0978: if (result instanceof LogConfigurationException) {
0979: LogConfigurationException ex = (LogConfigurationException) result;
0980: if (isDiagnosticsEnabled()) {
0981: logDiagnostic("An error occurred while loading the factory class:"
0982: + ex.getMessage());
0983: }
0984: throw ex;
0985: }
0986: if (isDiagnosticsEnabled()) {
0987: logDiagnostic("Created object " + objectId(result)
0988: + " to manage classloader "
0989: + objectId(contextClassLoader));
0990: }
0991: return (LogFactory) result;
0992: }
0993:
0994: /**
0995: * Method provided for backwards compatibility; see newFactory version that
0996: * takes 3 parameters.
0997: * <p>
0998: * This method would only ever be called in some rather odd situation.
0999: * Note that this method is static, so overriding in a subclass doesn't
1000: * have any effect unless this method is called from a method in that
1001: * subclass. However this method only makes sense to use from the
1002: * getFactory method, and as that is almost always invoked via
1003: * LogFactory.getFactory, any custom definition in a subclass would be
1004: * pointless. Only a class with a custom getFactory method, then invoked
1005: * directly via CustomFactoryImpl.getFactory or similar would ever call
1006: * this. Anyway, it's here just in case, though the "managed class loader"
1007: * value output to the diagnostics will not report the correct value.
1008: */
1009: protected static LogFactory newFactory(final String factoryClass,
1010: final ClassLoader classLoader) {
1011: return newFactory(factoryClass, classLoader, null);
1012: }
1013:
1014: /**
1015: * Implements the operations described in the javadoc for newFactory.
1016: *
1017: * @param factoryClass
1018: *
1019: * @param classLoader used to load the specified factory class. This is
1020: * expected to be either the TCCL or the classloader which loaded this
1021: * class. Note that the classloader which loaded this class might be
1022: * "null" (ie the bootloader) for embedded systems.
1023: *
1024: * @return either a LogFactory object or a LogConfigurationException object.
1025: * @since 1.1
1026: */
1027: protected static Object createFactory(String factoryClass,
1028: ClassLoader classLoader) {
1029:
1030: // This will be used to diagnose bad configurations
1031: // and allow a useful message to be sent to the user
1032: Class logFactoryClass = null;
1033: try {
1034: if (classLoader != null) {
1035: try {
1036: // First the given class loader param (thread class loader)
1037:
1038: // Warning: must typecast here & allow exception
1039: // to be generated/caught & recast properly.
1040: logFactoryClass = classLoader
1041: .loadClass(factoryClass);
1042: if (LogFactory.class
1043: .isAssignableFrom(logFactoryClass)) {
1044: if (isDiagnosticsEnabled()) {
1045: logDiagnostic("Loaded class "
1046: + logFactoryClass.getName()
1047: + " from classloader "
1048: + objectId(classLoader));
1049: }
1050: } else {
1051: //
1052: // This indicates a problem with the ClassLoader tree.
1053: // An incompatible ClassLoader was used to load the
1054: // implementation.
1055: // As the same classes
1056: // must be available in multiple class loaders,
1057: // it is very likely that multiple JCL jars are present.
1058: // The most likely fix for this
1059: // problem is to remove the extra JCL jars from the
1060: // ClassLoader hierarchy.
1061: //
1062: if (isDiagnosticsEnabled()) {
1063: logDiagnostic("Factory class "
1064: + logFactoryClass.getName()
1065: + " loaded from classloader "
1066: + objectId(logFactoryClass
1067: .getClassLoader())
1068: + " does not extend '"
1069: + LogFactory.class.getName()
1070: + "' as loaded by this classloader.");
1071: logHierarchy("[BAD CL TREE] ", classLoader);
1072: }
1073: }
1074:
1075: return (LogFactory) logFactoryClass.newInstance();
1076:
1077: } catch (ClassNotFoundException ex) {
1078: if (classLoader == this ClassLoader) {
1079: // Nothing more to try, onwards.
1080: if (isDiagnosticsEnabled()) {
1081: logDiagnostic("Unable to locate any class called '"
1082: + factoryClass
1083: + "' via classloader "
1084: + objectId(classLoader));
1085: }
1086: throw ex;
1087: }
1088: // ignore exception, continue
1089: } catch (NoClassDefFoundError e) {
1090: if (classLoader == this ClassLoader) {
1091: // Nothing more to try, onwards.
1092: if (isDiagnosticsEnabled()) {
1093: logDiagnostic("Class '"
1094: + factoryClass
1095: + "' cannot be loaded"
1096: + " via classloader "
1097: + objectId(classLoader)
1098: + " - it depends on some other class that cannot"
1099: + " be found.");
1100: }
1101: throw e;
1102: }
1103: // ignore exception, continue
1104: } catch (ClassCastException e) {
1105: if (classLoader == this ClassLoader) {
1106: // There's no point in falling through to the code below that
1107: // tries again with thisClassLoader, because we've just tried
1108: // loading with that loader (not the TCCL). Just throw an
1109: // appropriate exception here.
1110:
1111: final boolean implements LogFactory = implements LogFactory(logFactoryClass);
1112:
1113: //
1114: // Construct a good message: users may not actual expect that a custom implementation
1115: // has been specified. Several well known containers use this mechanism to adapt JCL
1116: // to their native logging system.
1117: //
1118: String msg = "The application has specified that a custom LogFactory implementation should be used but "
1119: + "Class '"
1120: + factoryClass
1121: + "' cannot be converted to '"
1122: + LogFactory.class.getName() + "'. ";
1123: if (implements LogFactory) {
1124: msg = msg
1125: + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. "
1126: + "Background can be found in http://jakarta.apache.org/commons/logging/tech.html. "
1127: + "If you have not explicitly specified a custom LogFactory then it is likely that "
1128: + "the container has set one without your knowledge. "
1129: + "In this case, consider using the commons-logging-adapters.jar file or "
1130: + "specifying the standard LogFactory from the command line. ";
1131: } else {
1132: msg = msg
1133: + "Please check the custom implementation. ";
1134: }
1135: msg = msg
1136: + "Help can be found @http://jakarta.apache.org/commons/logging/troubleshooting.html.";
1137:
1138: if (isDiagnosticsEnabled()) {
1139: logDiagnostic(msg);
1140: }
1141:
1142: ClassCastException ex = new ClassCastException(
1143: msg);
1144: throw ex;
1145: }
1146:
1147: // Ignore exception, continue. Presumably the classloader was the
1148: // TCCL; the code below will try to load the class via thisClassLoader.
1149: // This will handle the case where the original calling class is in
1150: // a shared classpath but the TCCL has a copy of LogFactory and the
1151: // specified LogFactory implementation; we will fall back to using the
1152: // LogFactory implementation from the same classloader as this class.
1153: //
1154: // Issue: this doesn't handle the reverse case, where this LogFactory
1155: // is in the webapp, and the specified LogFactory implementation is
1156: // in a shared classpath. In that case:
1157: // (a) the class really does implement LogFactory (bad log msg above)
1158: // (b) the fallback code will result in exactly the same problem.
1159: }
1160: }
1161:
1162: /* At this point, either classLoader == null, OR
1163: * classLoader was unable to load factoryClass.
1164: *
1165: * In either case, we call Class.forName, which is equivalent
1166: * to LogFactory.class.getClassLoader().load(name), ie we ignore
1167: * the classloader parameter the caller passed, and fall back
1168: * to trying the classloader associated with this class. See the
1169: * javadoc for the newFactory method for more info on the
1170: * consequences of this.
1171: *
1172: * Notes:
1173: * * LogFactory.class.getClassLoader() may return 'null'
1174: * if LogFactory is loaded by the bootstrap classloader.
1175: */
1176: // Warning: must typecast here & allow exception
1177: // to be generated/caught & recast properly.
1178: if (isDiagnosticsEnabled()) {
1179: logDiagnostic("Unable to load factory class via classloader "
1180: + objectId(classLoader)
1181: + " - trying the classloader associated with this LogFactory.");
1182: }
1183: logFactoryClass = Class.forName(factoryClass);
1184: return (LogFactory) logFactoryClass.newInstance();
1185: } catch (Exception e) {
1186: // Check to see if we've got a bad configuration
1187: if (isDiagnosticsEnabled()) {
1188: logDiagnostic("Unable to create LogFactory instance.");
1189: }
1190: if (logFactoryClass != null
1191: && !LogFactory.class
1192: .isAssignableFrom(logFactoryClass)) {
1193:
1194: return new LogConfigurationException(
1195: "The chosen LogFactory implementation does not extend LogFactory."
1196: + " Please check your configuration.",
1197: e);
1198: }
1199: return new LogConfigurationException(e);
1200: }
1201: }
1202:
1203: /**
1204: * Determines whether the given class actually implements <code>LogFactory</code>.
1205: * Diagnostic information is also logged.
1206: * <p>
1207: * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
1208: * of incompatibility. The test used is whether the class is assignable from
1209: * the <code>LogFactory</code> class loaded by the class's classloader.
1210: * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
1211: * @return true if the <code>logFactoryClass</code> does extend
1212: * <code>LogFactory</code> when that class is loaded via the same
1213: * classloader that loaded the <code>logFactoryClass</code>.
1214: */
1215: private static boolean implements LogFactory(Class logFactoryClass) {
1216: boolean implements LogFactory = false;
1217: if (logFactoryClass != null) {
1218: try {
1219: ClassLoader logFactoryClassLoader = logFactoryClass
1220: .getClassLoader();
1221: if (logFactoryClassLoader == null) {
1222: logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
1223: } else {
1224: logHierarchy("[CUSTOM LOG FACTORY] ",
1225: logFactoryClassLoader);
1226: Class factoryFromCustomLoader = Class.forName(
1227: "org.apache.commons.logging.LogFactory",
1228: false, logFactoryClassLoader);
1229: implements LogFactory = factoryFromCustomLoader
1230: .isAssignableFrom(logFactoryClass);
1231: if (implements LogFactory) {
1232: logDiagnostic("[CUSTOM LOG FACTORY] "
1233: + logFactoryClass.getName()
1234: + " implements LogFactory but was loaded by an incompatible classloader.");
1235: } else {
1236: logDiagnostic("[CUSTOM LOG FACTORY] "
1237: + logFactoryClass.getName()
1238: + " does not implement LogFactory.");
1239: }
1240: }
1241: } catch (SecurityException e) {
1242: //
1243: // The application is running within a hostile security environment.
1244: // This will make it very hard to diagnose issues with JCL.
1245: // Consider running less securely whilst debugging this issue.
1246: //
1247: logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether "
1248: + "the compatibility was caused by a classloader conflict: "
1249: + e.getMessage());
1250: } catch (LinkageError e) {
1251: //
1252: // This should be an unusual circumstance.
1253: // LinkageError's usually indicate that a dependent class has incompatibly changed.
1254: // Another possibility may be an exception thrown by an initializer.
1255: // Time for a clean rebuild?
1256: //
1257: logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether "
1258: + "the compatibility was caused by a classloader conflict: "
1259: + e.getMessage());
1260: } catch (ClassNotFoundException e) {
1261: //
1262: // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation.
1263: // The custom implementation is not viable until this is corrected.
1264: // Ensure that the JCL jar and the custom class are available from the same classloader.
1265: // Running with diagnostics on should give information about the classloaders used
1266: // to load the custom factory.
1267: //
1268: logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the "
1269: + "custom LogFactory implementation. Is the custom factory in the right classloader?");
1270: }
1271: }
1272: return implements LogFactory;
1273: }
1274:
1275: /**
1276: * Applets may run in an environment where accessing resources of a loader is
1277: * a secure operation, but where the commons-logging library has explicitly
1278: * been granted permission for that operation. In this case, we need to
1279: * run the operation using an AccessController.
1280: */
1281: private static InputStream getResourceAsStream(
1282: final ClassLoader loader, final String name) {
1283: return (InputStream) AccessController
1284: .doPrivileged(new PrivilegedAction() {
1285: public Object run() {
1286: if (loader != null) {
1287: return loader.getResourceAsStream(name);
1288: } else {
1289: return ClassLoader
1290: .getSystemResourceAsStream(name);
1291: }
1292: }
1293: });
1294: }
1295:
1296: /**
1297: * Given a filename, return an enumeration of URLs pointing to
1298: * all the occurrences of that filename in the classpath.
1299: * <p>
1300: * This is just like ClassLoader.getResources except that the
1301: * operation is done under an AccessController so that this method will
1302: * succeed when this jarfile is privileged but the caller is not.
1303: * This method must therefore remain private to avoid security issues.
1304: * <p>
1305: * If no instances are found, an Enumeration is returned whose
1306: * hasMoreElements method returns false (ie an "empty" enumeration).
1307: * If resources could not be listed for some reason, null is returned.
1308: */
1309: private static Enumeration getResources(final ClassLoader loader,
1310: final String name) {
1311: PrivilegedAction action = new PrivilegedAction() {
1312: public Object run() {
1313: try {
1314: if (loader != null) {
1315: return loader.getResources(name);
1316: } else {
1317: return ClassLoader.getSystemResources(name);
1318: }
1319: } catch (IOException e) {
1320: if (isDiagnosticsEnabled()) {
1321: logDiagnostic("Exception while trying to find configuration file "
1322: + name + ":" + e.getMessage());
1323: }
1324: return null;
1325: } catch (NoSuchMethodError e) {
1326: // we must be running on a 1.1 JVM which doesn't support
1327: // ClassLoader.getSystemResources; just return null in
1328: // this case.
1329: return null;
1330: }
1331: }
1332: };
1333: Object result = AccessController.doPrivileged(action);
1334: return (Enumeration) result;
1335: }
1336:
1337: /**
1338: * Given a URL that refers to a .properties file, load that file.
1339: * This is done under an AccessController so that this method will
1340: * succeed when this jarfile is privileged but the caller is not.
1341: * This method must therefore remain private to avoid security issues.
1342: * <p>
1343: * Null is returned if the URL cannot be opened.
1344: */
1345: private static Properties getProperties(final URL url) {
1346: PrivilegedAction action = new PrivilegedAction() {
1347: public Object run() {
1348: try {
1349: InputStream stream = url.openStream();
1350: if (stream != null) {
1351: Properties props = new Properties();
1352: props.load(stream);
1353: stream.close();
1354: return props;
1355: }
1356: } catch (IOException e) {
1357: if (isDiagnosticsEnabled()) {
1358: logDiagnostic("Unable to read URL " + url);
1359: }
1360: }
1361:
1362: return null;
1363: }
1364: };
1365: return (Properties) AccessController.doPrivileged(action);
1366: }
1367:
1368: /**
1369: * Locate a user-provided configuration file.
1370: * <p>
1371: * The classpath of the specified classLoader (usually the context classloader)
1372: * is searched for properties files of the specified name. If none is found,
1373: * null is returned. If more than one is found, then the file with the greatest
1374: * value for its PRIORITY property is returned. If multiple files have the
1375: * same PRIORITY value then the first in the classpath is returned.
1376: * <p>
1377: * This differs from the 1.0.x releases; those always use the first one found.
1378: * However as the priority is a new field, this change is backwards compatible.
1379: * <p>
1380: * The purpose of the priority field is to allow a webserver administrator to
1381: * override logging settings in all webapps by placing a commons-logging.properties
1382: * file in a shared classpath location with a priority > 0; this overrides any
1383: * commons-logging.properties files without priorities which are in the
1384: * webapps. Webapps can also use explicit priorities to override a configuration
1385: * file in the shared classpath if needed.
1386: */
1387: private static final Properties getConfigurationFile(
1388: ClassLoader classLoader, String fileName) {
1389:
1390: Properties props = null;
1391: double priority = 0.0;
1392: URL propsUrl = null;
1393: try {
1394: Enumeration urls = getResources(classLoader, fileName);
1395:
1396: if (urls == null) {
1397: return null;
1398: }
1399:
1400: while (urls.hasMoreElements()) {
1401: URL url = (URL) urls.nextElement();
1402:
1403: Properties newProps = getProperties(url);
1404: if (newProps != null) {
1405: if (props == null) {
1406: propsUrl = url;
1407: props = newProps;
1408: String priorityStr = props
1409: .getProperty(PRIORITY_KEY);
1410: priority = 0.0;
1411: if (priorityStr != null) {
1412: priority = Double.parseDouble(priorityStr);
1413: }
1414:
1415: if (isDiagnosticsEnabled()) {
1416: logDiagnostic("[LOOKUP] Properties file found at '"
1417: + url
1418: + "'"
1419: + " with priority "
1420: + priority);
1421: }
1422: } else {
1423: String newPriorityStr = newProps
1424: .getProperty(PRIORITY_KEY);
1425: double newPriority = 0.0;
1426: if (newPriorityStr != null) {
1427: newPriority = Double
1428: .parseDouble(newPriorityStr);
1429: }
1430:
1431: if (newPriority > priority) {
1432: if (isDiagnosticsEnabled()) {
1433: logDiagnostic("[LOOKUP] Properties file at '"
1434: + url
1435: + "'"
1436: + " with priority "
1437: + newPriority
1438: + " overrides file at '"
1439: + propsUrl
1440: + "'"
1441: + " with priority " + priority);
1442: }
1443:
1444: propsUrl = url;
1445: props = newProps;
1446: priority = newPriority;
1447: } else {
1448: if (isDiagnosticsEnabled()) {
1449: logDiagnostic("[LOOKUP] Properties file at '"
1450: + url
1451: + "'"
1452: + " with priority "
1453: + newPriority
1454: + " does not override file at '"
1455: + propsUrl
1456: + "'"
1457: + " with priority " + priority);
1458: }
1459: }
1460: }
1461:
1462: }
1463: }
1464: } catch (SecurityException e) {
1465: if (isDiagnosticsEnabled()) {
1466: logDiagnostic("SecurityException thrown while trying to find/read config files.");
1467: }
1468: }
1469:
1470: if (isDiagnosticsEnabled()) {
1471: if (props == null) {
1472: logDiagnostic("[LOOKUP] No properties file of name '"
1473: + fileName + "' found.");
1474: } else {
1475: logDiagnostic("[LOOKUP] Properties file of name '"
1476: + fileName + "' found at '" + propsUrl + '"');
1477: }
1478: }
1479:
1480: return props;
1481: }
1482:
1483: /**
1484: * Determines whether the user wants internal diagnostic output. If so,
1485: * returns an appropriate writer object. Users can enable diagnostic
1486: * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
1487: * a filename, or the special values STDOUT or STDERR.
1488: */
1489: private static void initDiagnostics() {
1490: String dest;
1491: try {
1492: dest = System.getProperty(DIAGNOSTICS_DEST_PROPERTY);
1493: if (dest == null) {
1494: return;
1495: }
1496: } catch (SecurityException ex) {
1497: // We must be running in some very secure environment.
1498: // We just have to assume output is not wanted..
1499: return;
1500: }
1501:
1502: if (dest.equals("STDOUT")) {
1503: diagnosticsStream = System.out;
1504: } else if (dest.equals("STDERR")) {
1505: diagnosticsStream = System.err;
1506: } else {
1507: try {
1508: // open the file in append mode
1509: FileOutputStream fos = new FileOutputStream(dest, true);
1510: diagnosticsStream = new PrintStream(fos);
1511: } catch (IOException ex) {
1512: // We should report this to the user - but how?
1513: return;
1514: }
1515: }
1516:
1517: // In order to avoid confusion where multiple instances of JCL are
1518: // being used via different classloaders within the same app, we
1519: // ensure each logged message has a prefix of form
1520: // [LogFactory from classloader OID]
1521: //
1522: // Note that this prefix should be kept consistent with that
1523: // in LogFactoryImpl. However here we don't need to output info
1524: // about the actual *instance* of LogFactory, as all methods that
1525: // output diagnostics from this class are static.
1526: String classLoaderName;
1527: try {
1528: ClassLoader classLoader = this ClassLoader;
1529: if (this ClassLoader == null) {
1530: classLoaderName = "BOOTLOADER";
1531: } else {
1532: classLoaderName = objectId(classLoader);
1533: }
1534: } catch (SecurityException e) {
1535: classLoaderName = "UNKNOWN";
1536: }
1537: diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
1538: }
1539:
1540: /**
1541: * Indicates true if the user has enabled internal logging.
1542: * <p>
1543: * By the way, sorry for the incorrect grammar, but calling this method
1544: * areDiagnosticsEnabled just isn't java beans style.
1545: *
1546: * @return true if calls to logDiagnostic will have any effect.
1547: * @since 1.1
1548: */
1549: protected static boolean isDiagnosticsEnabled() {
1550: return diagnosticsStream != null;
1551: }
1552:
1553: /**
1554: * Write the specified message to the internal logging destination.
1555: * <p>
1556: * Note that this method is private; concrete subclasses of this class
1557: * should not call it because the diagnosticPrefix string this
1558: * method puts in front of all its messages is LogFactory@....,
1559: * while subclasses should put SomeSubClass@...
1560: * <p>
1561: * Subclasses should instead compute their own prefix, then call
1562: * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
1563: * fine for subclasses.
1564: * <p>
1565: * Note that it is safe to call this method before initDiagnostics
1566: * is called; any output will just be ignored (as isDiagnosticsEnabled
1567: * will return false).
1568: *
1569: * @param msg is the diagnostic message to be output.
1570: */
1571: private static final void logDiagnostic(String msg) {
1572: if (diagnosticsStream != null) {
1573: diagnosticsStream.print(diagnosticPrefix);
1574: diagnosticsStream.println(msg);
1575: diagnosticsStream.flush();
1576: }
1577: }
1578:
1579: /**
1580: * Write the specified message to the internal logging destination.
1581: *
1582: * @param msg is the diagnostic message to be output.
1583: * @since 1.1
1584: */
1585: protected static final void logRawDiagnostic(String msg) {
1586: if (diagnosticsStream != null) {
1587: diagnosticsStream.println(msg);
1588: diagnosticsStream.flush();
1589: }
1590: }
1591:
1592: /**
1593: * Generate useful diagnostics regarding the classloader tree for
1594: * the specified class.
1595: * <p>
1596: * As an example, if the specified class was loaded via a webapp's
1597: * classloader, then you may get the following output:
1598: * <pre>
1599: * Class com.acme.Foo was loaded via classloader 11111
1600: * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT
1601: * </pre>
1602: * <p>
1603: * This method returns immediately if isDiagnosticsEnabled()
1604: * returns false.
1605: *
1606: * @param clazz is the class whose classloader + tree are to be
1607: * output.
1608: */
1609: private static void logClassLoaderEnvironment(Class clazz) {
1610: if (!isDiagnosticsEnabled()) {
1611: return;
1612: }
1613:
1614: try {
1615: logDiagnostic("[ENV] Extension directories (java.ext.dir): "
1616: + System.getProperty("java.ext.dir"));
1617: logDiagnostic("[ENV] Application classpath (java.class.path): "
1618: + System.getProperty("java.class.path"));
1619: } catch (SecurityException ex) {
1620: logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
1621: }
1622:
1623: String className = clazz.getName();
1624: ClassLoader classLoader;
1625:
1626: try {
1627: classLoader = getClassLoader(clazz);
1628: } catch (SecurityException ex) {
1629: // not much useful diagnostics we can print here!
1630: logDiagnostic("[ENV] Security forbids determining the classloader for "
1631: + className);
1632: return;
1633: }
1634:
1635: logDiagnostic("[ENV] Class " + className
1636: + " was loaded via classloader "
1637: + objectId(classLoader));
1638: logHierarchy("[ENV] Ancestry of classloader which loaded "
1639: + className + " is ", classLoader);
1640: }
1641:
1642: /**
1643: * Logs diagnostic messages about the given classloader
1644: * and it's hierarchy. The prefix is prepended to the message
1645: * and is intended to make it easier to understand the logs.
1646: * @param prefix
1647: * @param classLoader
1648: */
1649: private static void logHierarchy(String prefix,
1650: ClassLoader classLoader) {
1651: if (!isDiagnosticsEnabled()) {
1652: return;
1653: }
1654: ClassLoader systemClassLoader;
1655: if (classLoader != null) {
1656: final String classLoaderString = classLoader.toString();
1657: logDiagnostic(prefix + objectId(classLoader) + " == '"
1658: + classLoaderString + "'");
1659: }
1660:
1661: try {
1662: systemClassLoader = ClassLoader.getSystemClassLoader();
1663: } catch (SecurityException ex) {
1664: logDiagnostic(prefix
1665: + "Security forbids determining the system classloader.");
1666: return;
1667: }
1668: if (classLoader != null) {
1669: StringBuffer buf = new StringBuffer(prefix
1670: + "ClassLoader tree:");
1671: for (;;) {
1672: buf.append(objectId(classLoader));
1673: if (classLoader == systemClassLoader) {
1674: buf.append(" (SYSTEM) ");
1675: }
1676:
1677: try {
1678: classLoader = classLoader.getParent();
1679: } catch (SecurityException ex) {
1680: buf.append(" --> SECRET");
1681: break;
1682: }
1683:
1684: buf.append(" --> ");
1685: if (classLoader == null) {
1686: buf.append("BOOT");
1687: break;
1688: }
1689: }
1690: logDiagnostic(buf.toString());
1691: }
1692: }
1693:
1694: /**
1695: * Returns a string that uniquely identifies the specified object, including
1696: * its class.
1697: * <p>
1698: * The returned string is of form "classname@hashcode", ie is the same as
1699: * the return value of the Object.toString() method, but works even when
1700: * the specified object's class has overidden the toString method.
1701: *
1702: * @param o may be null.
1703: * @return a string of form classname@hashcode, or "null" if param o is null.
1704: * @since 1.1
1705: */
1706: public static String objectId(Object o) {
1707: if (o == null) {
1708: return "null";
1709: } else {
1710: return o.getClass().getName() + "@"
1711: + System.identityHashCode(o);
1712: }
1713: }
1714:
1715: // ----------------------------------------------------------------------
1716: // Static initialiser block to perform initialisation at class load time.
1717: //
1718: // We can't do this in the class constructor, as there are many
1719: // static methods on this class that can be called before any
1720: // LogFactory instances are created, and they depend upon this
1721: // stuff having been set up.
1722: //
1723: // Note that this block must come after any variable declarations used
1724: // by any methods called from this block, as we want any static initialiser
1725: // associated with the variable to run first. If static initialisers for
1726: // variables run after this code, then (a) their value might be needed
1727: // by methods called from here, and (b) they might *override* any value
1728: // computed here!
1729: //
1730: // So the wisest thing to do is just to place this code at the very end
1731: // of the class file.
1732: // ----------------------------------------------------------------------
1733:
1734: static {
1735: // note: it's safe to call methods before initDiagnostics.
1736: this ClassLoader = getClassLoader(LogFactory.class);
1737: initDiagnostics();
1738: logClassLoaderEnvironment(LogFactory.class);
1739: factories = createFactoryStore();
1740: if (isDiagnosticsEnabled()) {
1741: logDiagnostic("BOOTSTRAP COMPLETED");
1742: }
1743: }
1744: }
|