View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */ 
17  
18  package org.apache.commons.logging.impl;
19  
20  
21  import java.lang.reflect.Constructor;
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.net.URL;
25  import java.security.AccessController;
26  import java.security.PrivilegedAction;
27  import java.util.Enumeration;
28  import java.util.Hashtable;
29  import java.util.Vector;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogConfigurationException;
33  import org.apache.commons.logging.LogFactory;
34  
35  
36  /**
37   * <p>Concrete subclass of {@link LogFactory} that implements the
38   * following algorithm to dynamically select a logging implementation
39   * class to instantiate a wrapper for.</p>
40   * <ul>
41   * <li>Use a factory configuration attribute named
42   *     <code>org.apache.commons.logging.Log</code> to identify the
43   *     requested implementation class.</li>
44   * <li>Use the <code>org.apache.commons.logging.Log</code> system property
45   *     to identify the requested implementation class.</li>
46   * <li>If <em>Log4J</em> is available, return an instance of
47   *     <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
48   * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
49   *     <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
50   * <li>Otherwise, return an instance of
51   *     <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
52   * </ul>
53   *
54   * <p>If the selected {@link Log} implementation class has a
55   * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
56   * parameter, this method will be called on each newly created instance
57   * to identify the associated factory.  This makes factory configuration
58   * attributes available to the Log instance, if it so desires.</p>
59   *
60   * <p>This factory will remember previously created <code>Log</code> instances
61   * for the same name, and will return them on repeated requests to the
62   * <code>getInstance()</code> method.</p>
63   *
64   * @author Rod Waldhoff
65   * @author Craig R. McClanahan
66   * @author Richard A. Sitze
67   * @author Brian Stansberry
68   * @version $Revision: 581090 $ $Date: 2007-10-02 00:01:06 +0200 (ti, 02 okt 2007) $
69   */
70  
71  public class LogFactoryImpl extends LogFactory {
72  
73  
74      /** Log4JLogger class name */
75      private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
76      /** Jdk14Logger class name */
77      private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
78      /** Jdk13LumberjackLogger class name */
79      private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
80      /** SimpleLog class name */
81      private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
82  
83      private static final String PKG_IMPL="org.apache.commons.logging.impl.";
84      private static final int PKG_LEN = PKG_IMPL.length();
85      
86      // ----------------------------------------------------------- Constructors
87  
88     
89  
90      /**
91       * Public no-arguments constructor required by the lookup mechanism.
92       */
93      public LogFactoryImpl() {
94          super();
95          initDiagnostics();  // method on this object
96          if (isDiagnosticsEnabled()) {
97              logDiagnostic("Instance created.");
98          }
99      }
100 
101 
102     // ----------------------------------------------------- Manifest Constants
103 
104 
105     /**
106      * The name (<code>org.apache.commons.logging.Log</code>) of the system 
107      * property identifying our {@link Log} implementation class.
108      */
109     public static final String LOG_PROPERTY =
110         "org.apache.commons.logging.Log";
111 
112 
113     /**
114      * The deprecated system property used for backwards compatibility with
115      * old versions of JCL.
116      */
117     protected static final String LOG_PROPERTY_OLD =
118         "org.apache.commons.logging.log";
119 
120     /**
121      * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>) 
122      * of the system property which can be set true/false to
123      * determine system behaviour when a bad context-classloader is encountered.
124      * When set to false, a LogConfigurationException is thrown if
125      * LogFactoryImpl is loaded via a child classloader of the TCCL (this
126      * should never happen in sane systems).
127      * 
128      * Default behaviour: true (tolerates bad context classloaders)
129      * 
130      * See also method setAttribute.
131      */
132     public static final String ALLOW_FLAWED_CONTEXT_PROPERTY = 
133         "org.apache.commons.logging.Log.allowFlawedContext";
134 
135     /**
136      * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>) 
137      * of the system property which can be set true/false to
138      * determine system behaviour when a bad logging adapter class is
139      * encountered during logging discovery. When set to false, an
140      * exception will be thrown and the app will fail to start. When set
141      * to true, discovery will continue (though the user might end up
142      * with a different logging implementation than they expected).
143      * 
144      * Default behaviour: true (tolerates bad logging adapters)
145      * 
146      * See also method setAttribute.
147      */
148     public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY = 
149         "org.apache.commons.logging.Log.allowFlawedDiscovery";
150 
151     /**
152      * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>) 
153      * of the system property which can be set true/false to
154      * determine system behaviour when a logging adapter class is
155      * encountered which has bound to the wrong Log class implementation.
156      * When set to false, an exception will be thrown and the app will fail
157      * to start. When set to true, discovery will continue (though the user
158      * might end up with a different logging implementation than they expected).
159      * 
160      * Default behaviour: true (tolerates bad Log class hierarchy)
161      * 
162      * See also method setAttribute.
163      */
164     public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY = 
165         "org.apache.commons.logging.Log.allowFlawedHierarchy";
166 
167 
168     /**
169      * The names of classes that will be tried (in order) as logging
170      * adapters. Each class is expected to implement the Log interface,
171      * and to throw NoClassDefFound or ExceptionInInitializerError when
172      * loaded if the underlying logging library is not available. Any 
173      * other error indicates that the underlying logging library is available
174      * but broken/unusable for some reason.
175      */
176     private static final String[] classesToDiscover = {
177             LOGGING_IMPL_LOG4J_LOGGER,
178             "org.apache.commons.logging.impl.Jdk14Logger",
179             "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
180             "org.apache.commons.logging.impl.SimpleLog"
181     };
182     
183 
184     // ----------------------------------------------------- Instance Variables
185 
186     /**
187      * Determines whether logging classes should be loaded using the thread-context
188      * classloader, or via the classloader that loaded this LogFactoryImpl class.
189      */
190     private boolean useTCCL = true;
191 
192     /**
193      * The string prefixed to every message output by the logDiagnostic method.
194      */
195     private String diagnosticPrefix;
196 
197 
198     /**
199      * Configuration attributes.
200      */
201     protected Hashtable attributes = new Hashtable();
202 
203 
204     /**
205      * The {@link org.apache.commons.logging.Log} instances that have
206      * already been created, keyed by logger name.
207      */
208     protected Hashtable instances = new Hashtable();
209 
210 
211     /**
212      * Name of the class implementing the Log interface.
213      */
214     private String logClassName;
215 
216 
217     /**
218      * The one-argument constructor of the
219      * {@link org.apache.commons.logging.Log}
220      * implementation class that will be used to create new instances.
221      * This value is initialized by <code>getLogConstructor()</code>,
222      * and then returned repeatedly.
223      */
224     protected Constructor logConstructor = null;
225 
226 
227     /**
228      * The signature of the Constructor to be used.
229      */
230     protected Class logConstructorSignature[] =
231     { java.lang.String.class };
232 
233 
234     /**
235      * The one-argument <code>setLogFactory</code> method of the selected
236      * {@link org.apache.commons.logging.Log} method, if it exists.
237      */
238     protected Method logMethod = null;
239 
240 
241     /**
242      * The signature of the <code>setLogFactory</code> method to be used.
243      */
244     protected Class logMethodSignature[] =
245     { LogFactory.class };
246 
247     /**
248      * See getBaseClassLoader and initConfiguration.
249      */
250     private boolean allowFlawedContext;
251     
252     /**
253      * See handleFlawedDiscovery and initConfiguration.
254      */
255     private boolean allowFlawedDiscovery;
256     
257     /**
258      * See handleFlawedHierarchy and initConfiguration.
259      */
260     private boolean allowFlawedHierarchy;
261     
262     // --------------------------------------------------------- Public Methods
263 
264 
265     /**
266      * Return the configuration attribute with the specified name (if any),
267      * or <code>null</code> if there is no such attribute.
268      *
269      * @param name Name of the attribute to return
270      */
271     public Object getAttribute(String name) {
272 
273         return (attributes.get(name));
274 
275     }
276 
277 
278     /**
279      * Return an array containing the names of all currently defined
280      * configuration attributes.  If there are no such attributes, a zero
281      * length array is returned.
282      */
283     public String[] getAttributeNames() {
284 
285         Vector names = new Vector();
286         Enumeration keys = attributes.keys();
287         while (keys.hasMoreElements()) {
288             names.addElement((String) keys.nextElement());
289         }
290         String results[] = new String[names.size()];
291         for (int i = 0; i < results.length; i++) {
292             results[i] = (String) names.elementAt(i);
293         }
294         return (results);
295 
296     }
297 
298 
299     /**
300      * Convenience method to derive a name from the specified class and
301      * call <code>getInstance(String)</code> with it.
302      *
303      * @param clazz Class for which a suitable Log name will be derived
304      *
305      * @exception LogConfigurationException if a suitable <code>Log</code>
306      *  instance cannot be returned
307      */
308     public Log getInstance(Class clazz) throws LogConfigurationException {
309 
310         return (getInstance(clazz.getName()));
311 
312     }
313 
314 
315     /**
316      * <p>Construct (if necessary) and return a <code>Log</code> instance,
317      * using the factory's current set of configuration attributes.</p>
318      *
319      * <p><strong>NOTE</strong> - Depending upon the implementation of
320      * the <code>LogFactory</code> you are using, the <code>Log</code>
321      * instance you are returned may or may not be local to the current
322      * application, and may or may not be returned again on a subsequent
323      * call with the same name argument.</p>
324      *
325      * @param name Logical name of the <code>Log</code> instance to be
326      *  returned (the meaning of this name is only known to the underlying
327      *  logging implementation that is being wrapped)
328      *
329      * @exception LogConfigurationException if a suitable <code>Log</code>
330      *  instance cannot be returned
331      */
332     public Log getInstance(String name) throws LogConfigurationException {
333 
334         Log instance = (Log) instances.get(name);
335         if (instance == null) {
336             instance = newInstance(name);
337             instances.put(name, instance);
338         }
339         return (instance);
340 
341     }
342 
343 
344     /**
345      * Release any internal references to previously created
346      * {@link org.apache.commons.logging.Log}
347      * instances returned by this factory.  This is useful in environments
348      * like servlet containers, which implement application reloading by
349      * throwing away a ClassLoader.  Dangling references to objects in that
350      * class loader would prevent garbage collection.
351      */
352     public void release() {
353 
354         logDiagnostic("Releasing all known loggers");
355         instances.clear();
356     }
357 
358 
359     /**
360      * Remove any configuration attribute associated with the specified name.
361      * If there is no such attribute, no action is taken.
362      *
363      * @param name Name of the attribute to remove
364      */
365     public void removeAttribute(String name) {
366 
367         attributes.remove(name);
368 
369     }
370 
371 
372     /**
373      * Set the configuration attribute with the specified name.  Calling
374      * this with a <code>null</code> value is equivalent to calling
375      * <code>removeAttribute(name)</code>.
376      * <p>
377      * This method can be used to set logging configuration programmatically
378      * rather than via system properties. It can also be used in code running
379      * within a container (such as a webapp) to configure behaviour on a
380      * per-component level instead of globally as system properties would do.
381      * To use this method instead of a system property, call
382      * <pre>
383      * LogFactory.getFactory().setAttribute(...)
384      * </pre>
385      * This must be done before the first Log object is created; configuration
386      * changes after that point will be ignored.
387      * <p>
388      * This method is also called automatically if LogFactory detects a
389      * commons-logging.properties file; every entry in that file is set
390      * automatically as an attribute here.
391      *
392      * @param name Name of the attribute to set
393      * @param value Value of the attribute to set, or <code>null</code>
394      *  to remove any setting for this attribute
395      */
396     public void setAttribute(String name, Object value) {
397 
398         if (logConstructor != null) {
399             logDiagnostic("setAttribute: call too late; configuration already performed.");
400         }
401 
402         if (value == null) {
403             attributes.remove(name);
404         } else {
405             attributes.put(name, value);
406         }
407         
408         if (name.equals(TCCL_KEY)) {
409             useTCCL = Boolean.valueOf(value.toString()).booleanValue();
410         }
411 
412     }
413 
414 
415     // ------------------------------------------------------ 
416     // Static Methods
417     //
418     // These methods only defined as workarounds for a java 1.2 bug;
419     // theoretically none of these are needed.
420     // ------------------------------------------------------ 
421     
422     /**
423      * Gets the context classloader.
424      * This method is a workaround for a java 1.2 compiler bug.
425      * @since 1.1
426      */
427     protected static ClassLoader getContextClassLoader() throws LogConfigurationException {
428         return LogFactory.getContextClassLoader();
429     }
430 
431     
432     /**
433      * Workaround for bug in Java1.2; in theory this method is not needed.
434      * See LogFactory.isDiagnosticsEnabled.
435      */
436     protected static boolean isDiagnosticsEnabled() {
437         return LogFactory.isDiagnosticsEnabled();
438     }
439 
440     
441     /**
442      * Workaround for bug in Java1.2; in theory this method is not needed.
443      * See LogFactory.getClassLoader.
444      * @since 1.1
445      */
446     protected static ClassLoader getClassLoader(Class clazz) {
447         return LogFactory.getClassLoader(clazz);
448     }
449 
450 
451     // ------------------------------------------------------ Protected Methods
452 
453     /**
454      * Calculate and cache a string that uniquely identifies this instance,
455      * including which classloader the object was loaded from.
456      * <p>
457      * This string will later be prefixed to each "internal logging" message
458      * emitted, so that users can clearly see any unexpected behaviour.
459      * <p>
460      * Note that this method does not detect whether internal logging is 
461      * enabled or not, nor where to output stuff if it is; that is all
462      * handled by the parent LogFactory class. This method just computes
463      * its own unique prefix for log messages.
464      */
465     private void initDiagnostics() {
466         // It would be nice to include an identifier of the context classloader
467         // that this LogFactoryImpl object is responsible for. However that
468         // isn't possible as that information isn't available. It is possible
469         // to figure this out by looking at the logging from LogFactory to
470         // see the context & impl ids from when this object was instantiated,
471         // in order to link the impl id output as this object's prefix back to
472         // the context it is intended to manage.
473         // Note that this prefix should be kept consistent with that 
474         // in LogFactory.
475         Class clazz = this.getClass();
476         ClassLoader classLoader = getClassLoader(clazz);
477         String classLoaderName;
478         try {
479             if (classLoader == null) {
480                 classLoaderName = "BOOTLOADER";
481             } else {
482                 classLoaderName = objectId(classLoader);
483             }
484         } catch(SecurityException e) {
485             classLoaderName = "UNKNOWN";
486         }
487         diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] ";
488     }
489 
490     
491     /**
492      * Output a diagnostic message to a user-specified destination (if the
493      * user has enabled diagnostic logging).
494      * 
495      * @param msg diagnostic message
496      * @since 1.1
497      */
498     protected void logDiagnostic(String msg) {
499         if (isDiagnosticsEnabled()) {
500             logRawDiagnostic(diagnosticPrefix + msg);
501         }
502     }
503 
504     /**
505      * Return the fully qualified Java classname of the {@link Log}
506      * implementation we will be using.  
507      * 
508      * @deprecated  Never invoked by this class; subclasses should not assume
509      *              it will be.
510      */
511     protected String getLogClassName() {
512 
513         if (logClassName == null) {
514             discoverLogImplementation(getClass().getName());
515         }
516         
517         return logClassName;
518     }
519 
520 
521     /**
522      * <p>Return the <code>Constructor</code> that can be called to instantiate
523      * new {@link org.apache.commons.logging.Log} instances.</p>
524      *
525      * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
526      * calling this method from more than one thread are ignored, because
527      * the same <code>Constructor</code> instance will ultimately be derived
528      * in all circumstances.</p>
529      *
530      * @exception LogConfigurationException if a suitable constructor
531      *  cannot be returned   
532      * 
533      * @deprecated  Never invoked by this class; subclasses should not assume
534      *              it will be.
535      */
536     protected Constructor getLogConstructor()
537         throws LogConfigurationException {
538 
539         // Return the previously identified Constructor (if any)
540         if (logConstructor == null) {
541             discoverLogImplementation(getClass().getName());
542         }
543 
544         return logConstructor;
545     }
546     
547 
548     /**
549      * Is <em>JDK 1.3 with Lumberjack</em> logging available?   
550      * 
551      * @deprecated  Never invoked by this class; subclasses should not assume
552      *              it will be.
553      */
554     protected boolean isJdk13LumberjackAvailable() {
555         return isLogLibraryAvailable(
556                 "Jdk13Lumberjack",
557                 "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
558     }
559 
560 
561     /**
562      * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging
563      * is available.  Also checks that the <code>Throwable</code> class
564      * supports <code>getStackTrace()</code>, which is required by
565      * Jdk14Logger.</p>  
566      * 
567      * @deprecated  Never invoked by this class; subclasses should not assume
568      *              it will be.
569      */
570     protected boolean isJdk14Available() {
571         return isLogLibraryAvailable(
572                 "Jdk14",
573                 "org.apache.commons.logging.impl.Jdk14Logger");
574     }
575 
576 
577     /**
578      * Is a <em>Log4J</em> implementation available? 
579      * 
580      * @deprecated  Never invoked by this class; subclasses should not assume
581      *              it will be.
582      */
583     protected boolean isLog4JAvailable() {
584         return isLogLibraryAvailable(
585                 "Log4J",
586                 LOGGING_IMPL_LOG4J_LOGGER);
587     }
588 
589 
590     /**
591      * Create and return a new {@link org.apache.commons.logging.Log}
592      * instance for the specified name.
593      *
594      * @param name Name of the new logger
595      *
596      * @exception LogConfigurationException if a new instance cannot
597      *  be created
598      */
599     protected Log newInstance(String name) throws LogConfigurationException {
600 
601         Log instance = null;
602         try {
603             if (logConstructor == null) {
604                 instance = discoverLogImplementation(name);
605             }
606             else {
607                 Object params[] = { name };
608                 instance = (Log) logConstructor.newInstance(params);
609             }
610             
611             if (logMethod != null) {
612                 Object params[] = { this };
613                 logMethod.invoke(instance, params);
614             }
615             
616             return (instance);
617             
618         } catch (LogConfigurationException lce) {
619             
620             // this type of exception means there was a problem in discovery
621             // and we've already output diagnostics about the issue, etc.; 
622             // just pass it on
623             throw (LogConfigurationException) lce;
624             
625         } catch (InvocationTargetException e) {
626             // A problem occurred invoking the Constructor or Method 
627             // previously discovered
628             Throwable c = e.getTargetException();
629             if (c != null) {
630                 throw new LogConfigurationException(c);
631             } else {
632                 throw new LogConfigurationException(e);
633             }
634         } catch (Throwable t) {
635             // A problem occurred invoking the Constructor or Method 
636             // previously discovered
637             throw new LogConfigurationException(t);
638         }
639     }
640     
641 
642     //  ------------------------------------------------------ Private Methods
643     
644     /**
645      * Calls LogFactory.directGetContextClassLoader under the control of an
646      * AccessController class. This means that java code running under a
647      * security manager that forbids access to ClassLoaders will still work
648      * if this class is given appropriate privileges, even when the caller
649      * doesn't have such privileges. Without using an AccessController, the
650      * the entire call stack must have the privilege before the call is
651      * allowed.
652      *  
653      * @return the context classloader associated with the current thread,
654      * or null if security doesn't allow it.
655      * 
656      * @throws LogConfigurationException if there was some weird error while
657      * attempting to get the context classloader.
658      * 
659      * @throws SecurityException if the current java security policy doesn't
660      * allow this class to access the context classloader.
661      */
662     private static ClassLoader getContextClassLoaderInternal()
663     throws LogConfigurationException {
664         return (ClassLoader)AccessController.doPrivileged(
665             new PrivilegedAction() {
666                 public Object run() {
667                     return LogFactory.directGetContextClassLoader();
668                 }
669             });
670     }
671 
672     /**
673      * Read the specified system property, using an AccessController so that 
674      * the property can be read if JCL has been granted the appropriate
675      * security rights even if the calling code has not.
676      * <p>
677      * Take care not to expose the value returned by this method to the
678      * calling application in any way; otherwise the calling app can use that
679      * info to access data that should not be available to it.
680      */
681     private static String getSystemProperty(final String key, final String def)
682     throws SecurityException {
683         return (String) AccessController.doPrivileged(
684                 new PrivilegedAction() {
685                     public Object run() {
686                         return System.getProperty(key, def);
687                     }
688                 });
689     }
690 
691     /**
692      * Fetch the parent classloader of a specified classloader.
693      * <p>
694      * If a SecurityException occurs, null is returned.
695      * <p>
696      * Note that this method is non-static merely so logDiagnostic is available.
697      */
698     private ClassLoader getParentClassLoader(final ClassLoader cl) {
699         try {
700             return (ClassLoader)AccessController.doPrivileged(
701                     new PrivilegedAction() {
702                         public Object run() {
703                             return cl.getParent();
704                         }
705                     });
706         } catch(SecurityException ex) {
707             logDiagnostic("[SECURITY] Unable to obtain parent classloader");
708             return null;
709         }
710         
711     }
712 
713     /**
714      * Utility method to check whether a particular logging library is
715      * present and available for use. Note that this does <i>not</i>
716      * affect the future behaviour of this class.
717      */
718     private boolean isLogLibraryAvailable(String name, String classname) {
719         if (isDiagnosticsEnabled()) {
720             logDiagnostic("Checking for '" + name + "'.");
721         }
722         try {
723             Log log = createLogFromClass(
724                         classname, 
725                         this.getClass().getName(), // dummy category
726                         false);
727 
728             if (log == null) {
729                 if (isDiagnosticsEnabled()) {
730                     logDiagnostic("Did not find '" + name + "'.");
731                 }
732                 return false;
733             } else {
734                 if (isDiagnosticsEnabled()) {
735                     logDiagnostic("Found '" + name + "'.");
736                 }
737                 return true;
738             }
739         } catch(LogConfigurationException e) {
740             if (isDiagnosticsEnabled()) {
741                 logDiagnostic("Logging system '" + name + "' is available but not useable.");
742             }
743             return false;
744         }
745     }
746 
747     /**
748      * Attempt to find an attribute (see method setAttribute) or a 
749      * system property with the provided name and return its value.
750      * <p>
751      * The attributes associated with this object are checked before
752      * system properties in case someone has explicitly called setAttribute,
753      * or a configuration property has been set in a commons-logging.properties
754      * file.
755      * 
756      * @return the value associated with the property, or null.
757      */
758     private String getConfigurationValue(String property) {
759         if (isDiagnosticsEnabled()) {
760             logDiagnostic("[ENV] Trying to get configuration for item " + property);
761         }
762 
763         Object valueObj =  getAttribute(property);
764         if (valueObj != null) {
765             if (isDiagnosticsEnabled()) {
766                 logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property);
767             }
768             return valueObj.toString();
769         }
770         
771         if (isDiagnosticsEnabled()) {
772             logDiagnostic("[ENV] No LogFactory attribute found for " + property);
773         }
774 
775         try {
776             // warning: minor security hole here, in that we potentially read a system
777             // property that the caller cannot, then output it in readable form as a
778             // diagnostic message. However it's only ever JCL-specific properties
779             // involved here, so the harm is truly trivial. 
780             String value = getSystemProperty(property, null);
781             if (value != null) {
782                 if (isDiagnosticsEnabled()) {
783                     logDiagnostic("[ENV] Found system property [" + value + "] for " + property);
784                 }
785                 return value;
786             }
787 
788             if (isDiagnosticsEnabled()) {
789                 logDiagnostic("[ENV] No system property found for property " + property);
790             }
791         } catch (SecurityException e) {
792             if (isDiagnosticsEnabled()) {
793                 logDiagnostic("[ENV] Security prevented reading system property " + property);
794             }
795         }
796 
797         if (isDiagnosticsEnabled()) {
798             logDiagnostic("[ENV] No configuration defined for item " + property);
799         }
800 
801         return null;
802     }
803     
804     /**
805      * Get the setting for the user-configurable behaviour specified by key.
806      * If nothing has explicitly been set, then return dflt.  
807      */
808     private boolean getBooleanConfiguration(String key, boolean dflt) {
809         String val = getConfigurationValue(key);
810         if (val == null)
811             return dflt;
812         return Boolean.valueOf(val).booleanValue();
813     }
814 
815     /**
816      * Initialize a number of variables that control the behaviour of this
817      * class and that can be tweaked by the user. This is done when the first
818      * logger is created, not in the constructor of this class, because we
819      * need to give the user a chance to call method setAttribute in order to
820      * configure this object.
821      */
822     private void initConfiguration() {
823         allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true);
824         allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true);
825         allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true);
826     }
827   
828 
829     /**
830      * Attempts to create a Log instance for the given category name.
831      * Follows the discovery process described in the class javadoc.
832      * 
833      * @param logCategory the name of the log category
834      * 
835      * @throws LogConfigurationException if an error in discovery occurs, 
836      * or if no adapter at all can be instantiated
837      */
838     private Log discoverLogImplementation(String logCategory)
839     throws LogConfigurationException
840     {
841         if (isDiagnosticsEnabled()) {
842             logDiagnostic("Discovering a Log implementation...");
843         }
844         
845         initConfiguration();
846         
847         Log result = null;
848         
849         // See if the user specified the Log implementation to use
850         String specifiedLogClassName = findUserSpecifiedLogClassName();
851 
852         if (specifiedLogClassName != null) {
853             if (isDiagnosticsEnabled()) {
854                 logDiagnostic("Attempting to load user-specified log class '" + 
855                     specifiedLogClassName + "'...");
856             }
857             
858             result = createLogFromClass(specifiedLogClassName,
859                                         logCategory,
860                                         true);
861             if (result == null) {
862                 StringBuffer messageBuffer =  new StringBuffer("User-specified log class '");
863                 messageBuffer.append(specifiedLogClassName);
864                 messageBuffer.append("' cannot be found or is not useable.");
865                 
866                 // Mistyping or misspelling names is a common fault.
867                 // Construct a good error message, if we can
868                 if (specifiedLogClassName != null) {
869                     informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
870                     informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
871                     informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
872                     informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
873                 }
874                 throw new LogConfigurationException(messageBuffer.toString());
875             }
876             
877             return result;
878         }
879         
880         // No user specified log; try to discover what's on the classpath
881         //
882         // Note that we deliberately loop here over classesToDiscover and
883         // expect method createLogFromClass to loop over the possible source
884         // classloaders. The effect is:
885         //   for each discoverable log adapter
886         //      for each possible classloader
887         //          see if it works
888         //
889         // It appears reasonable at first glance to do the opposite: 
890         //   for each possible classloader
891         //     for each discoverable log adapter
892         //        see if it works
893         //
894         // The latter certainly has advantages for user-installable logging
895         // libraries such as log4j; in a webapp for example this code should
896         // first check whether the user has provided any of the possible
897         // logging libraries before looking in the parent classloader. 
898         // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4,
899         // and SimpleLog will always work in any JVM. So the loop would never
900         // ever look for logging libraries in the parent classpath. Yet many
901         // users would expect that putting log4j there would cause it to be
902         // detected (and this is the historical JCL behaviour). So we go with
903         // the first approach. A user that has bundled a specific logging lib
904         // in a webapp should use a commons-logging.properties file or a
905         // service file in META-INF to force use of that logging lib anyway,
906         // rather than relying on discovery.
907         
908         if (isDiagnosticsEnabled()) {
909             logDiagnostic(
910                 "No user-specified Log implementation; performing discovery" +
911                 " using the standard supported logging implementations...");
912         }
913         for(int i=0; (i<classesToDiscover.length) && (result == null); ++i) {
914             result = createLogFromClass(classesToDiscover[i], logCategory, true);
915         }
916         
917         if (result == null) {
918             throw new LogConfigurationException
919                         ("No suitable Log implementation");
920         }
921         
922         return result;        
923     }
924 
925 
926     /**
927      * Appends message if the given name is similar to the candidate.
928      * @param messageBuffer <code>StringBuffer</code> the message should be appended to, 
929      * not null
930      * @param name the (trimmed) name to be test against the candidate, not null
931      * @param candidate the candidate name (not null)
932      */
933     private void informUponSimilarName(final StringBuffer messageBuffer, final String name, 
934             final String candidate) {
935         if (name.equals(candidate)) {
936             // Don't suggest a name that is exactly the same as the one the
937             // user tried...
938             return;
939         }
940 
941         // If the user provides a name that is in the right package, and gets
942         // the first 5 characters of the adapter class right (ignoring case),
943         // then suggest the candidate adapter class name.
944         if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) {
945             messageBuffer.append(" Did you mean '");
946             messageBuffer.append(candidate);
947             messageBuffer.append("'?");
948         }
949     }
950     
951     
952     /**
953      * Checks system properties and the attribute map for 
954      * a Log implementation specified by the user under the 
955      * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}.
956      * 
957      * @return classname specified by the user, or <code>null</code>
958      */
959     private String findUserSpecifiedLogClassName()
960     {
961         if (isDiagnosticsEnabled()) {
962             logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'");
963         }
964         String specifiedClass = (String) getAttribute(LOG_PROPERTY);
965 
966         if (specifiedClass == null) { // @deprecated
967             if (isDiagnosticsEnabled()) {
968                 logDiagnostic("Trying to get log class from attribute '" + 
969                               LOG_PROPERTY_OLD + "'");
970             }
971             specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
972         }
973 
974         if (specifiedClass == null) {
975             if (isDiagnosticsEnabled()) {
976                 logDiagnostic("Trying to get log class from system property '" + 
977                           LOG_PROPERTY + "'");
978             }
979             try {
980                 specifiedClass = getSystemProperty(LOG_PROPERTY, null);
981             } catch (SecurityException e) {
982                 if (isDiagnosticsEnabled()) {
983                     logDiagnostic("No access allowed to system property '" + 
984                         LOG_PROPERTY + "' - " + e.getMessage());
985                 }
986             }
987         }
988 
989         if (specifiedClass == null) { // @deprecated
990             if (isDiagnosticsEnabled()) {
991                 logDiagnostic("Trying to get log class from system property '" + 
992                           LOG_PROPERTY_OLD + "'");
993             }
994             try {
995                 specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null);
996             } catch (SecurityException e) {
997                 if (isDiagnosticsEnabled()) {
998                     logDiagnostic("No access allowed to system property '" + 
999                         LOG_PROPERTY_OLD + "' - " + e.getMessage());
1000                 }
1001             }
1002         }
1003         
1004         // Remove any whitespace; it's never valid in a classname so its
1005         // presence just means a user mistake. As we know what they meant,
1006         // we may as well strip the spaces.
1007         if (specifiedClass != null) {
1008             specifiedClass = specifiedClass.trim();
1009         }
1010 
1011         return specifiedClass;
1012     }
1013 
1014     
1015     /**
1016      * Attempts to load the given class, find a suitable constructor,
1017      * and instantiate an instance of Log.
1018      * 
1019      * @param logAdapterClassName classname of the Log implementation
1020      * 
1021      * @param logCategory  argument to pass to the Log implementation's
1022      * constructor
1023      * 
1024      * @param affectState  <code>true</code> if this object's state should
1025      * be affected by this method call, <code>false</code> otherwise.
1026      * 
1027      * @return  an instance of the given class, or null if the logging
1028      * library associated with the specified adapter is not available.
1029      *                          
1030      * @throws LogConfigurationException if there was a serious error with
1031      * configuration and the handleFlawedDiscovery method decided this
1032      * problem was fatal.
1033      */                                  
1034     private Log createLogFromClass(String logAdapterClassName,
1035                                    String logCategory,
1036                                    boolean affectState) 
1037             throws LogConfigurationException {       
1038 
1039         if (isDiagnosticsEnabled()) {
1040             logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
1041         }
1042         
1043         Object[] params = { logCategory };
1044         Log logAdapter = null;
1045         Constructor constructor = null;
1046         
1047         Class logAdapterClass = null;
1048         ClassLoader currentCL = getBaseClassLoader();
1049         
1050         for(;;) {
1051             // Loop through the classloader hierarchy trying to find
1052             // a viable classloader.
1053             logDiagnostic(
1054                     "Trying to load '"
1055                     + logAdapterClassName
1056                     + "' from classloader "
1057                     + objectId(currentCL));
1058             try {
1059                 if (isDiagnosticsEnabled()) {
1060                     // Show the location of the first occurrence of the .class file
1061                     // in the classpath. This is the location that ClassLoader.loadClass
1062                     // will load the class from -- unless the classloader is doing
1063                     // something weird. 
1064                     URL url;
1065                     String resourceName = logAdapterClassName.replace('.', '/') + ".class";
1066                     if (currentCL != null) {
1067                         url = currentCL.getResource(resourceName );
1068                     } else {
1069                         url = ClassLoader.getSystemResource(resourceName + ".class");
1070                     }
1071 
1072                     if (url == null) {
1073                         logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
1074                     } else {
1075                         logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
1076                     }
1077                 }
1078 
1079                 Class c = null;
1080                 try {
1081                     c = Class.forName(logAdapterClassName, true, currentCL);
1082                 } catch (ClassNotFoundException originalClassNotFoundException) {
1083                     // The current classloader was unable to find the log adapter 
1084                     // in this or any ancestor classloader. There's no point in
1085                     // trying higher up in the hierarchy in this case..
1086                     String msg = "" + originalClassNotFoundException.getMessage();
1087                     logDiagnostic(
1088                         "The log adapter '"
1089                         + logAdapterClassName
1090                         + "' is not available via classloader " 
1091                         + objectId(currentCL)
1092                         + ": "
1093                         + msg.trim());
1094                     try {
1095                         // Try the class classloader.
1096                         // This may work in cases where the TCCL
1097                         // does not contain the code executed or JCL.
1098                         // This behaviour indicates that the application 
1099                         // classloading strategy is not consistent with the
1100                         // Java 1.2 classloading guidelines but JCL can
1101                         // and so should handle this case.
1102                         c = Class.forName(logAdapterClassName);
1103                     } catch (ClassNotFoundException secondaryClassNotFoundException) {
1104                         // no point continuing: this adapter isn't available
1105                         msg = "" + secondaryClassNotFoundException.getMessage();
1106                         logDiagnostic(
1107                             "The log adapter '"
1108                             + logAdapterClassName
1109                             + "' is not available via the LogFactoryImpl class classloader: "
1110                             + msg.trim());
1111                         break;
1112                     }
1113                 }
1114                 
1115                 constructor = c.getConstructor(logConstructorSignature);
1116                 Object o = constructor.newInstance(params);
1117 
1118                 // Note that we do this test after trying to create an instance
1119                 // [rather than testing Log.class.isAssignableFrom(c)] so that
1120                 // we don't complain about Log hierarchy problems when the
1121                 // adapter couldn't be instantiated anyway.
1122                 if (o instanceof Log) {
1123                     logAdapterClass = c;
1124                     logAdapter = (Log) o;
1125                     break;
1126                 }
1127                 
1128                 // Oops, we have a potential problem here. An adapter class
1129                 // has been found and its underlying lib is present too, but
1130                 // there are multiple Log interface classes available making it
1131                 // impossible to cast to the type the caller wanted. We 
1132                 // certainly can't use this logger, but we need to know whether
1133                 // to keep on discovering or terminate now.
1134                 //
1135                 // The handleFlawedHierarchy method will throw 
1136                 // LogConfigurationException if it regards this problem as
1137                 // fatal, and just return if not.
1138                 handleFlawedHierarchy(currentCL, c);
1139             } catch (NoClassDefFoundError e) {
1140                 // We were able to load the adapter but it had references to
1141                 // other classes that could not be found. This simply means that
1142                 // the underlying logger library is not present in this or any
1143                 // ancestor classloader. There's no point in trying higher up
1144                 // in the hierarchy in this case..
1145                 String msg = "" + e.getMessage();
1146                 logDiagnostic(
1147                     "The log adapter '"
1148                     + logAdapterClassName
1149                     + "' is missing dependencies when loaded via classloader "
1150                     + objectId(currentCL)
1151                     + ": "
1152                     + msg.trim());
1153                 break;
1154             } catch (ExceptionInInitializerError e) {
1155                 // A static initializer block or the initializer code associated 
1156                 // with a static variable on the log adapter class has thrown
1157                 // an exception.
1158                 //
1159                 // We treat this as meaning the adapter's underlying logging
1160                 // library could not be found.
1161                 String msg = "" + e.getMessage();
1162                 logDiagnostic(
1163                     "The log adapter '"
1164                     + logAdapterClassName
1165                     + "' is unable to initialize itself when loaded via classloader "
1166                     + objectId(currentCL)
1167                     + ": "
1168                     + msg.trim());
1169                 break;
1170             } catch(LogConfigurationException e) {
1171                 // call to handleFlawedHierarchy above must have thrown
1172                 // a LogConfigurationException, so just throw it on                
1173                 throw e;
1174             } catch(Throwable t) {
1175                 // handleFlawedDiscovery will determine whether this is a fatal
1176                 // problem or not. If it is fatal, then a LogConfigurationException
1177                 // will be thrown.
1178                 handleFlawedDiscovery(logAdapterClassName, currentCL, t);
1179             }
1180                         
1181             if (currentCL == null) {
1182                 break;
1183             }
1184             
1185             // try the parent classloader
1186             // currentCL = currentCL.getParent();
1187             currentCL = getParentClassLoader(currentCL);
1188         }
1189 
1190         if ((logAdapter != null) && affectState) {
1191             // We've succeeded, so set instance fields
1192             this.logClassName   = logAdapterClassName;
1193             this.logConstructor = constructor;
1194             
1195             // Identify the <code>setLogFactory</code> method (if there is one)
1196             try {
1197                 this.logMethod = logAdapterClass.getMethod("setLogFactory",
1198                                                logMethodSignature);
1199                 logDiagnostic("Found method setLogFactory(LogFactory) in '" 
1200                               + logAdapterClassName + "'");
1201             } catch (Throwable t) {
1202                 this.logMethod = null;
1203                 logDiagnostic(
1204                     "[INFO] '" + logAdapterClassName 
1205                     + "' from classloader " + objectId(currentCL)
1206                     + " does not declare optional method "
1207                     + "setLogFactory(LogFactory)");
1208             }
1209             
1210             logDiagnostic(
1211                 "Log adapter '" + logAdapterClassName 
1212                 + "' from classloader " + objectId(logAdapterClass.getClassLoader())
1213                 + " has been selected for use.");
1214         }
1215         
1216         return logAdapter;
1217     }
1218     
1219     
1220     /**
1221      * Return the classloader from which we should try to load the logging
1222      * adapter classes.
1223      * <p>
1224      * This method usually returns the context classloader. However if it
1225      * is discovered that the classloader which loaded this class is a child
1226      * of the context classloader <i>and</i> the allowFlawedContext option
1227      * has been set then the classloader which loaded this class is returned
1228      * instead.
1229      * <p>
1230      * The only time when the classloader which loaded this class is a
1231      * descendant (rather than the same as or an ancestor of the context
1232      * classloader) is when an app has created custom classloaders but
1233      * failed to correctly set the context classloader. This is a bug in
1234      * the calling application; however we provide the option for JCL to
1235      * simply generate a warning rather than fail outright.
1236      * 
1237      */
1238     private ClassLoader getBaseClassLoader() throws LogConfigurationException {
1239         ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class);
1240         
1241         if (useTCCL == false) {
1242             return thisClassLoader;
1243         }
1244 
1245         ClassLoader contextClassLoader = getContextClassLoaderInternal();
1246 
1247         ClassLoader baseClassLoader = getLowestClassLoader(
1248                 contextClassLoader, thisClassLoader);
1249         
1250         if (baseClassLoader == null) {
1251            // The two classloaders are not part of a parent child relationship.
1252            // In some classloading setups (e.g. JBoss with its 
1253            // UnifiedLoaderRepository) this can still work, so if user hasn't
1254            // forbidden it, just return the contextClassLoader.
1255            if (allowFlawedContext) {   
1256               if (isDiagnosticsEnabled()) {
1257                    logDiagnostic(
1258                            "[WARNING] the context classloader is not part of a"
1259                            + " parent-child relationship with the classloader that"
1260                            + " loaded LogFactoryImpl.");
1261               }
1262               // If contextClassLoader were null, getLowestClassLoader() would
1263               // have returned thisClassLoader.  The fact we are here means
1264               // contextClassLoader is not null, so we can just return it.
1265               return contextClassLoader;
1266            }
1267            else {
1268             throw new LogConfigurationException(
1269                 "Bad classloader hierarchy; LogFactoryImpl was loaded via"
1270                 + " a classloader that is not related to the current context"
1271                 + " classloader.");
1272            }           
1273         }
1274 
1275         if (baseClassLoader != contextClassLoader) {
1276             // We really should just use the contextClassLoader as the starting
1277             // point for scanning for log adapter classes. However it is expected
1278             // that there are a number of broken systems out there which create
1279             // custom classloaders but fail to set the context classloader so
1280             // we handle those flawed systems anyway.
1281             if (allowFlawedContext) {
1282                 if (isDiagnosticsEnabled()) {
1283                     logDiagnostic(
1284                             "Warning: the context classloader is an ancestor of the"
1285                             + " classloader that loaded LogFactoryImpl; it should be"
1286                             + " the same or a descendant. The application using"
1287                             + " commons-logging should ensure the context classloader"
1288                             + " is used correctly.");
1289                 }
1290             } else {
1291                 throw new LogConfigurationException(
1292                         "Bad classloader hierarchy; LogFactoryImpl was loaded via"
1293                         + " a classloader that is not related to the current context"
1294                         + " classloader."); 
1295             }
1296         }
1297         
1298         return baseClassLoader;
1299     }
1300 
1301     /**
1302      * Given two related classloaders, return the one which is a child of
1303      * the other.
1304      * <p>
1305      * @param c1 is a classloader (including the null classloader)
1306      * @param c2 is a classloader (including the null classloader)
1307      * 
1308      * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor,
1309      * and null if neither is an ancestor of the other.
1310      */
1311     private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) {
1312         // TODO: use AccessController when dealing with classloaders here
1313         
1314         if (c1 == null)
1315             return c2;
1316         
1317         if (c2 == null)
1318             return c1;
1319         
1320         ClassLoader current;
1321 
1322         // scan c1's ancestors to find c2
1323         current = c1;
1324         while (current != null) {
1325             if (current == c2)
1326                 return c1;
1327             current = current.getParent();
1328         }
1329        
1330         // scan c2's ancestors to find c1
1331         current = c2;
1332         while (current != null) {
1333             if (current == c1)
1334                 return c2;
1335             current = current.getParent();
1336         }
1337 
1338         return null;
1339     }
1340 
1341     /**
1342      * Generates an internal diagnostic logging of the discovery failure and 
1343      * then throws a <code>LogConfigurationException</code> that wraps 
1344      * the passed <code>Throwable</code>.
1345      * 
1346      * @param logAdapterClassName is the class name of the Log implementation
1347      * that could not be instantiated. Cannot be <code>null</code>.
1348      * 
1349      * @param classLoader is the classloader that we were trying to load the
1350      * logAdapterClassName from when the exception occurred.
1351      * 
1352      * @param discoveryFlaw is the Throwable created by the classloader
1353      * 
1354      * @throws LogConfigurationException    ALWAYS
1355      */
1356     private void handleFlawedDiscovery(String logAdapterClassName,
1357                                        ClassLoader classLoader,
1358                                        Throwable discoveryFlaw) {
1359         
1360         if (isDiagnosticsEnabled()) {
1361             logDiagnostic("Could not instantiate Log '"
1362                       + logAdapterClassName + "' -- "
1363                       + discoveryFlaw.getClass().getName() + ": "
1364                       + discoveryFlaw.getLocalizedMessage());       
1365 
1366             if (discoveryFlaw instanceof InvocationTargetException ) {
1367                 // Ok, the lib is there but while trying to create a real underlying
1368                 // logger something failed in the underlying lib; display info about
1369                 // that if possible.
1370                 InvocationTargetException ite = (InvocationTargetException)discoveryFlaw;
1371                 Throwable cause = ite.getTargetException();
1372                 if (cause != null) {
1373                     logDiagnostic("... InvocationTargetException: " +
1374                         cause.getClass().getName() + ": " +
1375                         cause.getLocalizedMessage());
1376 
1377                     if (cause instanceof ExceptionInInitializerError) {
1378                         ExceptionInInitializerError eiie = (ExceptionInInitializerError)cause;
1379                         Throwable cause2 = eiie.getException();
1380                         if (cause2 != null) {
1381                             logDiagnostic("... ExceptionInInitializerError: " +
1382                                 cause2.getClass().getName() + ": " +
1383                                 cause2.getLocalizedMessage());
1384                         }
1385                     }
1386                 }
1387             }
1388         }
1389         
1390         if (!allowFlawedDiscovery) {
1391             throw new LogConfigurationException(discoveryFlaw);
1392         }
1393     }
1394 
1395     
1396     /**
1397      * Report a problem loading the log adapter, then either return 
1398      * (if the situation is considered recoverable) or throw a
1399      * LogConfigurationException.
1400      *  <p>
1401      * There are two possible reasons why we successfully loaded the 
1402      * specified log adapter class then failed to cast it to a Log object:
1403      * <ol>
1404      * <li>the specific class just doesn't implement the Log interface 
1405      *     (user screwed up), or
1406      * <li> the specified class has bound to a Log class loaded by some other
1407      *      classloader; Log@classloaderX cannot be cast to Log@classloaderY.
1408      * </ol>
1409      * <p>
1410      * Here we try to figure out which case has occurred so we can give the
1411      * user some reasonable feedback.
1412      * 
1413      * @param badClassLoader is the classloader we loaded the problem class from,
1414      * ie it is equivalent to badClass.getClassLoader().
1415      * 
1416      * @param badClass is a Class object with the desired name, but which 
1417      * does not implement Log correctly.
1418      * 
1419      * @throws LogConfigurationException when the situation
1420      * should not be recovered from.
1421      */
1422     private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass)
1423     throws LogConfigurationException {
1424 
1425         boolean implementsLog = false;
1426         String logInterfaceName = Log.class.getName();
1427         Class interfaces[] = badClass.getInterfaces();
1428         for (int i = 0; i < interfaces.length; i++) {
1429             if (logInterfaceName.equals(interfaces[i].getName())) {
1430                 implementsLog = true;
1431                 break;
1432             }
1433         }
1434         
1435         if (implementsLog) {
1436             // the class does implement an interface called Log, but
1437             // it is in the wrong classloader
1438             if (isDiagnosticsEnabled()) {
1439                 try {
1440                     ClassLoader logInterfaceClassLoader = getClassLoader(Log.class);
1441                     logDiagnostic(
1442                         "Class '" + badClass.getName()
1443                         + "' was found in classloader " 
1444                         + objectId(badClassLoader)
1445                         + ". It is bound to a Log interface which is not"
1446                         + " the one loaded from classloader "
1447                         + objectId(logInterfaceClassLoader));
1448                 } catch (Throwable t) {
1449                     logDiagnostic(
1450                         "Error while trying to output diagnostics about"
1451                         + " bad class '" + badClass + "'");
1452                 }
1453             }
1454             
1455             if (!allowFlawedHierarchy) {
1456                 StringBuffer msg = new StringBuffer();
1457                 msg.append("Terminating logging for this context ");
1458                 msg.append("due to bad log hierarchy. ");
1459                 msg.append("You have more than one version of '");
1460                 msg.append(Log.class.getName());
1461                 msg.append("' visible.");
1462                 if (isDiagnosticsEnabled()) {
1463                     logDiagnostic(msg.toString());
1464                 } 
1465                 throw new LogConfigurationException(msg.toString());
1466             }
1467         
1468             if (isDiagnosticsEnabled()) {
1469                 StringBuffer msg = new StringBuffer();
1470                 msg.append("Warning: bad log hierarchy. ");
1471                 msg.append("You have more than one version of '");
1472                 msg.append(Log.class.getName());
1473                 msg.append("' visible.");
1474                 logDiagnostic(msg.toString());
1475             }
1476         } else {
1477             // this is just a bad adapter class
1478             if (!allowFlawedDiscovery) {
1479                 StringBuffer msg = new StringBuffer();
1480                 msg.append("Terminating logging for this context. ");
1481                 msg.append("Log class '");
1482                 msg.append(badClass.getName());
1483                 msg.append("' does not implement the Log interface.");
1484                 if (isDiagnosticsEnabled()) {
1485                     logDiagnostic(msg.toString());
1486                 }
1487                 
1488                 throw new LogConfigurationException(msg.toString());
1489             }
1490 
1491             if (isDiagnosticsEnabled()) {
1492                 StringBuffer msg = new StringBuffer();
1493                 msg.append("[WARNING] Log class '");
1494                 msg.append(badClass.getName());
1495                 msg.append("' does not implement the Log interface.");
1496                 logDiagnostic(msg.toString());
1497             }
1498         }
1499     }
1500 }