View Javadoc

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  
17  package org.apache.commons.logging;
18  
19  
20  import java.io.BufferedReader;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.io.PrintStream;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.net.URL;
29  import java.security.AccessController;
30  import java.security.PrivilegedAction;
31  import java.util.Enumeration;
32  import java.util.Hashtable;
33  import 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  
51  public 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         return (getFactory().getInstance(clazz));
646 
647     }
648 
649 
650     /***
651      * Convenience method to return a named logger, without the application
652      * having to care about factories.
653      *
654      * @param name Logical name of the <code>Log</code> instance to be
655      *  returned (the meaning of this name is only known to the underlying
656      *  logging implementation that is being wrapped)
657      *
658      * @exception LogConfigurationException if a suitable <code>Log</code>
659      *  instance cannot be returned
660      */
661     public static Log getLog(String name)
662         throws LogConfigurationException {
663 
664         return (getFactory().getInstance(name));
665 
666     }
667 
668 
669     /***
670      * Release any internal references to previously created {@link LogFactory}
671      * instances that have been associated with the specified class loader
672      * (if any), after calling the instance method <code>release()</code> on
673      * each of them.
674      *
675      * @param classLoader ClassLoader for which to release the LogFactory
676      */
677     public static void release(ClassLoader classLoader) {
678 
679         if (isDiagnosticsEnabled()) {
680             logDiagnostic("Releasing factory for classloader " + objectId(classLoader));
681         }
682         synchronized (factories) {
683             if (classLoader == null) {
684                 if (nullClassLoaderFactory != null) {
685                     nullClassLoaderFactory.release();
686                     nullClassLoaderFactory = null;
687                 }
688             } else {
689                 LogFactory factory = (LogFactory) factories.get(classLoader);
690                 if (factory != null) {
691                     factory.release();
692                     factories.remove(classLoader);
693                 }
694             }
695         }
696 
697     }
698 
699 
700     /***
701      * Release any internal references to previously created {@link LogFactory}
702      * instances, after calling the instance method <code>release()</code> on
703      * each of them.  This is useful in environments like servlet containers,
704      * which implement application reloading by throwing away a ClassLoader.
705      * Dangling references to objects in that class loader would prevent
706      * garbage collection.
707      */
708     public static void releaseAll() {
709 
710         if (isDiagnosticsEnabled()) {
711             logDiagnostic("Releasing factory for all classloaders.");
712         }
713         synchronized (factories) {
714             Enumeration elements = factories.elements();
715             while (elements.hasMoreElements()) {
716                 LogFactory element = (LogFactory) elements.nextElement();
717                 element.release();
718             }
719             factories.clear();
720 
721             if (nullClassLoaderFactory != null) {
722                 nullClassLoaderFactory.release();
723                 nullClassLoaderFactory = null;
724             }
725         }
726 
727     }
728 
729 
730     // ------------------------------------------------------ Protected Methods
731 
732     /***
733      * Safely get access to the classloader for the specified class.
734      * <p>
735      * Theoretically, calling getClassLoader can throw a security exception,
736      * and so should be done under an AccessController in order to provide
737      * maximum flexibility. However in practice people don't appear to use
738      * security policies that forbid getClassLoader calls. So for the moment
739      * all code is written to call this method rather than Class.getClassLoader,
740      * so that we could put AccessController stuff in this method without any
741      * disruption later if we need to.
742      * <p>
743      * Even when using an AccessController, however, this method can still
744      * throw SecurityException. Commons-logging basically relies on the
745      * ability to access classloaders, ie a policy that forbids all
746      * classloader access will also prevent commons-logging from working: 
747      * currently this method will throw an exception preventing the entire app
748      * from starting up. Maybe it would be good to detect this situation and
749      * just disable all commons-logging? Not high priority though - as stated
750      * above, security policies that prevent classloader access aren't common.
751      * 
752      * @since 1.1
753      */
754     protected static ClassLoader getClassLoader(Class clazz) {
755         try {
756             return clazz.getClassLoader();
757         } catch(SecurityException ex) {
758             if (isDiagnosticsEnabled()) {
759                 logDiagnostic(
760                         "Unable to get classloader for class '" + clazz
761                         + "' due to security restrictions - " + ex.getMessage());
762             }
763             throw ex;
764         }
765     }
766 
767     /***
768      * Calls LogFactory.directGetContextClassLoader under the control of an
769      * AccessController class. This means that java code running under a
770      * security manager that forbids access to ClassLoaders will still work
771      * if this class is given appropriate privileges, even when the caller
772      * doesn't have such privileges. Without using an AccessController, the
773      * the entire call stack must have the privilege before the call is
774      * allowed.
775      *  
776      * @return the context classloader associated with the current thread,
777      * or null if security doesn't allow it.
778      * 
779      * @throws LogConfigurationException if there was some weird error while
780      * attempting to get the context classloader.
781      * 
782      * @throws SecurityException if the current java security policy doesn't
783      * allow this class to access the context classloader.
784      */
785     protected static ClassLoader getContextClassLoader()
786         throws LogConfigurationException {
787 
788         return (ClassLoader)AccessController.doPrivileged(
789             new PrivilegedAction() {
790                 public Object run() {
791                     return directGetContextClassLoader();
792                 }
793             });
794     }
795 
796     /***
797      * Return the thread context class loader if available; otherwise return 
798      * null. 
799      * <p>
800      * Most/all code should call getContextClassLoader rather than calling
801      * this method directly.
802      * <p>
803      * The thread context class loader is available for JDK 1.2
804      * or later, if certain security conditions are met.
805      * <p>
806      * Note that no internal logging is done within this method because
807      * this method is called every time LogFactory.getLogger() is called,
808      * and we don't want too much output generated here.
809      * 
810      * @exception LogConfigurationException if a suitable class loader
811      * cannot be identified.
812      * 
813      * @exception SecurityException if the java security policy forbids
814      * access to the context classloader from one of the classes in the
815      * current call stack. 
816      * @since 1.1
817      */
818     protected static ClassLoader directGetContextClassLoader()
819         throws LogConfigurationException
820     {
821         ClassLoader classLoader = null;
822 
823         try {
824             // Are we running on a JDK 1.2 or later system?
825             Method method = Thread.class.getMethod("getContextClassLoader", 
826                     (Class[]) null);
827 
828             // Get the thread context class loader (if there is one)
829             try {
830                 classLoader = (ClassLoader)method.invoke(Thread.currentThread(), 
831                         (Object[]) null);
832             } catch (IllegalAccessException e) {
833                 throw new LogConfigurationException
834                     ("Unexpected IllegalAccessException", e);
835             } catch (InvocationTargetException e) {
836                 /***
837                  * InvocationTargetException is thrown by 'invoke' when
838                  * the method being invoked (getContextClassLoader) throws
839                  * an exception.
840                  *
841                  * getContextClassLoader() throws SecurityException when
842                  * the context class loader isn't an ancestor of the
843                  * calling class's class loader, or if security
844                  * permissions are restricted.
845                  *
846                  * In the first case (not related), we want to ignore and
847                  * keep going.  We cannot help but also ignore the second
848                  * with the logic below, but other calls elsewhere (to
849                  * obtain a class loader) will trigger this exception where
850                  * we can make a distinction.
851                  */
852                 if (e.getTargetException() instanceof SecurityException) {
853                     ;  // ignore
854                 } else {
855                     // Capture 'e.getTargetException()' exception for details
856                     // alternate: log 'e.getTargetException()', and pass back 'e'.
857                     throw new LogConfigurationException
858                         ("Unexpected InvocationTargetException", e.getTargetException());
859                 }
860             }
861         } catch (NoSuchMethodException e) {
862             // Assume we are running on JDK 1.1
863             classLoader = getClassLoader(LogFactory.class);
864 
865             // We deliberately don't log a message here to outputStream;
866             // this message would be output for every call to LogFactory.getLog()
867             // when running on JDK1.1
868             //
869             // if (outputStream != null) {
870             //    outputStream.println(
871             //        "Method Thread.getContextClassLoader does not exist;"
872             //         + " assuming this is JDK 1.1, and that the context"
873             //         + " classloader is the same as the class that loaded"
874             //         + " the concrete LogFactory class.");
875             // }
876             
877         }
878 
879         // Return the selected class loader
880         return classLoader;
881     }
882 
883     /***
884      * Check cached factories (keyed by contextClassLoader)
885      *
886      * @param contextClassLoader is the context classloader associated
887      * with the current thread. This allows separate LogFactory objects
888      * per component within a container, provided each component has
889      * a distinct context classloader set. This parameter may be null
890      * in JDK1.1, and in embedded systems where jcl-using code is
891      * placed in the bootclasspath.
892      * 
893      * @return the factory associated with the specified classloader if
894      * one has previously been created, or null if this is the first time
895      * we have seen this particular classloader.
896      */
897     private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
898     {
899         LogFactory factory = null;
900 
901         if (contextClassLoader == null) {
902             // We have to handle this specially, as factories is a Hashtable
903             // and those don't accept null as a key value.
904             //
905             // nb: nullClassLoaderFactory might be null. That's ok.
906             factory = nullClassLoaderFactory;
907         } else {
908             factory = (LogFactory) factories.get(contextClassLoader);
909         }
910 
911         return factory;
912     }
913 
914     /***
915      * Remember this factory, so later calls to LogFactory.getCachedFactory
916      * can return the previously created object (together with all its
917      * cached Log objects).
918      *
919      * @param classLoader should be the current context classloader. Note that
920      * this can be null under some circumstances; this is ok.
921      *
922      * @param factory should be the factory to cache. This should never be null.
923      */
924     private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
925     {
926         // Ideally we would assert(factory != null) here. However reporting
927         // errors from within a logging implementation is a little tricky!
928 
929         if (factory != null) {
930             if (classLoader == null) {
931                 nullClassLoaderFactory = factory;
932             } else {
933                 factories.put(classLoader, factory);
934             }
935         }
936     }
937 
938     /***
939      * Return a new instance of the specified <code>LogFactory</code>
940      * implementation class, loaded by the specified class loader.
941      * If that fails, try the class loader used to load this
942      * (abstract) LogFactory.
943      * <p>
944      * <h2>ClassLoader conflicts</h2>
945      * Note that there can be problems if the specified ClassLoader is not the 
946      * same as the classloader that loaded this class, ie when loading a
947      * concrete LogFactory subclass via a context classloader.
948      * <p>
949      * The problem is the same one that can occur when loading a concrete Log
950      * subclass via a context classloader.
951      * <p>
952      * The problem occurs when code running in the context classloader calls
953      * class X which was loaded via a parent classloader, and class X then calls
954      * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
955      * class X was loaded via the parent, it binds to LogFactory loaded via
956      * the parent. When the code in this method finds some LogFactoryYYYY
957      * class in the child (context) classloader, and there also happens to be a
958      * LogFactory class defined in the child classloader, then LogFactoryYYYY
959      * will be bound to LogFactory@childloader. It cannot be cast to
960      * LogFactory@parentloader, ie this method cannot return the object as
961      * the desired type. Note that it doesn't matter if the LogFactory class
962      * in the child classloader is identical to the LogFactory class in the
963      * parent classloader, they are not compatible.
964      * <p>
965      * The solution taken here is to simply print out an error message when
966      * this occurs then throw an exception. The deployer of the application
967      * must ensure they remove all occurrences of the LogFactory class from
968      * the child classloader in order to resolve the issue. Note that they
969      * do not have to move the custom LogFactory subclass; that is ok as
970      * long as the only LogFactory class it can find to bind to is in the
971      * parent classloader.
972      * <p>
973      * @param factoryClass Fully qualified name of the <code>LogFactory</code>
974      *  implementation class
975      * @param classLoader ClassLoader from which to load this class
976      * @param contextClassLoader is the context that this new factory will
977      * manage logging for.
978      *
979      * @exception LogConfigurationException if a suitable instance
980      *  cannot be created
981      * @since 1.1
982      */
983     protected static LogFactory newFactory(final String factoryClass,
984                                            final ClassLoader classLoader,
985                                            final ClassLoader contextClassLoader)
986         throws LogConfigurationException
987     {
988         // Note that any unchecked exceptions thrown by the createFactory
989         // method will propagate out of this method; in particular a
990         // ClassCastException can be thrown.
991         Object result = AccessController.doPrivileged(
992             new PrivilegedAction() {
993                 public Object run() {
994                     return createFactory(factoryClass, classLoader);
995                 }
996             });
997 
998         if (result instanceof LogConfigurationException) {
999             LogConfigurationException ex = (LogConfigurationException) result;
1000             if (isDiagnosticsEnabled()) {
1001                 logDiagnostic(
1002                         "An error occurred while loading the factory class:"
1003                         + ex.getMessage());
1004             }
1005             throw ex;
1006         }
1007         if (isDiagnosticsEnabled()) {
1008             logDiagnostic(
1009                     "Created object " + objectId(result)
1010                     + " to manage classloader " + objectId(contextClassLoader));
1011         }
1012         return (LogFactory)result;
1013     }
1014 
1015     /***
1016      * Method provided for backwards compatibility; see newFactory version that
1017      * takes 3 parameters.
1018      * <p>
1019      * This method would only ever be called in some rather odd situation.
1020      * Note that this method is static, so overriding in a subclass doesn't
1021      * have any effect unless this method is called from a method in that
1022      * subclass. However this method only makes sense to use from the
1023      * getFactory method, and as that is almost always invoked via
1024      * LogFactory.getFactory, any custom definition in a subclass would be
1025      * pointless. Only a class with a custom getFactory method, then invoked
1026      * directly via CustomFactoryImpl.getFactory or similar would ever call
1027      * this. Anyway, it's here just in case, though the "managed class loader"
1028      * value output to the diagnostics will not report the correct value.
1029      */
1030     protected static LogFactory newFactory(final String factoryClass,
1031                                            final ClassLoader classLoader) {
1032 	    return newFactory(factoryClass, classLoader, null);
1033     }
1034 
1035     /***
1036      * Implements the operations described in the javadoc for newFactory.
1037      * 
1038      * @param factoryClass
1039      * 
1040      * @param classLoader used to load the specified factory class. This is
1041      * expected to be either the TCCL or the classloader which loaded this
1042      * class. Note that the classloader which loaded this class might be
1043      * "null" (ie the bootloader) for embedded systems.
1044      * 
1045      * @return either a LogFactory object or a LogConfigurationException object.
1046      * @since 1.1
1047      */
1048     protected static Object createFactory(String factoryClass, ClassLoader classLoader) {
1049 
1050         // This will be used to diagnose bad configurations
1051         // and allow a useful message to be sent to the user
1052         Class logFactoryClass = null;
1053         try {
1054             if (classLoader != null) {
1055                 try {
1056                     // First the given class loader param (thread class loader)
1057 
1058                     // Warning: must typecast here & allow exception
1059                     // to be generated/caught & recast properly.
1060                     logFactoryClass = classLoader.loadClass(factoryClass);
1061                     if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
1062                         if (isDiagnosticsEnabled()) {
1063                             logDiagnostic(
1064                                     "Loaded class " + logFactoryClass.getName()
1065                                     + " from classloader " + objectId(classLoader));
1066                         }
1067                     } else {
1068                         //
1069                         // This indicates a problem with the ClassLoader tree.
1070                         // An incompatible ClassLoader was used to load the 
1071                         // implementation. 
1072                         // As the same classes
1073                         // must be available in multiple class loaders,
1074                         // it is very likely that multiple JCL jars are present.
1075                         // The most likely fix for this
1076                         // problem is to remove the extra JCL jars from the 
1077                         // ClassLoader hierarchy. 
1078                         //
1079                         if (isDiagnosticsEnabled()) {
1080                             logDiagnostic(
1081                                     "Factory class " + logFactoryClass.getName()
1082                                 + " loaded from classloader " + objectId(logFactoryClass.getClassLoader())
1083                                 + " does not extend '" + LogFactory.class.getName()
1084                                 + "' as loaded by this classloader.");
1085                             logHierarchy("[BAD CL TREE] ", classLoader);
1086                         }
1087                     }
1088                     
1089                     return (LogFactory) logFactoryClass.newInstance();
1090 
1091                 } catch (ClassNotFoundException ex) {
1092                     if (classLoader == thisClassLoader) {
1093                         // Nothing more to try, onwards.
1094                         if (isDiagnosticsEnabled()) {
1095                             logDiagnostic(
1096                                     "Unable to locate any class called '" + factoryClass
1097                                     + "' via classloader " + objectId(classLoader));
1098                         }
1099                         throw ex;
1100                     }
1101                     // ignore exception, continue
1102                 } catch (NoClassDefFoundError e) {
1103                     if (classLoader == thisClassLoader) {
1104                         // Nothing more to try, onwards.
1105                         if (isDiagnosticsEnabled()) {
1106                             logDiagnostic(
1107                                     "Class '" + factoryClass + "' cannot be loaded"
1108                                     + " via classloader " + objectId(classLoader)
1109                                     + " - it depends on some other class that cannot"
1110                                     + " be found.");
1111                         }
1112                         throw e;
1113                     }
1114                     // ignore exception, continue
1115                 } catch(ClassCastException e) {
1116                     if (classLoader == thisClassLoader) {
1117                         // There's no point in falling through to the code below that
1118                         // tries again with thisClassLoader, because we've just tried
1119                         // loading with that loader (not the TCCL). Just throw an
1120                         // appropriate exception here.
1121 
1122                     	final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
1123                         
1124                         //
1125                         // Construct a good message: users may not actual expect that a custom implementation 
1126                         // has been specified. Several well known containers use this mechanism to adapt JCL 
1127                         // to their native logging system. 
1128                         // 
1129                         String msg = 
1130                             "The application has specified that a custom LogFactory implementation should be used but " +
1131                             "Class '" + factoryClass + "' cannot be converted to '"
1132                             + LogFactory.class.getName() + "'. ";
1133                         if (implementsLogFactory) {
1134                             msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " +
1135                     		"Background can be found in http://jakarta.apache.org/commons/logging/tech.html. " +
1136                     		"If you have not explicitly specified a custom LogFactory then it is likely that " +
1137                     		"the container has set one without your knowledge. " +
1138                     		"In this case, consider using the commons-logging-adapters.jar file or " +
1139                     		"specifying the standard LogFactory from the command line. ";
1140                         } else {
1141                         	msg = msg + "Please check the custom implementation. ";
1142                         }
1143                         msg = msg + "Help can be found @http://jakarta.apache.org/commons/logging/troubleshooting.html.";
1144                         
1145                         if (isDiagnosticsEnabled()) {
1146                             logDiagnostic(msg);
1147                         }
1148                         
1149                         ClassCastException ex = new ClassCastException(msg);
1150                         throw ex;
1151                     }
1152                     
1153                     // Ignore exception, continue. Presumably the classloader was the
1154                     // TCCL; the code below will try to load the class via thisClassLoader.
1155                     // This will handle the case where the original calling class is in
1156                     // a shared classpath but the TCCL has a copy of LogFactory and the
1157                     // specified LogFactory implementation; we will fall back to using the
1158                     // LogFactory implementation from the same classloader as this class.
1159                     //
1160                     // Issue: this doesn't handle the reverse case, where this LogFactory
1161                     // is in the webapp, and the specified LogFactory implementation is
1162                     // in a shared classpath. In that case:
1163                     // (a) the class really does implement LogFactory (bad log msg above)
1164                     // (b) the fallback code will result in exactly the same problem.
1165                 }
1166             }
1167 
1168             /* At this point, either classLoader == null, OR
1169              * classLoader was unable to load factoryClass.
1170              *
1171              * In either case, we call Class.forName, which is equivalent
1172              * to LogFactory.class.getClassLoader().load(name), ie we ignore
1173              * the classloader parameter the caller passed, and fall back
1174              * to trying the classloader associated with this class. See the
1175              * javadoc for the newFactory method for more info on the 
1176              * consequences of this.
1177              *
1178              * Notes:
1179              * * LogFactory.class.getClassLoader() may return 'null'
1180              *   if LogFactory is loaded by the bootstrap classloader.
1181              */
1182             // Warning: must typecast here & allow exception
1183             // to be generated/caught & recast properly.
1184             if (isDiagnosticsEnabled()) {
1185                 logDiagnostic(
1186                     "Unable to load factory class via classloader " 
1187                     + objectId(classLoader)
1188                     + " - trying the classloader associated with this LogFactory.");
1189             }
1190             logFactoryClass = Class.forName(factoryClass);
1191             return (LogFactory) logFactoryClass.newInstance();
1192         } catch (Exception e) {
1193             // Check to see if we've got a bad configuration
1194             if (isDiagnosticsEnabled()) {
1195                 logDiagnostic("Unable to create LogFactory instance.");
1196             }
1197             if (logFactoryClass != null
1198                 && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
1199                 
1200                 return new LogConfigurationException(
1201                     "The chosen LogFactory implementation does not extend LogFactory."
1202                     + " Please check your configuration.",
1203                     e);
1204             }
1205             return new LogConfigurationException(e);
1206         }
1207     }
1208 
1209     /***
1210      * Determines whether the given class actually implements <code>LogFactory</code>.
1211      * Diagnostic information is also logged.
1212      * <p>
1213      * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
1214      * of incompatibility. The test used is whether the class is assignable from
1215      * the <code>LogFactory</code> class loaded by the class's classloader.
1216      * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
1217      * @return true if the <code>logFactoryClass</code> does extend
1218      * <code>LogFactory</code> when that class is loaded via the same
1219      * classloader that loaded the <code>logFactoryClass</code>.
1220      */
1221     private static boolean implementsLogFactory(Class logFactoryClass) {
1222         boolean implementsLogFactory = false;
1223         if (logFactoryClass != null) {
1224             try {
1225                 ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
1226                 if (logFactoryClassLoader == null) {
1227                     logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
1228                 } else {
1229                     logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
1230                     Class factoryFromCustomLoader
1231                         = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
1232                     implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
1233                     if (implementsLogFactory) {
1234                         logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
1235                                 + " implements LogFactory but was loaded by an incompatible classloader.");
1236                     } else {
1237                         logDiagnostic("[CUSTOM LOG FACTORY] " + 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 implementsLogFactory;
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(final ClassLoader loader,
1282                                                    final String name)
1283     {
1284         return (InputStream)AccessController.doPrivileged(
1285             new PrivilegedAction() {
1286                 public Object run() {
1287                     if (loader != null) {
1288                         return loader.getResourceAsStream(name);
1289                     } else {
1290                         return ClassLoader.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     {
1312         PrivilegedAction action = 
1313             new PrivilegedAction() {
1314                 public Object run() {
1315                     try {
1316                         if (loader != null) {
1317                             return loader.getResources(name);
1318                         } else {
1319                             return ClassLoader.getSystemResources(name);
1320                         }
1321                     } catch(IOException e) {
1322                         if (isDiagnosticsEnabled()) {
1323                             logDiagnostic(
1324                                 "Exception while trying to find configuration file "
1325                                 + name + ":" + e.getMessage());
1326                         }
1327                         return null;
1328                     } catch(NoSuchMethodError e) {
1329                         // we must be running on a 1.1 JVM which doesn't support
1330                         // ClassLoader.getSystemResources; just return null in
1331                         // this case.
1332                         return null;
1333                     }
1334                 }
1335             };
1336         Object result = AccessController.doPrivileged(action);
1337         return (Enumeration) result;
1338     }
1339 
1340     /***
1341      * Given a URL that refers to a .properties file, load that file.
1342      * This is done under an AccessController so that this method will
1343      * succeed when this jarfile is privileged but the caller is not.
1344      * This method must therefore remain private to avoid security issues.
1345      * <p>
1346      * Null is returned if the URL cannot be opened.
1347      */
1348     private static Properties getProperties(final URL url) {
1349         PrivilegedAction action = 
1350             new PrivilegedAction() {
1351                 public Object run() {
1352                     try {
1353                         InputStream stream = url.openStream();
1354                         if (stream != null) {
1355                             Properties props = new Properties();
1356                             props.load(stream);
1357                             stream.close();
1358                             return props;
1359                         }
1360                     } catch(IOException e) {
1361                         if (isDiagnosticsEnabled()) {
1362                             logDiagnostic("Unable to read URL " + url);
1363                         }
1364                     }
1365 
1366                     return null;
1367                 }
1368             };
1369         return (Properties) AccessController.doPrivileged(action);
1370     }
1371 
1372     /***
1373      * Locate a user-provided configuration file.
1374      * <p>
1375      * The classpath of the specified classLoader (usually the context classloader)
1376      * is searched for properties files of the specified name. If none is found,
1377      * null is returned. If more than one is found, then the file with the greatest
1378      * value for its PRIORITY property is returned. If multiple files have the
1379      * same PRIORITY value then the first in the classpath is returned.
1380      * <p> 
1381      * This differs from the 1.0.x releases; those always use the first one found.
1382      * However as the priority is a new field, this change is backwards compatible.
1383      * <p>
1384      * The purpose of the priority field is to allow a webserver administrator to
1385      * override logging settings in all webapps by placing a commons-logging.properties
1386      * file in a shared classpath location with a priority > 0; this overrides any
1387      * commons-logging.properties files without priorities which are in the
1388      * webapps. Webapps can also use explicit priorities to override a configuration
1389      * file in the shared classpath if needed. 
1390      */
1391     private static final Properties getConfigurationFile(
1392             ClassLoader classLoader, String fileName) {
1393 
1394         Properties props = null;
1395         double priority = 0.0;
1396         URL propsUrl = null;
1397         try {
1398             Enumeration urls = getResources(classLoader, fileName);
1399 
1400             if (urls == null) {
1401                 return null;
1402             }
1403             
1404             while (urls.hasMoreElements()) {
1405                 URL url = (URL) urls.nextElement();
1406                 
1407                 Properties newProps = getProperties(url);
1408                 if (newProps != null) {
1409                     if (props == null) {
1410                         propsUrl = url; 
1411                         props = newProps;
1412                         String priorityStr = props.getProperty(PRIORITY_KEY);
1413                         priority = 0.0;
1414                         if (priorityStr != null) {
1415                             priority = Double.parseDouble(priorityStr);
1416                         }
1417 
1418                         if (isDiagnosticsEnabled()) {
1419                             logDiagnostic(
1420                                 "[LOOKUP] Properties file found at '" + url + "'"
1421                                 + " with priority " + priority); 
1422                         }
1423                     } else {
1424                         String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
1425                         double newPriority = 0.0;
1426                         if (newPriorityStr != null) {
1427                             newPriority = Double.parseDouble(newPriorityStr);
1428                         }
1429 
1430                         if (newPriority > priority) {
1431                             if (isDiagnosticsEnabled()) {
1432                                 logDiagnostic(
1433                                     "[LOOKUP] Properties file at '" + url + "'"
1434                                     + " with priority " + newPriority 
1435                                     + " overrides file at '" + propsUrl + "'"
1436                                     + " with priority " + priority);
1437                             }
1438 
1439                             propsUrl = url; 
1440                             props = newProps;
1441                             priority = newPriority;
1442                         } else {
1443                             if (isDiagnosticsEnabled()) {
1444                                 logDiagnostic(
1445                                     "[LOOKUP] Properties file at '" + url + "'"
1446                                     + " with priority " + newPriority 
1447                                     + " does not override file at '" + propsUrl + "'"
1448                                     + " with priority " + priority);
1449                             }
1450                         }
1451                     }
1452 
1453                 }
1454             }
1455         } catch (SecurityException e) {
1456             if (isDiagnosticsEnabled()) {
1457                 logDiagnostic("SecurityException thrown while trying to find/read config files.");
1458             }
1459         }
1460 
1461         if (isDiagnosticsEnabled()) {
1462             if (props == null) {
1463                 logDiagnostic(
1464                     "[LOOKUP] No properties file of name '" + fileName
1465                     + "' found.");
1466             } else {
1467                 logDiagnostic(
1468                     "[LOOKUP] Properties file of name '" + fileName
1469                     + "' found at '" + propsUrl + '"');
1470             }
1471         }
1472 
1473         return props;
1474     }
1475 
1476     /***
1477      * Determines whether the user wants internal diagnostic output. If so,
1478      * returns an appropriate writer object. Users can enable diagnostic
1479      * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
1480      * a filename, or the special values STDOUT or STDERR. 
1481      */
1482     private static void initDiagnostics() {
1483         String dest;
1484     	try {
1485     	    dest = System.getProperty(DIAGNOSTICS_DEST_PROPERTY);
1486     	    if (dest == null) {
1487     	        return;
1488     	    }
1489     	} catch(SecurityException ex) {
1490     	    // We must be running in some very secure environment.
1491     	    // We just have to assume output is not wanted..
1492     	    return;
1493     	}
1494     	
1495     	if (dest.equals("STDOUT")) {
1496     	    diagnosticsStream = System.out;
1497     	} else if (dest.equals("STDERR")) {
1498     	    diagnosticsStream = System.err;
1499     	} else {
1500     	    try {
1501                 // open the file in append mode
1502     	        FileOutputStream fos = new FileOutputStream(dest, true);
1503     	        diagnosticsStream = new PrintStream(fos);
1504     	    } catch(IOException ex) {
1505     	        // We should report this to the user - but how?
1506     	        return;
1507     	    }
1508     	}
1509 
1510         // In order to avoid confusion where multiple instances of JCL are
1511         // being used via different classloaders within the same app, we
1512         // ensure each logged message has a prefix of form
1513         // [LogFactory from classloader OID]
1514         //
1515         // Note that this prefix should be kept consistent with that 
1516         // in LogFactoryImpl. However here we don't need to output info
1517         // about the actual *instance* of LogFactory, as all methods that
1518         // output diagnostics from this class are static.
1519         String classLoaderName;
1520         try {
1521             ClassLoader classLoader = thisClassLoader;
1522             if (thisClassLoader == null) {
1523                 classLoaderName = "BOOTLOADER";
1524             } else {
1525                 classLoaderName = objectId(classLoader);
1526             }
1527         } catch(SecurityException e) {
1528             classLoaderName = "UNKNOWN";
1529         }
1530         diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
1531     }
1532 
1533     /***
1534      * Indicates true if the user has enabled internal logging.
1535      * <p>
1536      * By the way, sorry for the incorrect grammar, but calling this method
1537      * areDiagnosticsEnabled just isn't java beans style.
1538      * 
1539      * @return true if calls to logDiagnostic will have any effect.
1540      * @since 1.1
1541      */
1542     protected static boolean isDiagnosticsEnabled() {
1543         return diagnosticsStream != null;
1544     }
1545 
1546     /***
1547      * Write the specified message to the internal logging destination.
1548      * <p>
1549      * Note that this method is private; concrete subclasses of this class
1550      * should not call it because the diagnosticPrefix string this
1551      * method puts in front of all its messages is LogFactory@....,
1552      * while subclasses should put SomeSubClass@...
1553      * <p>
1554      * Subclasses should instead compute their own prefix, then call
1555      * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
1556      * fine for subclasses.
1557      * <p>
1558      * Note that it is safe to call this method before initDiagnostics
1559      * is called; any output will just be ignored (as isDiagnosticsEnabled
1560      * will return false).
1561      * 
1562      * @param msg is the diagnostic message to be output.
1563      */
1564     private static final void logDiagnostic(String msg) {
1565         if (diagnosticsStream != null) {
1566             diagnosticsStream.print(diagnosticPrefix);
1567             diagnosticsStream.println(msg);
1568             diagnosticsStream.flush();
1569         }
1570     }
1571 
1572     /***
1573      * Write the specified message to the internal logging destination.
1574      * 
1575      * @param msg is the diagnostic message to be output.
1576      * @since 1.1
1577      */
1578     protected static final void logRawDiagnostic(String msg) {
1579         if (diagnosticsStream != null) {
1580             diagnosticsStream.println(msg);
1581             diagnosticsStream.flush();
1582         }
1583     }
1584 
1585     /***
1586      * Generate useful diagnostics regarding the classloader tree for
1587      * the specified class.
1588      * <p>
1589      * As an example, if the specified class was loaded via a webapp's
1590      * classloader, then you may get the following output:
1591      * <pre>
1592      * Class com.acme.Foo was loaded via classloader 11111
1593      * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT 
1594      * </pre>
1595      * <p>
1596      * This method returns immediately if isDiagnosticsEnabled()
1597      * returns false.
1598      * 
1599      * @param clazz is the class whose classloader + tree are to be
1600      * output.
1601      */
1602     private static void logClassLoaderEnvironment(Class clazz) {
1603         if (!isDiagnosticsEnabled()) {
1604             return;
1605         }
1606         
1607         try {
1608             logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
1609             logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
1610         } catch(SecurityException ex) {
1611             logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
1612         }
1613         
1614         String className = clazz.getName();
1615         ClassLoader classLoader;
1616         
1617         try {
1618             classLoader = getClassLoader(clazz);
1619         } catch(SecurityException ex) {
1620             // not much useful diagnostics we can print here!
1621             logDiagnostic(
1622                 "[ENV] Security forbids determining the classloader for " + className);
1623             return;
1624         }
1625 
1626         logDiagnostic(
1627             "[ENV] Class " + className + " was loaded via classloader "
1628             + objectId(classLoader));
1629         logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader);
1630     }
1631 
1632     /***
1633      * Logs diagnostic messages about the given classloader
1634      * and it's hierarchy. The prefix is prepended to the message
1635      * and is intended to make it easier to understand the logs.
1636      * @param prefix 
1637      * @param classLoader
1638      */
1639     private static void logHierarchy(String prefix, ClassLoader classLoader) {
1640         if (!isDiagnosticsEnabled()) {
1641             return;
1642         }
1643         ClassLoader systemClassLoader;
1644         if (classLoader != null) {
1645             final String classLoaderString = classLoader.toString();
1646             logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
1647         }
1648         
1649         try {
1650             systemClassLoader = ClassLoader.getSystemClassLoader();
1651         } catch(SecurityException ex) {
1652             logDiagnostic(
1653                     prefix + "Security forbids determining the system classloader.");
1654             return;
1655         }        
1656         if (classLoader != null) {
1657             StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
1658             for(;;) {
1659                 buf.append(objectId(classLoader));
1660                 if (classLoader == systemClassLoader) {
1661                     buf.append(" (SYSTEM) ");
1662                 }
1663 
1664                 try {
1665                     classLoader = classLoader.getParent();
1666                 } catch(SecurityException ex) {
1667                     buf.append(" --> SECRET");
1668                     break;
1669                 }
1670 
1671                 buf.append(" --> ");
1672                 if (classLoader == null) {
1673                     buf.append("BOOT");
1674                     break;
1675                 }
1676             }
1677             logDiagnostic(buf.toString());
1678         }
1679     }
1680 
1681     /***
1682      * Returns a string that uniquely identifies the specified object, including
1683      * its class.
1684      * <p>
1685      * The returned string is of form "classname@hashcode", ie is the same as
1686      * the return value of the Object.toString() method, but works even when
1687      * the specified object's class has overidden the toString method.
1688      * 
1689      * @param o may be null.
1690      * @return a string of form classname@hashcode, or "null" if param o is null.
1691      * @since 1.1
1692      */
1693     public static String objectId(Object o) {
1694         if (o == null) {
1695             return "null";
1696         } else {
1697             return o.getClass().getName() + "@" + System.identityHashCode(o);
1698         }
1699     }
1700 
1701     // ----------------------------------------------------------------------
1702     // Static initialiser block to perform initialisation at class load time.
1703     //
1704     // We can't do this in the class constructor, as there are many 
1705     // static methods on this class that can be called before any
1706     // LogFactory instances are created, and they depend upon this
1707     // stuff having been set up.
1708     //
1709     // Note that this block must come after any variable declarations used
1710     // by any methods called from this block, as we want any static initialiser
1711     // associated with the variable to run first. If static initialisers for
1712     // variables run after this code, then (a) their value might be needed
1713     // by methods called from here, and (b) they might *override* any value
1714     // computed here!
1715     //
1716     // So the wisest thing to do is just to place this code at the very end
1717     // of the class file.
1718     // ----------------------------------------------------------------------
1719 
1720     static {
1721         // note: it's safe to call methods before initDiagnostics.
1722         thisClassLoader = getClassLoader(LogFactory.class);
1723         initDiagnostics();
1724         logClassLoaderEnvironment(LogFactory.class);
1725         factories = createFactoryStore();
1726         if (isDiagnosticsEnabled()) {
1727             logDiagnostic("BOOTSTRAP COMPLETED");
1728         }
1729     }
1730 }