Home | History | Annotate | Download | only in impl
      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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
   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      */
   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      */
   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      */
   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