View Javadoc

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