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