001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */ 
017    
018    package org.apache.commons.logging;
019    
020    
021    import java.io.BufferedReader;
022    import java.io.FileOutputStream;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.InputStreamReader;
026    import java.io.PrintStream;
027    import java.lang.reflect.InvocationTargetException;
028    import java.lang.reflect.Method;
029    import java.net.URL;
030    import java.security.AccessController;
031    import java.security.PrivilegedAction;
032    import java.util.Enumeration;
033    import java.util.Hashtable;
034    import java.util.Properties;
035    
036    
037    /**
038     * <p>Factory for creating {@link Log} instances, with discovery and
039     * configuration features similar to that employed by standard Java APIs
040     * such as JAXP.</p>
041     * 
042     * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
043     * based on the SAXParserFactory and DocumentBuilderFactory implementations
044     * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
045     *
046     * @author Craig R. McClanahan
047     * @author Costin Manolache
048     * @author Richard A. Sitze
049     * @version $Revision: 593798 $ $Date: 2007-11-10 18:40:43 +0100 $
050     */
051    
052    public abstract class LogFactory {
053        // Implementation note re AccessController usage
054        //
055        // It is important to keep code invoked via an AccessController to small
056        // auditable blocks. Such code must carefully evaluate all user input
057        // (parameters, system properties, config file contents, etc). As an 
058        // example, a Log implementation should not write to its logfile
059        // with an AccessController anywhere in the call stack, otherwise an
060        // insecure application could configure the log implementation to write
061        // to a protected file using the privileges granted to JCL rather than
062        // to the calling application.
063        //
064        // Under no circumstance should a non-private method return data that is
065        // retrieved via an AccessController. That would allow an insecure app
066        // to invoke that method and obtain data that it is not permitted to have.
067        //
068        // Invoking user-supplied code with an AccessController set is not a major
069        // issue (eg invoking the constructor of the class specified by 
070        // HASHTABLE_IMPLEMENTATION_PROPERTY). That class will be in a different
071        // trust domain, and therefore must have permissions to do whatever it
072        // is trying to do regardless of the permissions granted to JCL. There is
073        // a slight issue in that untrusted code may point that environment var
074        // to another trusted library, in which case the code runs if both that
075        // lib and JCL have the necessary permissions even when the untrusted
076        // caller does not. That's a pretty hard route to exploit though.
077    
078    
079        // ----------------------------------------------------- Manifest Constants
080    
081        /**
082         * The name (<code>priority</code>) of the key in the config file used to 
083         * specify the priority of that particular config file. The associated value 
084         * is a floating-point number; higher values take priority over lower values.
085         */
086        public static final String PRIORITY_KEY = "priority";
087    
088        /**
089         * The name (<code>use_tccl</code>) of the key in the config file used 
090         * to specify whether logging classes should be loaded via the thread 
091         * context class loader (TCCL), or not. By default, the TCCL is used.
092         */
093        public static final String TCCL_KEY = "use_tccl";
094    
095        /**
096         * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property 
097         * used to identify the LogFactory implementation
098         * class name. This can be used as a system property, or as an entry in a
099         * 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    }