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