LogFactory.java revision 069490a5ca2fd1988d29daf45d892f47ad665115
1/*
2 * Copyright 2001-2006 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.apache.commons.logging;
18
19
20import java.io.BufferedReader;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.InputStreamReader;
25import java.io.PrintStream;
26import java.lang.reflect.InvocationTargetException;
27import java.lang.reflect.Method;
28import java.net.URL;
29import java.security.AccessController;
30import java.security.PrivilegedAction;
31import java.util.Enumeration;
32import java.util.Hashtable;
33import java.util.Properties;
34
35
36/**
37 * <p>Factory for creating {@link Log} instances, with discovery and
38 * configuration features similar to that employed by standard Java APIs
39 * such as JAXP.</p>
40 *
41 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
42 * based on the SAXParserFactory and DocumentBuilderFactory implementations
43 * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
44 *
45 * @author Craig R. McClanahan
46 * @author Costin Manolache
47 * @author Richard A. Sitze
48 * @version $Revision: 399431 $ $Date: 2006-05-03 21:58:34 +0100 (Wed, 03 May 2006) $
49 */
50
51public abstract class LogFactory {
52
53
54    // ----------------------------------------------------- Manifest Constants
55
56    /**
57     * The name (<code>priority</code>) of the key in the config file used to
58     * specify the priority of that particular config file. The associated value
59     * is a floating-point number; higher values take priority over lower values.
60     */
61    public static final String PRIORITY_KEY = "priority";
62
63    /**
64     * The name (<code>use_tccl</code>) of the key in the config file used
65     * to specify whether logging classes should be loaded via the thread
66     * context class loader (TCCL), or not. By default, the TCCL is used.
67     */
68    public static final String TCCL_KEY = "use_tccl";
69
70    /**
71     * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property
72     * used to identify the LogFactory implementation
73     * class name. This can be used as a system property, or as an entry in a
74     * configuration properties file.
75     */
76    public static final String FACTORY_PROPERTY =
77        "org.apache.commons.logging.LogFactory";
78
79    /**
80     * The fully qualified class name of the fallback <code>LogFactory</code>
81     * implementation class to use, if no other can be found.
82     */
83    public static final String FACTORY_DEFAULT =
84        "org.apache.commons.logging.impl.LogFactoryImpl";
85
86    /**
87     * The name (<code>commons-logging.properties</code>) of the properties file to search for.
88     */
89    public static final String FACTORY_PROPERTIES =
90        "commons-logging.properties";
91
92    /**
93     * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
94     * 'Service Provider' specification</a>.
95     *
96     */
97    protected static final String SERVICE_ID =
98        "META-INF/services/org.apache.commons.logging.LogFactory";
99
100    /**
101     * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
102     * of the property used to enable internal commons-logging
103     * diagnostic output, in order to get information on what logging
104     * implementations are being discovered, what classloaders they
105     * are loaded through, etc.
106     * <p>
107     * If a system property of this name is set then the value is
108     * assumed to be the name of a file. The special strings
109     * STDOUT or STDERR (case-sensitive) indicate output to
110     * System.out and System.err respectively.
111     * <p>
112     * Diagnostic logging should be used only to debug problematic
113     * configurations and should not be set in normal production use.
114     */
115    public static final String DIAGNOSTICS_DEST_PROPERTY =
116        "org.apache.commons.logging.diagnostics.dest";
117
118    /**
119     * When null (the usual case), no diagnostic output will be
120     * generated by LogFactory or LogFactoryImpl. When non-null,
121     * interesting events will be written to the specified object.
122     */
123    private static PrintStream diagnosticsStream = null;
124
125    /**
126     * A string that gets prefixed to every message output by the
127     * logDiagnostic method, so that users can clearly see which
128     * LogFactory class is generating the output.
129     */
130    private static String diagnosticPrefix;
131
132    /**
133     * <p>Setting this system property
134     * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>)
135     * value allows the <code>Hashtable</code> used to store
136     * classloaders to be substituted by an alternative implementation.
137     * </p>
138     * <p>
139     * <strong>Note:</strong> <code>LogFactory</code> will print:
140     * <code><pre>
141     * [ERROR] LogFactory: Load of custom hashtable failed</em>
142     * </pre></code>
143     * to system error and then continue using a standard Hashtable.
144     * </p>
145     * <p>
146     * <strong>Usage:</strong> Set this property when Java is invoked
147     * and <code>LogFactory</code> will attempt to load a new instance
148     * of the given implementation class.
149     * For example, running the following ant scriplet:
150     * <code><pre>
151     *  &lt;java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"&gt;
152     *     ...
153     *     &lt;sysproperty
154     *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
155     *        value="org.apache.commons.logging.AltHashtable"/&gt;
156     *  &lt;/java&gt;
157     * </pre></code>
158     * will mean that <code>LogFactory</code> will load an instance of
159     * <code>org.apache.commons.logging.AltHashtable</code>.
160     * </p>
161     * <p>
162     * A typical use case is to allow a custom
163     * Hashtable implementation using weak references to be substituted.
164     * This will allow classloaders to be garbage collected without
165     * the need to release them (on 1.3+ JVMs only, of course ;)
166     * </p>
167     */
168    public static final String HASHTABLE_IMPLEMENTATION_PROPERTY =
169        "org.apache.commons.logging.LogFactory.HashtableImpl";
170    /** Name used to load the weak hashtable implementation by names */
171    private static final String WEAK_HASHTABLE_CLASSNAME =
172        "org.apache.commons.logging.impl.WeakHashtable";
173
174    /**
175     * A reference to the classloader that loaded this class. This is the
176     * same as LogFactory.class.getClassLoader(). However computing this
177     * value isn't quite as simple as that, as we potentially need to use
178     * AccessControllers etc. It's more efficient to compute it once and
179     * cache it here.
180     */
181    private static ClassLoader thisClassLoader;
182
183    // ----------------------------------------------------------- Constructors
184
185
186    /**
187     * Protected constructor that is not available for public use.
188     */
189    protected LogFactory() {
190    }
191
192    // --------------------------------------------------------- Public Methods
193
194
195    /**
196     * Return the configuration attribute with the specified name (if any),
197     * or <code>null</code> if there is no such attribute.
198     *
199     * @param name Name of the attribute to return
200     */
201    public abstract Object getAttribute(String name);
202
203
204    /**
205     * Return an array containing the names of all currently defined
206     * configuration attributes.  If there are no such attributes, a zero
207     * length array is returned.
208     */
209    public abstract String[] getAttributeNames();
210
211
212    /**
213     * Convenience method to derive a name from the specified class and
214     * call <code>getInstance(String)</code> with it.
215     *
216     * @param clazz Class for which a suitable Log name will be derived
217     *
218     * @exception LogConfigurationException if a suitable <code>Log</code>
219     *  instance cannot be returned
220     */
221    public abstract Log getInstance(Class clazz)
222        throws LogConfigurationException;
223
224
225    /**
226     * <p>Construct (if necessary) and return a <code>Log</code> instance,
227     * using the factory's current set of configuration attributes.</p>
228     *
229     * <p><strong>NOTE</strong> - Depending upon the implementation of
230     * the <code>LogFactory</code> you are using, the <code>Log</code>
231     * instance you are returned may or may not be local to the current
232     * application, and may or may not be returned again on a subsequent
233     * call with the same name argument.</p>
234     *
235     * @param name Logical name of the <code>Log</code> instance to be
236     *  returned (the meaning of this name is only known to the underlying
237     *  logging implementation that is being wrapped)
238     *
239     * @exception LogConfigurationException if a suitable <code>Log</code>
240     *  instance cannot be returned
241     */
242    public abstract Log getInstance(String name)
243        throws LogConfigurationException;
244
245
246    /**
247     * Release any internal references to previously created {@link Log}
248     * instances returned by this factory.  This is useful in environments
249     * like servlet containers, which implement application reloading by
250     * throwing away a ClassLoader.  Dangling references to objects in that
251     * class loader would prevent garbage collection.
252     */
253    public abstract void release();
254
255
256    /**
257     * Remove any configuration attribute associated with the specified name.
258     * If there is no such attribute, no action is taken.
259     *
260     * @param name Name of the attribute to remove
261     */
262    public abstract void removeAttribute(String name);
263
264
265    /**
266     * Set the configuration attribute with the specified name.  Calling
267     * this with a <code>null</code> value is equivalent to calling
268     * <code>removeAttribute(name)</code>.
269     *
270     * @param name Name of the attribute to set
271     * @param value Value of the attribute to set, or <code>null</code>
272     *  to remove any setting for this attribute
273     */
274    public abstract void setAttribute(String name, Object value);
275
276
277    // ------------------------------------------------------- Static Variables
278
279
280    /**
281     * The previously constructed <code>LogFactory</code> instances, keyed by
282     * the <code>ClassLoader</code> with which it was created.
283     */
284    protected static Hashtable factories = null;
285
286    /**
287     * Prevously constructed <code>LogFactory</code> instance as in the
288     * <code>factories</code> map, but for the case where
289     * <code>getClassLoader</code> returns <code>null</code>.
290     * This can happen when:
291     * <ul>
292     * <li>using JDK1.1 and the calling code is loaded via the system
293     *  classloader (very common)</li>
294     * <li>using JDK1.2+ and the calling code is loaded via the boot
295     *  classloader (only likely for embedded systems work).</li>
296     * </ul>
297     * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
298     * and hashtables don't allow null as a key.
299     */
300    protected static LogFactory nullClassLoaderFactory = null;
301
302    /**
303     * Create the hashtable which will be used to store a map of
304     * (context-classloader -> logfactory-object). Version 1.2+ of Java
305     * supports "weak references", allowing a custom Hashtable class
306     * to be used which uses only weak references to its keys. Using weak
307     * references can fix memory leaks on webapp unload in some cases (though
308     * not all). Version 1.1 of Java does not support weak references, so we
309     * must dynamically determine which we are using. And just for fun, this
310     * code also supports the ability for a system property to specify an
311     * arbitrary Hashtable implementation name.
312     * <p>
313     * Note that the correct way to ensure no memory leaks occur is to ensure
314     * that LogFactory.release(contextClassLoader) is called whenever a
315     * webapp is undeployed.
316     */
317    private static final Hashtable createFactoryStore() {
318        Hashtable result = null;
319        String storeImplementationClass
320            = System.getProperty(HASHTABLE_IMPLEMENTATION_PROPERTY);
321        if (storeImplementationClass == null) {
322            storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
323        }
324        try {
325            Class implementationClass = Class.forName(storeImplementationClass);
326            result = (Hashtable) implementationClass.newInstance();
327
328        } catch (Throwable t) {
329            // ignore
330            if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
331                // if the user's trying to set up a custom implementation, give a clue
332                if (isDiagnosticsEnabled()) {
333                    // use internal logging to issue the warning
334                    logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
335                } else {
336                    // we *really* want this output, even if diagnostics weren't
337                    // explicitly enabled by the user.
338                    System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
339                }
340            }
341        }
342        if (result == null) {
343            result = new Hashtable();
344        }
345        return result;
346    }
347
348
349    // --------------------------------------------------------- Static Methods
350
351    /**
352     * <p>Construct (if necessary) and return a <code>LogFactory</code>
353     * instance, using the following ordered lookup procedure to determine
354     * the name of the implementation class to be loaded.</p>
355     * <ul>
356     * <li>The <code>org.apache.commons.logging.LogFactory</code> system
357     *     property.</li>
358     * <li>The JDK 1.3 Service Discovery mechanism</li>
359     * <li>Use the properties file <code>commons-logging.properties</code>
360     *     file, if found in the class path of this class.  The configuration
361     *     file is in standard <code>java.util.Properties</code> format and
362     *     contains the fully qualified name of the implementation class
363     *     with the key being the system property defined above.</li>
364     * <li>Fall back to a default implementation class
365     *     (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
366     * </ul>
367     *
368     * <p><em>NOTE</em> - If the properties file method of identifying the
369     * <code>LogFactory</code> implementation class is utilized, all of the
370     * properties defined in this file will be set as configuration attributes
371     * on the corresponding <code>LogFactory</code> instance.</p>
372     *
373     * <p><em>NOTE</em> - In a multithreaded environment it is possible
374     * that two different instances will be returned for the same
375     * classloader environment.
376     * </p>
377     *
378     * @exception LogConfigurationException if the implementation class is not
379     *  available or cannot be instantiated.
380     */
381    public static LogFactory getFactory() throws LogConfigurationException {
382        // Identify the class loader we will be using
383        ClassLoader contextClassLoader = getContextClassLoader();
384
385        if (contextClassLoader == null) {
386            // This is an odd enough situation to report about. This
387            // output will be a nuisance on JDK1.1, as the system
388            // classloader is null in that environment.
389            if (isDiagnosticsEnabled()) {
390                logDiagnostic("Context classloader is null.");
391            }
392        }
393
394        // Return any previously registered factory for this class loader
395        LogFactory factory = getCachedFactory(contextClassLoader);
396        if (factory != null) {
397            return factory;
398        }
399
400        if (isDiagnosticsEnabled()) {
401            logDiagnostic(
402                    "[LOOKUP] LogFactory implementation requested for the first time for context classloader "
403                    + objectId(contextClassLoader));
404            logHierarchy("[LOOKUP] ", contextClassLoader);
405        }
406
407        // Load properties file.
408        //
409        // If the properties file exists, then its contents are used as
410        // "attributes" on the LogFactory implementation class. One particular
411        // property may also control which LogFactory concrete subclass is
412        // used, but only if other discovery mechanisms fail..
413        //
414        // As the properties file (if it exists) will be used one way or
415        // another in the end we may as well look for it first.
416
417        Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
418
419        // Determine whether we will be using the thread context class loader to
420        // load logging classes or not by checking the loaded properties file (if any).
421        ClassLoader baseClassLoader = contextClassLoader;
422        if (props != null) {
423            String useTCCLStr = props.getProperty(TCCL_KEY);
424            if (useTCCLStr != null) {
425                // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
426                // is required for Java 1.2 compatability.
427                if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
428                    // Don't use current context classloader when locating any
429                    // LogFactory or Log classes, just use the class that loaded
430                    // this abstract class. When this class is deployed in a shared
431                    // classpath of a container, it means webapps cannot deploy their
432                    // own logging implementations. It also means that it is up to the
433                    // implementation whether to load library-specific config files
434                    // from the TCCL or not.
435                    baseClassLoader = thisClassLoader;
436                }
437            }
438        }
439
440        // Determine which concrete LogFactory subclass to use.
441        // First, try a global system property
442        if (isDiagnosticsEnabled()) {
443            logDiagnostic(
444                    "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY
445                    + "] to define the LogFactory subclass to use...");
446        }
447
448        try {
449            String factoryClass = System.getProperty(FACTORY_PROPERTY);
450            if (factoryClass != null) {
451                if (isDiagnosticsEnabled()) {
452                    logDiagnostic(
453                            "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass
454                            + "' as specified by system property " + FACTORY_PROPERTY);
455                }
456
457                factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
458            } else {
459                if (isDiagnosticsEnabled()) {
460                    logDiagnostic(
461                            "[LOOKUP] No system property [" + FACTORY_PROPERTY
462                            + "] defined.");
463                }
464            }
465        } catch (SecurityException e) {
466            if (isDiagnosticsEnabled()) {
467                logDiagnostic(
468                        "[LOOKUP] A security exception occurred while trying to create an"
469                        + " instance of the custom factory class"
470                        + ": [" + e.getMessage().trim()
471                        + "]. Trying alternative implementations...");
472            }
473            ;  // ignore
474        } catch(RuntimeException e) {
475            // This is not consistent with the behaviour when a bad LogFactory class is
476            // specified in a services file.
477            //
478            // One possible exception that can occur here is a ClassCastException when
479            // the specified class wasn't castable to this LogFactory type.
480            if (isDiagnosticsEnabled()) {
481                logDiagnostic(
482                        "[LOOKUP] An exception occurred while trying to create an"
483                        + " instance of the custom factory class"
484                        + ": [" + e.getMessage().trim()
485                        + "] as specified by a system property.");
486            }
487            throw e;
488        }
489
490
491        // Second, try to find a service by using the JDK1.3 class
492        // discovery mechanism, which involves putting a file with the name
493        // of an interface class in the META-INF/services directory, where the
494        // contents of the file is a single line specifying a concrete class
495        // that implements the desired interface.
496
497        if (factory == null) {
498            if (isDiagnosticsEnabled()) {
499                logDiagnostic(
500                        "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID
501                        + "] to define the LogFactory subclass to use...");
502            }
503            try {
504                InputStream is = getResourceAsStream(contextClassLoader,
505                                                     SERVICE_ID);
506
507                if( is != null ) {
508                    // This code is needed by EBCDIC and other strange systems.
509                    // It's a fix for bugs reported in xerces
510                    BufferedReader rd;
511                    try {
512                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
513                    } catch (java.io.UnsupportedEncodingException e) {
514                        rd = new BufferedReader(new InputStreamReader(is));
515                    }
516
517                    String factoryClassName = rd.readLine();
518                    rd.close();
519
520                    if (factoryClassName != null &&
521                        ! "".equals(factoryClassName)) {
522                        if (isDiagnosticsEnabled()) {
523                            logDiagnostic(
524                                    "[LOOKUP]  Creating an instance of LogFactory class " + factoryClassName
525                                    + " as specified by file '" + SERVICE_ID
526                                    + "' which was present in the path of the context"
527                                    + " classloader.");
528                        }
529                        factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
530                    }
531                } else {
532                    // is == null
533                    if (isDiagnosticsEnabled()) {
534                        logDiagnostic(
535                            "[LOOKUP] No resource file with name '" + SERVICE_ID
536                            + "' found.");
537                    }
538                }
539            } catch( Exception ex ) {
540                // note: if the specified LogFactory class wasn't compatible with LogFactory
541                // for some reason, a ClassCastException will be caught here, and attempts will
542                // continue to find a compatible class.
543                if (isDiagnosticsEnabled()) {
544                    logDiagnostic(
545                        "[LOOKUP] A security exception occurred while trying to create an"
546                        + " instance of the custom factory class"
547                        + ": [" + ex.getMessage().trim()
548                        + "]. Trying alternative implementations...");
549                }
550                ; // ignore
551            }
552        }
553
554
555        // Third try looking into the properties file read earlier (if found)
556
557        if (factory == null) {
558            if (props != null) {
559                if (isDiagnosticsEnabled()) {
560                    logDiagnostic(
561                        "[LOOKUP] Looking in properties file for entry with key '"
562                        + FACTORY_PROPERTY
563                        + "' to define the LogFactory subclass to use...");
564                }
565                String factoryClass = props.getProperty(FACTORY_PROPERTY);
566                if (factoryClass != null) {
567                    if (isDiagnosticsEnabled()) {
568                        logDiagnostic(
569                            "[LOOKUP] Properties file specifies LogFactory subclass '"
570                            + factoryClass + "'");
571                    }
572                    factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
573
574                    // TODO: think about whether we need to handle exceptions from newFactory
575                } else {
576                    if (isDiagnosticsEnabled()) {
577                        logDiagnostic(
578                            "[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
579                    }
580                }
581            } else {
582                if (isDiagnosticsEnabled()) {
583                    logDiagnostic(
584                        "[LOOKUP] No properties file available to determine"
585                        + " LogFactory subclass from..");
586                }
587            }
588        }
589
590
591        // Fourth, try the fallback implementation class
592
593        if (factory == null) {
594            if (isDiagnosticsEnabled()) {
595                logDiagnostic(
596                "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT
597                + "' via the same classloader that loaded this LogFactory"
598                + " class (ie not looking in the context classloader).");
599            }
600
601            // Note: unlike the above code which can try to load custom LogFactory
602            // implementations via the TCCL, we don't try to load the default LogFactory
603            // implementation via the context classloader because:
604            // * that can cause problems (see comments in newFactory method)
605            // * no-one should be customising the code of the default class
606            // Yes, we do give up the ability for the child to ship a newer
607            // version of the LogFactoryImpl class and have it used dynamically
608            // by an old LogFactory class in the parent, but that isn't
609            // necessarily a good idea anyway.
610            factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
611        }
612
613        if (factory != null) {
614            /**
615             * Always cache using context class loader.
616             */
617            cacheFactory(contextClassLoader, factory);
618
619            if( props!=null ) {
620                Enumeration names = props.propertyNames();
621                while (names.hasMoreElements()) {
622                    String name = (String) names.nextElement();
623                    String value = props.getProperty(name);
624                    factory.setAttribute(name, value);
625                }
626            }
627        }
628
629        return factory;
630    }
631
632
633    /**
634     * Convenience method to return a named logger, without the application
635     * having to care about factories.
636     *
637     * @param clazz Class from which a log name will be derived
638     *
639     * @exception LogConfigurationException if a suitable <code>Log</code>
640     *  instance cannot be returned
641     */
642    public static Log getLog(Class clazz)
643        throws LogConfigurationException {
644
645        // BEGIN android-added
646        return getLog(clazz.getName());
647        // END android-added
648        // BEGIN android-deleted
649        //return (getFactory().getInstance(clazz));
650        // END android-deleted
651
652    }
653
654
655    /**
656     * Convenience method to return a named logger, without the application
657     * having to care about factories.
658     *
659     * @param name Logical name of the <code>Log</code> instance to be
660     *  returned (the meaning of this name is only known to the underlying
661     *  logging implementation that is being wrapped)
662     *
663     * @exception LogConfigurationException if a suitable <code>Log</code>
664     *  instance cannot be returned
665     */
666    public static Log getLog(String name)
667        throws LogConfigurationException {
668
669        // BEGIN android-added
670        return new org.apache.commons.logging.impl.Jdk14Logger(name);
671        // END android-added
672        // BEGIN android-deleted
673        //return (getFactory().getInstance(name));
674        // END android-deleted
675
676    }
677
678
679    /**
680     * Release any internal references to previously created {@link LogFactory}
681     * instances that have been associated with the specified class loader
682     * (if any), after calling the instance method <code>release()</code> on
683     * each of them.
684     *
685     * @param classLoader ClassLoader for which to release the LogFactory
686     */
687    public static void release(ClassLoader classLoader) {
688
689        if (isDiagnosticsEnabled()) {
690            logDiagnostic("Releasing factory for classloader " + objectId(classLoader));
691        }
692        synchronized (factories) {
693            if (classLoader == null) {
694                if (nullClassLoaderFactory != null) {
695                    nullClassLoaderFactory.release();
696                    nullClassLoaderFactory = null;
697                }
698            } else {
699                LogFactory factory = (LogFactory) factories.get(classLoader);
700                if (factory != null) {
701                    factory.release();
702                    factories.remove(classLoader);
703                }
704            }
705        }
706
707    }
708
709
710    /**
711     * Release any internal references to previously created {@link LogFactory}
712     * instances, after calling the instance method <code>release()</code> on
713     * each of them.  This is useful in environments like servlet containers,
714     * which implement application reloading by throwing away a ClassLoader.
715     * Dangling references to objects in that class loader would prevent
716     * garbage collection.
717     */
718    public static void releaseAll() {
719
720        if (isDiagnosticsEnabled()) {
721            logDiagnostic("Releasing factory for all classloaders.");
722        }
723        synchronized (factories) {
724            Enumeration elements = factories.elements();
725            while (elements.hasMoreElements()) {
726                LogFactory element = (LogFactory) elements.nextElement();
727                element.release();
728            }
729            factories.clear();
730
731            if (nullClassLoaderFactory != null) {
732                nullClassLoaderFactory.release();
733                nullClassLoaderFactory = null;
734            }
735        }
736
737    }
738
739
740    // ------------------------------------------------------ Protected Methods
741
742    /**
743     * Safely get access to the classloader for the specified class.
744     * <p>
745     * Theoretically, calling getClassLoader can throw a security exception,
746     * and so should be done under an AccessController in order to provide
747     * maximum flexibility. However in practice people don't appear to use
748     * security policies that forbid getClassLoader calls. So for the moment
749     * all code is written to call this method rather than Class.getClassLoader,
750     * so that we could put AccessController stuff in this method without any
751     * disruption later if we need to.
752     * <p>
753     * Even when using an AccessController, however, this method can still
754     * throw SecurityException. Commons-logging basically relies on the
755     * ability to access classloaders, ie a policy that forbids all
756     * classloader access will also prevent commons-logging from working:
757     * currently this method will throw an exception preventing the entire app
758     * from starting up. Maybe it would be good to detect this situation and
759     * just disable all commons-logging? Not high priority though - as stated
760     * above, security policies that prevent classloader access aren't common.
761     *
762     * @since 1.1
763     */
764    protected static ClassLoader getClassLoader(Class clazz) {
765        try {
766            return clazz.getClassLoader();
767        } catch(SecurityException ex) {
768            if (isDiagnosticsEnabled()) {
769                logDiagnostic(
770                        "Unable to get classloader for class '" + clazz
771                        + "' due to security restrictions - " + ex.getMessage());
772            }
773            throw ex;
774        }
775    }
776
777    /**
778     * Calls LogFactory.directGetContextClassLoader under the control of an
779     * AccessController class. This means that java code running under a
780     * security manager that forbids access to ClassLoaders will still work
781     * if this class is given appropriate privileges, even when the caller
782     * doesn't have such privileges. Without using an AccessController, the
783     * the entire call stack must have the privilege before the call is
784     * allowed.
785     *
786     * @return the context classloader associated with the current thread,
787     * or null if security doesn't allow it.
788     *
789     * @throws LogConfigurationException if there was some weird error while
790     * attempting to get the context classloader.
791     *
792     * @throws SecurityException if the current java security policy doesn't
793     * allow this class to access the context classloader.
794     */
795    protected static ClassLoader getContextClassLoader()
796        throws LogConfigurationException {
797
798        return (ClassLoader)AccessController.doPrivileged(
799            new PrivilegedAction() {
800                public Object run() {
801                    return directGetContextClassLoader();
802                }
803            });
804    }
805
806    /**
807     * Return the thread context class loader if available; otherwise return
808     * null.
809     * <p>
810     * Most/all code should call getContextClassLoader rather than calling
811     * this method directly.
812     * <p>
813     * The thread context class loader is available for JDK 1.2
814     * or later, if certain security conditions are met.
815     * <p>
816     * Note that no internal logging is done within this method because
817     * this method is called every time LogFactory.getLogger() is called,
818     * and we don't want too much output generated here.
819     *
820     * @exception LogConfigurationException if a suitable class loader
821     * cannot be identified.
822     *
823     * @exception SecurityException if the java security policy forbids
824     * access to the context classloader from one of the classes in the
825     * current call stack.
826     * @since 1.1
827     */
828    protected static ClassLoader directGetContextClassLoader()
829        throws LogConfigurationException
830    {
831        ClassLoader classLoader = null;
832
833        try {
834            // Are we running on a JDK 1.2 or later system?
835            Method method = Thread.class.getMethod("getContextClassLoader",
836                    (Class[]) null);
837
838            // Get the thread context class loader (if there is one)
839            try {
840                classLoader = (ClassLoader)method.invoke(Thread.currentThread(),
841                        (Object[]) null);
842            } catch (IllegalAccessException e) {
843                throw new LogConfigurationException
844                    ("Unexpected IllegalAccessException", e);
845            } catch (InvocationTargetException e) {
846                /**
847                 * InvocationTargetException is thrown by 'invoke' when
848                 * the method being invoked (getContextClassLoader) throws
849                 * an exception.
850                 *
851                 * getContextClassLoader() throws SecurityException when
852                 * the context class loader isn't an ancestor of the
853                 * calling class's class loader, or if security
854                 * permissions are restricted.
855                 *
856                 * In the first case (not related), we want to ignore and
857                 * keep going.  We cannot help but also ignore the second
858                 * with the logic below, but other calls elsewhere (to
859                 * obtain a class loader) will trigger this exception where
860                 * we can make a distinction.
861                 */
862                if (e.getTargetException() instanceof SecurityException) {
863                    ;  // ignore
864                } else {
865                    // Capture 'e.getTargetException()' exception for details
866                    // alternate: log 'e.getTargetException()', and pass back 'e'.
867                    throw new LogConfigurationException
868                        ("Unexpected InvocationTargetException", e.getTargetException());
869                }
870            }
871        } catch (NoSuchMethodException e) {
872            // Assume we are running on JDK 1.1
873            classLoader = getClassLoader(LogFactory.class);
874
875            // We deliberately don't log a message here to outputStream;
876            // this message would be output for every call to LogFactory.getLog()
877            // when running on JDK1.1
878            //
879            // if (outputStream != null) {
880            //    outputStream.println(
881            //        "Method Thread.getContextClassLoader does not exist;"
882            //         + " assuming this is JDK 1.1, and that the context"
883            //         + " classloader is the same as the class that loaded"
884            //         + " the concrete LogFactory class.");
885            // }
886
887        }
888
889        // Return the selected class loader
890        return classLoader;
891    }
892
893    /**
894     * Check cached factories (keyed by contextClassLoader)
895     *
896     * @param contextClassLoader is the context classloader associated
897     * with the current thread. This allows separate LogFactory objects
898     * per component within a container, provided each component has
899     * a distinct context classloader set. This parameter may be null
900     * in JDK1.1, and in embedded systems where jcl-using code is
901     * placed in the bootclasspath.
902     *
903     * @return the factory associated with the specified classloader if
904     * one has previously been created, or null if this is the first time
905     * we have seen this particular classloader.
906     */
907    private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
908    {
909        LogFactory factory = null;
910
911        if (contextClassLoader == null) {
912            // We have to handle this specially, as factories is a Hashtable
913            // and those don't accept null as a key value.
914            //
915            // nb: nullClassLoaderFactory might be null. That's ok.
916            factory = nullClassLoaderFactory;
917        } else {
918            factory = (LogFactory) factories.get(contextClassLoader);
919        }
920
921        return factory;
922    }
923
924    /**
925     * Remember this factory, so later calls to LogFactory.getCachedFactory
926     * can return the previously created object (together with all its
927     * cached Log objects).
928     *
929     * @param classLoader should be the current context classloader. Note that
930     * this can be null under some circumstances; this is ok.
931     *
932     * @param factory should be the factory to cache. This should never be null.
933     */
934    private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
935    {
936        // Ideally we would assert(factory != null) here. However reporting
937        // errors from within a logging implementation is a little tricky!
938
939        if (factory != null) {
940            if (classLoader == null) {
941                nullClassLoaderFactory = factory;
942            } else {
943                factories.put(classLoader, factory);
944            }
945        }
946    }
947
948    /**
949     * Return a new instance of the specified <code>LogFactory</code>
950     * implementation class, loaded by the specified class loader.
951     * If that fails, try the class loader used to load this
952     * (abstract) LogFactory.
953     * <p>
954     * <h2>ClassLoader conflicts</h2>
955     * Note that there can be problems if the specified ClassLoader is not the
956     * same as the classloader that loaded this class, ie when loading a
957     * concrete LogFactory subclass via a context classloader.
958     * <p>
959     * The problem is the same one that can occur when loading a concrete Log
960     * subclass via a context classloader.
961     * <p>
962     * The problem occurs when code running in the context classloader calls
963     * class X which was loaded via a parent classloader, and class X then calls
964     * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
965     * class X was loaded via the parent, it binds to LogFactory loaded via
966     * the parent. When the code in this method finds some LogFactoryYYYY
967     * class in the child (context) classloader, and there also happens to be a
968     * LogFactory class defined in the child classloader, then LogFactoryYYYY
969     * will be bound to LogFactory@childloader. It cannot be cast to
970     * LogFactory@parentloader, ie this method cannot return the object as
971     * the desired type. Note that it doesn't matter if the LogFactory class
972     * in the child classloader is identical to the LogFactory class in the
973     * parent classloader, they are not compatible.
974     * <p>
975     * The solution taken here is to simply print out an error message when
976     * this occurs then throw an exception. The deployer of the application
977     * must ensure they remove all occurrences of the LogFactory class from
978     * the child classloader in order to resolve the issue. Note that they
979     * do not have to move the custom LogFactory subclass; that is ok as
980     * long as the only LogFactory class it can find to bind to is in the
981     * parent classloader.
982     * <p>
983     * @param factoryClass Fully qualified name of the <code>LogFactory</code>
984     *  implementation class
985     * @param classLoader ClassLoader from which to load this class
986     * @param contextClassLoader is the context that this new factory will
987     * manage logging for.
988     *
989     * @exception LogConfigurationException if a suitable instance
990     *  cannot be created
991     * @since 1.1
992     */
993    protected static LogFactory newFactory(final String factoryClass,
994                                           final ClassLoader classLoader,
995                                           final ClassLoader contextClassLoader)
996        throws LogConfigurationException
997    {
998        // Note that any unchecked exceptions thrown by the createFactory
999        // method will propagate out of this method; in particular a
1000        // ClassCastException can be thrown.
1001        Object result = AccessController.doPrivileged(
1002            new PrivilegedAction() {
1003                public Object run() {
1004                    return createFactory(factoryClass, classLoader);
1005                }
1006            });
1007
1008        if (result instanceof LogConfigurationException) {
1009            LogConfigurationException ex = (LogConfigurationException) result;
1010            if (isDiagnosticsEnabled()) {
1011                logDiagnostic(
1012                        "An error occurred while loading the factory class:"
1013                        + ex.getMessage());
1014            }
1015            throw ex;
1016        }
1017        if (isDiagnosticsEnabled()) {
1018            logDiagnostic(
1019                    "Created object " + objectId(result)
1020                    + " to manage classloader " + objectId(contextClassLoader));
1021        }
1022        return (LogFactory)result;
1023    }
1024
1025    /**
1026     * Method provided for backwards compatibility; see newFactory version that
1027     * takes 3 parameters.
1028     * <p>
1029     * This method would only ever be called in some rather odd situation.
1030     * Note that this method is static, so overriding in a subclass doesn't
1031     * have any effect unless this method is called from a method in that
1032     * subclass. However this method only makes sense to use from the
1033     * getFactory method, and as that is almost always invoked via
1034     * LogFactory.getFactory, any custom definition in a subclass would be
1035     * pointless. Only a class with a custom getFactory method, then invoked
1036     * directly via CustomFactoryImpl.getFactory or similar would ever call
1037     * this. Anyway, it's here just in case, though the "managed class loader"
1038     * value output to the diagnostics will not report the correct value.
1039     */
1040    protected static LogFactory newFactory(final String factoryClass,
1041                                           final ClassLoader classLoader) {
1042	    return newFactory(factoryClass, classLoader, null);
1043    }
1044
1045    /**
1046     * Implements the operations described in the javadoc for newFactory.
1047     *
1048     * @param factoryClass
1049     *
1050     * @param classLoader used to load the specified factory class. This is
1051     * expected to be either the TCCL or the classloader which loaded this
1052     * class. Note that the classloader which loaded this class might be
1053     * "null" (ie the bootloader) for embedded systems.
1054     *
1055     * @return either a LogFactory object or a LogConfigurationException object.
1056     * @since 1.1
1057     */
1058    protected static Object createFactory(String factoryClass, ClassLoader classLoader) {
1059
1060        // This will be used to diagnose bad configurations
1061        // and allow a useful message to be sent to the user
1062        Class logFactoryClass = null;
1063        try {
1064            if (classLoader != null) {
1065                try {
1066                    // First the given class loader param (thread class loader)
1067
1068                    // Warning: must typecast here & allow exception
1069                    // to be generated/caught & recast properly.
1070                    logFactoryClass = classLoader.loadClass(factoryClass);
1071                    if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
1072                        if (isDiagnosticsEnabled()) {
1073                            logDiagnostic(
1074                                    "Loaded class " + logFactoryClass.getName()
1075                                    + " from classloader " + objectId(classLoader));
1076                        }
1077                    } else {
1078                        //
1079                        // This indicates a problem with the ClassLoader tree.
1080                        // An incompatible ClassLoader was used to load the
1081                        // implementation.
1082                        // As the same classes
1083                        // must be available in multiple class loaders,
1084                        // it is very likely that multiple JCL jars are present.
1085                        // The most likely fix for this
1086                        // problem is to remove the extra JCL jars from the
1087                        // ClassLoader hierarchy.
1088                        //
1089                        if (isDiagnosticsEnabled()) {
1090                            logDiagnostic(
1091                                    "Factory class " + logFactoryClass.getName()
1092                                + " loaded from classloader " + objectId(logFactoryClass.getClassLoader())
1093                                + " does not extend '" + LogFactory.class.getName()
1094                                + "' as loaded by this classloader.");
1095                            logHierarchy("[BAD CL TREE] ", classLoader);
1096                        }
1097                    }
1098
1099                    return (LogFactory) logFactoryClass.newInstance();
1100
1101                } catch (ClassNotFoundException ex) {
1102                    if (classLoader == thisClassLoader) {
1103                        // Nothing more to try, onwards.
1104                        if (isDiagnosticsEnabled()) {
1105                            logDiagnostic(
1106                                    "Unable to locate any class called '" + factoryClass
1107                                    + "' via classloader " + objectId(classLoader));
1108                        }
1109                        throw ex;
1110                    }
1111                    // ignore exception, continue
1112                } catch (NoClassDefFoundError e) {
1113                    if (classLoader == thisClassLoader) {
1114                        // Nothing more to try, onwards.
1115                        if (isDiagnosticsEnabled()) {
1116                            logDiagnostic(
1117                                    "Class '" + factoryClass + "' cannot be loaded"
1118                                    + " via classloader " + objectId(classLoader)
1119                                    + " - it depends on some other class that cannot"
1120                                    + " be found.");
1121                        }
1122                        throw e;
1123                    }
1124                    // ignore exception, continue
1125                } catch(ClassCastException e) {
1126                    if (classLoader == thisClassLoader) {
1127                        // There's no point in falling through to the code below that
1128                        // tries again with thisClassLoader, because we've just tried
1129                        // loading with that loader (not the TCCL). Just throw an
1130                        // appropriate exception here.
1131
1132                    	final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
1133
1134                        //
1135                        // Construct a good message: users may not actual expect that a custom implementation
1136                        // has been specified. Several well known containers use this mechanism to adapt JCL
1137                        // to their native logging system.
1138                        //
1139                        String msg =
1140                            "The application has specified that a custom LogFactory implementation should be used but " +
1141                            "Class '" + factoryClass + "' cannot be converted to '"
1142                            + LogFactory.class.getName() + "'. ";
1143                        if (implementsLogFactory) {
1144                            msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " +
1145                    		"Background can be found in http://jakarta.apache.org/commons/logging/tech.html. " +
1146                    		"If you have not explicitly specified a custom LogFactory then it is likely that " +
1147                    		"the container has set one without your knowledge. " +
1148                    		"In this case, consider using the commons-logging-adapters.jar file or " +
1149                    		"specifying the standard LogFactory from the command line. ";
1150                        } else {
1151                        	msg = msg + "Please check the custom implementation. ";
1152                        }
1153                        msg = msg + "Help can be found @http://jakarta.apache.org/commons/logging/troubleshooting.html.";
1154
1155                        if (isDiagnosticsEnabled()) {
1156                            logDiagnostic(msg);
1157                        }
1158
1159                        ClassCastException ex = new ClassCastException(msg);
1160                        throw ex;
1161                    }
1162
1163                    // Ignore exception, continue. Presumably the classloader was the
1164                    // TCCL; the code below will try to load the class via thisClassLoader.
1165                    // This will handle the case where the original calling class is in
1166                    // a shared classpath but the TCCL has a copy of LogFactory and the
1167                    // specified LogFactory implementation; we will fall back to using the
1168                    // LogFactory implementation from the same classloader as this class.
1169                    //
1170                    // Issue: this doesn't handle the reverse case, where this LogFactory
1171                    // is in the webapp, and the specified LogFactory implementation is
1172                    // in a shared classpath. In that case:
1173                    // (a) the class really does implement LogFactory (bad log msg above)
1174                    // (b) the fallback code will result in exactly the same problem.
1175                }
1176            }
1177
1178            /* At this point, either classLoader == null, OR
1179             * classLoader was unable to load factoryClass.
1180             *
1181             * In either case, we call Class.forName, which is equivalent
1182             * to LogFactory.class.getClassLoader().load(name), ie we ignore
1183             * the classloader parameter the caller passed, and fall back
1184             * to trying the classloader associated with this class. See the
1185             * javadoc for the newFactory method for more info on the
1186             * consequences of this.
1187             *
1188             * Notes:
1189             * * LogFactory.class.getClassLoader() may return 'null'
1190             *   if LogFactory is loaded by the bootstrap classloader.
1191             */
1192            // Warning: must typecast here & allow exception
1193            // to be generated/caught & recast properly.
1194            if (isDiagnosticsEnabled()) {
1195                logDiagnostic(
1196                    "Unable to load factory class via classloader "
1197                    + objectId(classLoader)
1198                    + " - trying the classloader associated with this LogFactory.");
1199            }
1200            logFactoryClass = Class.forName(factoryClass);
1201            return (LogFactory) logFactoryClass.newInstance();
1202        } catch (Exception e) {
1203            // Check to see if we've got a bad configuration
1204            if (isDiagnosticsEnabled()) {
1205                logDiagnostic("Unable to create LogFactory instance.");
1206            }
1207            if (logFactoryClass != null
1208                && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
1209
1210                return new LogConfigurationException(
1211                    "The chosen LogFactory implementation does not extend LogFactory."
1212                    + " Please check your configuration.",
1213                    e);
1214            }
1215            return new LogConfigurationException(e);
1216        }
1217    }
1218
1219    /**
1220     * Determines whether the given class actually implements <code>LogFactory</code>.
1221     * Diagnostic information is also logged.
1222     * <p>
1223     * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
1224     * of incompatibility. The test used is whether the class is assignable from
1225     * the <code>LogFactory</code> class loaded by the class's classloader.
1226     * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
1227     * @return true if the <code>logFactoryClass</code> does extend
1228     * <code>LogFactory</code> when that class is loaded via the same
1229     * classloader that loaded the <code>logFactoryClass</code>.
1230     */
1231    private static boolean implementsLogFactory(Class logFactoryClass) {
1232        boolean implementsLogFactory = false;
1233        if (logFactoryClass != null) {
1234            try {
1235                ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
1236                if (logFactoryClassLoader == null) {
1237                    logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
1238                } else {
1239                    logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
1240                    Class factoryFromCustomLoader
1241                        = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
1242                    implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
1243                    if (implementsLogFactory) {
1244                        logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
1245                                + " implements LogFactory but was loaded by an incompatible classloader.");
1246                    } else {
1247                        logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
1248                                + " does not implement LogFactory.");
1249                    }
1250                }
1251            } catch (SecurityException e) {
1252                //
1253                // The application is running within a hostile security environment.
1254                // This will make it very hard to diagnose issues with JCL.
1255                // Consider running less securely whilst debugging this issue.
1256                //
1257                logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " +
1258                        "the compatibility was caused by a classloader conflict: "
1259                        + e.getMessage());
1260            } catch (LinkageError e) {
1261                //
1262                // This should be an unusual circumstance.
1263                // LinkageError's usually indicate that a dependent class has incompatibly changed.
1264                // Another possibility may be an exception thrown by an initializer.
1265                // Time for a clean rebuild?
1266                //
1267                logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " +
1268                        "the compatibility was caused by a classloader conflict: "
1269                        + e.getMessage());
1270            } catch (ClassNotFoundException e) {
1271                //
1272                // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation.
1273                // The custom implementation is not viable until this is corrected.
1274                // Ensure that the JCL jar and the custom class are available from the same classloader.
1275                // Running with diagnostics on should give information about the classloaders used
1276                // to load the custom factory.
1277                //
1278                logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the " +
1279                        "custom LogFactory implementation. Is the custom factory in the right classloader?");
1280            }
1281        }
1282        return implementsLogFactory;
1283    }
1284
1285    /**
1286     * Applets may run in an environment where accessing resources of a loader is
1287     * a secure operation, but where the commons-logging library has explicitly
1288     * been granted permission for that operation. In this case, we need to
1289     * run the operation using an AccessController.
1290     */
1291    private static InputStream getResourceAsStream(final ClassLoader loader,
1292                                                   final String name)
1293    {
1294        return (InputStream)AccessController.doPrivileged(
1295            new PrivilegedAction() {
1296                public Object run() {
1297                    if (loader != null) {
1298                        return loader.getResourceAsStream(name);
1299                    } else {
1300                        return ClassLoader.getSystemResourceAsStream(name);
1301                    }
1302                }
1303            });
1304    }
1305
1306    /**
1307     * Given a filename, return an enumeration of URLs pointing to
1308     * all the occurrences of that filename in the classpath.
1309     * <p>
1310     * This is just like ClassLoader.getResources except that the
1311     * operation is done under an AccessController so that this method will
1312     * succeed when this jarfile is privileged but the caller is not.
1313     * This method must therefore remain private to avoid security issues.
1314     * <p>
1315     * If no instances are found, an Enumeration is returned whose
1316     * hasMoreElements method returns false (ie an "empty" enumeration).
1317     * If resources could not be listed for some reason, null is returned.
1318     */
1319    private static Enumeration getResources(final ClassLoader loader,
1320            final String name)
1321    {
1322        PrivilegedAction action =
1323            new PrivilegedAction() {
1324                public Object run() {
1325                    try {
1326                        if (loader != null) {
1327                            return loader.getResources(name);
1328                        } else {
1329                            return ClassLoader.getSystemResources(name);
1330                        }
1331                    } catch(IOException e) {
1332                        if (isDiagnosticsEnabled()) {
1333                            logDiagnostic(
1334                                "Exception while trying to find configuration file "
1335                                + name + ":" + e.getMessage());
1336                        }
1337                        return null;
1338                    } catch(NoSuchMethodError e) {
1339                        // we must be running on a 1.1 JVM which doesn't support
1340                        // ClassLoader.getSystemResources; just return null in
1341                        // this case.
1342                        return null;
1343                    }
1344                }
1345            };
1346        Object result = AccessController.doPrivileged(action);
1347        return (Enumeration) result;
1348    }
1349
1350    /**
1351     * Given a URL that refers to a .properties file, load that file.
1352     * This is done under an AccessController so that this method will
1353     * succeed when this jarfile is privileged but the caller is not.
1354     * This method must therefore remain private to avoid security issues.
1355     * <p>
1356     * Null is returned if the URL cannot be opened.
1357     */
1358    private static Properties getProperties(final URL url) {
1359        PrivilegedAction action =
1360            new PrivilegedAction() {
1361                public Object run() {
1362                    try {
1363                        InputStream stream = url.openStream();
1364                        if (stream != null) {
1365                            Properties props = new Properties();
1366                            props.load(stream);
1367                            stream.close();
1368                            return props;
1369                        }
1370                    } catch(IOException e) {
1371                        if (isDiagnosticsEnabled()) {
1372                            logDiagnostic("Unable to read URL " + url);
1373                        }
1374                    }
1375
1376                    return null;
1377                }
1378            };
1379        return (Properties) AccessController.doPrivileged(action);
1380    }
1381
1382    /**
1383     * Locate a user-provided configuration file.
1384     * <p>
1385     * The classpath of the specified classLoader (usually the context classloader)
1386     * is searched for properties files of the specified name. If none is found,
1387     * null is returned. If more than one is found, then the file with the greatest
1388     * value for its PRIORITY property is returned. If multiple files have the
1389     * same PRIORITY value then the first in the classpath is returned.
1390     * <p>
1391     * This differs from the 1.0.x releases; those always use the first one found.
1392     * However as the priority is a new field, this change is backwards compatible.
1393     * <p>
1394     * The purpose of the priority field is to allow a webserver administrator to
1395     * override logging settings in all webapps by placing a commons-logging.properties
1396     * file in a shared classpath location with a priority > 0; this overrides any
1397     * commons-logging.properties files without priorities which are in the
1398     * webapps. Webapps can also use explicit priorities to override a configuration
1399     * file in the shared classpath if needed.
1400     */
1401    private static final Properties getConfigurationFile(
1402            ClassLoader classLoader, String fileName) {
1403
1404        Properties props = null;
1405        double priority = 0.0;
1406        URL propsUrl = null;
1407        try {
1408            Enumeration urls = getResources(classLoader, fileName);
1409
1410            if (urls == null) {
1411                return null;
1412            }
1413
1414            while (urls.hasMoreElements()) {
1415                URL url = (URL) urls.nextElement();
1416
1417                Properties newProps = getProperties(url);
1418                if (newProps != null) {
1419                    if (props == null) {
1420                        propsUrl = url;
1421                        props = newProps;
1422                        String priorityStr = props.getProperty(PRIORITY_KEY);
1423                        priority = 0.0;
1424                        if (priorityStr != null) {
1425                            priority = Double.parseDouble(priorityStr);
1426                        }
1427
1428                        if (isDiagnosticsEnabled()) {
1429                            logDiagnostic(
1430                                "[LOOKUP] Properties file found at '" + url + "'"
1431                                + " with priority " + priority);
1432                        }
1433                    } else {
1434                        String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
1435                        double newPriority = 0.0;
1436                        if (newPriorityStr != null) {
1437                            newPriority = Double.parseDouble(newPriorityStr);
1438                        }
1439
1440                        if (newPriority > priority) {
1441                            if (isDiagnosticsEnabled()) {
1442                                logDiagnostic(
1443                                    "[LOOKUP] Properties file at '" + url + "'"
1444                                    + " with priority " + newPriority
1445                                    + " overrides file at '" + propsUrl + "'"
1446                                    + " with priority " + priority);
1447                            }
1448
1449                            propsUrl = url;
1450                            props = newProps;
1451                            priority = newPriority;
1452                        } else {
1453                            if (isDiagnosticsEnabled()) {
1454                                logDiagnostic(
1455                                    "[LOOKUP] Properties file at '" + url + "'"
1456                                    + " with priority " + newPriority
1457                                    + " does not override file at '" + propsUrl + "'"
1458                                    + " with priority " + priority);
1459                            }
1460                        }
1461                    }
1462
1463                }
1464            }
1465        } catch (SecurityException e) {
1466            if (isDiagnosticsEnabled()) {
1467                logDiagnostic("SecurityException thrown while trying to find/read config files.");
1468            }
1469        }
1470
1471        if (isDiagnosticsEnabled()) {
1472            if (props == null) {
1473                logDiagnostic(
1474                    "[LOOKUP] No properties file of name '" + fileName
1475                    + "' found.");
1476            } else {
1477                logDiagnostic(
1478                    "[LOOKUP] Properties file of name '" + fileName
1479                    + "' found at '" + propsUrl + '"');
1480            }
1481        }
1482
1483        return props;
1484    }
1485
1486    /**
1487     * Determines whether the user wants internal diagnostic output. If so,
1488     * returns an appropriate writer object. Users can enable diagnostic
1489     * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
1490     * a filename, or the special values STDOUT or STDERR.
1491     */
1492    private static void initDiagnostics() {
1493        String dest;
1494    	try {
1495    	    dest = System.getProperty(DIAGNOSTICS_DEST_PROPERTY);
1496    	    if (dest == null) {
1497    	        return;
1498    	    }
1499    	} catch(SecurityException ex) {
1500    	    // We must be running in some very secure environment.
1501    	    // We just have to assume output is not wanted..
1502    	    return;
1503    	}
1504
1505    	if (dest.equals("STDOUT")) {
1506    	    diagnosticsStream = System.out;
1507    	} else if (dest.equals("STDERR")) {
1508    	    diagnosticsStream = System.err;
1509    	} else {
1510    	    try {
1511                // open the file in append mode
1512    	        FileOutputStream fos = new FileOutputStream(dest, true);
1513    	        diagnosticsStream = new PrintStream(fos);
1514    	    } catch(IOException ex) {
1515    	        // We should report this to the user - but how?
1516    	        return;
1517    	    }
1518    	}
1519
1520        // In order to avoid confusion where multiple instances of JCL are
1521        // being used via different classloaders within the same app, we
1522        // ensure each logged message has a prefix of form
1523        // [LogFactory from classloader OID]
1524        //
1525        // Note that this prefix should be kept consistent with that
1526        // in LogFactoryImpl. However here we don't need to output info
1527        // about the actual *instance* of LogFactory, as all methods that
1528        // output diagnostics from this class are static.
1529        String classLoaderName;
1530        try {
1531            ClassLoader classLoader = thisClassLoader;
1532            if (thisClassLoader == null) {
1533                classLoaderName = "BOOTLOADER";
1534            } else {
1535                classLoaderName = objectId(classLoader);
1536            }
1537        } catch(SecurityException e) {
1538            classLoaderName = "UNKNOWN";
1539        }
1540        diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
1541    }
1542
1543    /**
1544     * Indicates true if the user has enabled internal logging.
1545     * <p>
1546     * By the way, sorry for the incorrect grammar, but calling this method
1547     * areDiagnosticsEnabled just isn't java beans style.
1548     *
1549     * @return true if calls to logDiagnostic will have any effect.
1550     * @since 1.1
1551     */
1552    protected static boolean isDiagnosticsEnabled() {
1553        return diagnosticsStream != null;
1554    }
1555
1556    /**
1557     * Write the specified message to the internal logging destination.
1558     * <p>
1559     * Note that this method is private; concrete subclasses of this class
1560     * should not call it because the diagnosticPrefix string this
1561     * method puts in front of all its messages is LogFactory@....,
1562     * while subclasses should put SomeSubClass@...
1563     * <p>
1564     * Subclasses should instead compute their own prefix, then call
1565     * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
1566     * fine for subclasses.
1567     * <p>
1568     * Note that it is safe to call this method before initDiagnostics
1569     * is called; any output will just be ignored (as isDiagnosticsEnabled
1570     * will return false).
1571     *
1572     * @param msg is the diagnostic message to be output.
1573     */
1574    private static final void logDiagnostic(String msg) {
1575        if (diagnosticsStream != null) {
1576            diagnosticsStream.print(diagnosticPrefix);
1577            diagnosticsStream.println(msg);
1578            diagnosticsStream.flush();
1579        }
1580    }
1581
1582    /**
1583     * Write the specified message to the internal logging destination.
1584     *
1585     * @param msg is the diagnostic message to be output.
1586     * @since 1.1
1587     */
1588    protected static final void logRawDiagnostic(String msg) {
1589        if (diagnosticsStream != null) {
1590            diagnosticsStream.println(msg);
1591            diagnosticsStream.flush();
1592        }
1593    }
1594
1595    /**
1596     * Generate useful diagnostics regarding the classloader tree for
1597     * the specified class.
1598     * <p>
1599     * As an example, if the specified class was loaded via a webapp's
1600     * classloader, then you may get the following output:
1601     * <pre>
1602     * Class com.acme.Foo was loaded via classloader 11111
1603     * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT
1604     * </pre>
1605     * <p>
1606     * This method returns immediately if isDiagnosticsEnabled()
1607     * returns false.
1608     *
1609     * @param clazz is the class whose classloader + tree are to be
1610     * output.
1611     */
1612    private static void logClassLoaderEnvironment(Class clazz) {
1613        if (!isDiagnosticsEnabled()) {
1614            return;
1615        }
1616
1617        try {
1618            logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
1619            logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
1620        } catch(SecurityException ex) {
1621            logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
1622        }
1623
1624        String className = clazz.getName();
1625        ClassLoader classLoader;
1626
1627        try {
1628            classLoader = getClassLoader(clazz);
1629        } catch(SecurityException ex) {
1630            // not much useful diagnostics we can print here!
1631            logDiagnostic(
1632                "[ENV] Security forbids determining the classloader for " + className);
1633            return;
1634        }
1635
1636        logDiagnostic(
1637            "[ENV] Class " + className + " was loaded via classloader "
1638            + objectId(classLoader));
1639        logHierarchy("[ENV] Ancestry of classloader which loaded " + 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, ClassLoader classLoader) {
1650        if (!isDiagnosticsEnabled()) {
1651            return;
1652        }
1653        ClassLoader systemClassLoader;
1654        if (classLoader != null) {
1655            final String classLoaderString = classLoader.toString();
1656            logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
1657        }
1658
1659        try {
1660            systemClassLoader = ClassLoader.getSystemClassLoader();
1661        } catch(SecurityException ex) {
1662            logDiagnostic(
1663                    prefix + "Security forbids determining the system classloader.");
1664            return;
1665        }
1666        if (classLoader != null) {
1667            StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
1668            for(;;) {
1669                buf.append(objectId(classLoader));
1670                if (classLoader == systemClassLoader) {
1671                    buf.append(" (SYSTEM) ");
1672                }
1673
1674                try {
1675                    classLoader = classLoader.getParent();
1676                } catch(SecurityException ex) {
1677                    buf.append(" --> SECRET");
1678                    break;
1679                }
1680
1681                buf.append(" --> ");
1682                if (classLoader == null) {
1683                    buf.append("BOOT");
1684                    break;
1685                }
1686            }
1687            logDiagnostic(buf.toString());
1688        }
1689    }
1690
1691    /**
1692     * Returns a string that uniquely identifies the specified object, including
1693     * its class.
1694     * <p>
1695     * The returned string is of form "classname@hashcode", ie is the same as
1696     * the return value of the Object.toString() method, but works even when
1697     * the specified object's class has overidden the toString method.
1698     *
1699     * @param o may be null.
1700     * @return a string of form classname@hashcode, or "null" if param o is null.
1701     * @since 1.1
1702     */
1703    public static String objectId(Object o) {
1704        if (o == null) {
1705            return "null";
1706        } else {
1707            return o.getClass().getName() + "@" + System.identityHashCode(o);
1708        }
1709    }
1710
1711    // ----------------------------------------------------------------------
1712    // Static initialiser block to perform initialisation at class load time.
1713    //
1714    // We can't do this in the class constructor, as there are many
1715    // static methods on this class that can be called before any
1716    // LogFactory instances are created, and they depend upon this
1717    // stuff having been set up.
1718    //
1719    // Note that this block must come after any variable declarations used
1720    // by any methods called from this block, as we want any static initialiser
1721    // associated with the variable to run first. If static initialisers for
1722    // variables run after this code, then (a) their value might be needed
1723    // by methods called from here, and (b) they might *override* any value
1724    // computed here!
1725    //
1726    // So the wisest thing to do is just to place this code at the very end
1727    // of the class file.
1728    // ----------------------------------------------------------------------
1729
1730    static {
1731        // note: it's safe to call methods before initDiagnostics.
1732        thisClassLoader = getClassLoader(LogFactory.class);
1733        initDiagnostics();
1734        logClassLoaderEnvironment(LogFactory.class);
1735        factories = createFactoryStore();
1736        if (isDiagnosticsEnabled()) {
1737            logDiagnostic("BOOTSTRAP COMPLETED");
1738        }
1739    }
1740}
1741