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