LogFactoryImpl xref

View Javadoc

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