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