Save This Page
Home » slf4j-1.5.5 » org.apache.commons » logging » impl » [javadoc | source]
    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   }

Save This Page
Home » slf4j-1.5.5 » org.apache.commons » logging » impl » [javadoc | source]