Home » commons-logging-1.1.1-src » org.apache.commons » logging » [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;
   19   
   20   
   21   import java.io.BufferedReader;
   22   import java.io.FileOutputStream;
   23   import java.io.IOException;
   24   import java.io.InputStream;
   25   import java.io.InputStreamReader;
   26   import java.io.PrintStream;
   27   import java.lang.reflect.InvocationTargetException;
   28   import java.lang.reflect.Method;
   29   import java.net.URL;
   30   import java.security.AccessController;
   31   import java.security.PrivilegedAction;
   32   import java.util.Enumeration;
   33   import java.util.Hashtable;
   34   import java.util.Properties;
   35   
   36   
   37   /**
   38    * <p>Factory for creating {@link Log} instances, with discovery and
   39    * configuration features similar to that employed by standard Java APIs
   40    * such as JAXP.</p>
   41    * 
   42    * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
   43    * based on the SAXParserFactory and DocumentBuilderFactory implementations
   44    * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
   45    *
   46    * @author Craig R. McClanahan
   47    * @author Costin Manolache
   48    * @author Richard A. Sitze
   49    * @version $Revision: 593798 $ $Date: 2007-11-10 18:40:43 +0100 $
   50    */
   51   
   52   public abstract class LogFactory {
   53       // Implementation note re AccessController usage
   54       //
   55       // It is important to keep code invoked via an AccessController to small
   56       // auditable blocks. Such code must carefully evaluate all user input
   57       // (parameters, system properties, config file contents, etc). As an 
   58       // example, a Log implementation should not write to its logfile
   59       // with an AccessController anywhere in the call stack, otherwise an
   60       // insecure application could configure the log implementation to write
   61       // to a protected file using the privileges granted to JCL rather than
   62       // to the calling application.
   63       //
   64       // Under no circumstance should a non-private method return data that is
   65       // retrieved via an AccessController. That would allow an insecure app
   66       // to invoke that method and obtain data that it is not permitted to have.
   67       //
   68       // Invoking user-supplied code with an AccessController set is not a major
   69       // issue (eg invoking the constructor of the class specified by 
   70       // HASHTABLE_IMPLEMENTATION_PROPERTY). That class will be in a different
   71       // trust domain, and therefore must have permissions to do whatever it
   72       // is trying to do regardless of the permissions granted to JCL. There is
   73       // a slight issue in that untrusted code may point that environment var
   74       // to another trusted library, in which case the code runs if both that
   75       // lib and JCL have the necessary permissions even when the untrusted
   76       // caller does not. That's a pretty hard route to exploit though.
   77   
   78   
   79       // ----------------------------------------------------- Manifest Constants
   80   
   81       /**
   82        * The name (<code>priority</code>) of the key in the config file used to 
   83        * specify the priority of that particular config file. The associated value 
   84        * is a floating-point number; higher values take priority over lower values.
   85        */
   86       public static final String PRIORITY_KEY = "priority";
   87   
   88       /**
   89        * The name (<code>use_tccl</code>) of the key in the config file used 
   90        * to specify whether logging classes should be loaded via the thread 
   91        * context class loader (TCCL), or not. By default, the TCCL is used.
   92        */
   93       public static final String TCCL_KEY = "use_tccl";
   94   
   95       /**
   96        * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property 
   97        * used to identify the LogFactory implementation
   98        * class name. This can be used as a system property, or as an entry in a
   99        * configuration properties file.
  100        */
  101       public static final String FACTORY_PROPERTY =
  102           "org.apache.commons.logging.LogFactory";
  103   
  104       /**
  105        * The fully qualified class name of the fallback <code>LogFactory</code>
  106        * implementation class to use, if no other can be found.
  107        */
  108       public static final String FACTORY_DEFAULT =
  109           "org.apache.commons.logging.impl.LogFactoryImpl";
  110   
  111       /**
  112        * The name (<code>commons-logging.properties</code>) of the properties file to search for.
  113        */
  114       public static final String FACTORY_PROPERTIES =
  115           "commons-logging.properties";
  116   
  117       /**
  118        * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
  119        * 'Service Provider' specification</a>.
  120        * 
  121        */
  122       protected static final String SERVICE_ID =
  123           "META-INF/services/org.apache.commons.logging.LogFactory";
  124   
  125       /**
  126        * The name (<code>org.apache.commons.logging.diagnostics.dest</code>) 
  127        * of the property used to enable internal commons-logging
  128        * diagnostic output, in order to get information on what logging
  129        * implementations are being discovered, what classloaders they 
  130        * are loaded through, etc.
  131        * <p>
  132        * If a system property of this name is set then the value is
  133        * assumed to be the name of a file. The special strings
  134        * STDOUT or STDERR (case-sensitive) indicate output to
  135        * System.out and System.err respectively.
  136        * <p>
  137        * Diagnostic logging should be used only to debug problematic
  138        * configurations and should not be set in normal production use.
  139        */
  140       public static final String DIAGNOSTICS_DEST_PROPERTY =
  141           "org.apache.commons.logging.diagnostics.dest";
  142   
  143       /**
  144        * When null (the usual case), no diagnostic output will be
  145        * generated by LogFactory or LogFactoryImpl. When non-null,
  146        * interesting events will be written to the specified object.
  147        */
  148       private static PrintStream diagnosticsStream = null;
  149       
  150       /**
  151        * A string that gets prefixed to every message output by the
  152        * logDiagnostic method, so that users can clearly see which
  153        * LogFactory class is generating the output.
  154        */
  155       private static String diagnosticPrefix;
  156       
  157       /**
  158        * <p>Setting this system property 
  159        * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>) 
  160        * value allows the <code>Hashtable</code> used to store
  161        * classloaders to be substituted by an alternative implementation.
  162        * </p>
  163        * <p>
  164        * <strong>Note:</strong> <code>LogFactory</code> will print:
  165        * <code><pre>
  166        * [ERROR] LogFactory: Load of custom hashtable failed</em>
  167        * </pre></code>
  168        * to system error and then continue using a standard Hashtable.
  169        * </p>
  170        * <p>
  171        * <strong>Usage:</strong> Set this property when Java is invoked
  172        * and <code>LogFactory</code> will attempt to load a new instance 
  173        * of the given implementation class.
  174        * For example, running the following ant scriplet:
  175        * <code><pre>
  176        *  &lt;java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"&gt;
  177        *     ...
  178        *     &lt;sysproperty 
  179        *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
  180        *        value="org.apache.commons.logging.AltHashtable"/&gt;
  181        *  &lt;/java&gt;
  182        * </pre></code>
  183        * will mean that <code>LogFactory</code> will load an instance of
  184        * <code>org.apache.commons.logging.AltHashtable</code>.
  185        * </p>
  186        * <p>
  187        * A typical use case is to allow a custom
  188        * Hashtable implementation using weak references to be substituted.
  189        * This will allow classloaders to be garbage collected without
  190        * the need to release them (on 1.3+ JVMs only, of course ;)
  191        * </p>
  192        */
  193       public static final String HASHTABLE_IMPLEMENTATION_PROPERTY =
  194           "org.apache.commons.logging.LogFactory.HashtableImpl";
  195       /** Name used to load the weak hashtable implementation by names */
  196       private static final String WEAK_HASHTABLE_CLASSNAME = 
  197           "org.apache.commons.logging.impl.WeakHashtable";
  198   
  199       /**
  200        * A reference to the classloader that loaded this class. This is the
  201        * same as LogFactory.class.getClassLoader(). However computing this
  202        * value isn't quite as simple as that, as we potentially need to use
  203        * AccessControllers etc. It's more efficient to compute it once and
  204        * cache it here.
  205        */
  206       private static ClassLoader thisClassLoader;
  207       
  208       // ----------------------------------------------------------- Constructors
  209   
  210   
  211       /**
  212        * Protected constructor that is not available for public use.
  213        */
  214       protected LogFactory() {
  215       }
  216       
  217       // --------------------------------------------------------- Public Methods
  218   
  219   
  220       /**
  221        * Return the configuration attribute with the specified name (if any),
  222        * or <code>null</code> if there is no such attribute.
  223        *
  224        * @param name Name of the attribute to return
  225        */
  226       public abstract Object getAttribute(String name);
  227   
  228   
  229       /**
  230        * Return an array containing the names of all currently defined
  231        * configuration attributes.  If there are no such attributes, a zero
  232        * length array is returned.
  233        */
  234       public abstract String[] getAttributeNames();
  235   
  236   
  237       /**
  238        * Convenience method to derive a name from the specified class and
  239        * call <code>getInstance(String)</code> with it.
  240        *
  241        * @param clazz Class for which a suitable Log name will be derived
  242        *
  243        * @exception LogConfigurationException if a suitable <code>Log</code>
  244        *  instance cannot be returned
  245        */
  246       public abstract Log getInstance(Class clazz)
  247           throws LogConfigurationException;
  248   
  249   
  250       /**
  251        * <p>Construct (if necessary) and return a <code>Log</code> instance,
  252        * using the factory's current set of configuration attributes.</p>
  253        *
  254        * <p><strong>NOTE</strong> - Depending upon the implementation of
  255        * the <code>LogFactory</code> you are using, the <code>Log</code>
  256        * instance you are returned may or may not be local to the current
  257        * application, and may or may not be returned again on a subsequent
  258        * call with the same name argument.</p>
  259        *
  260        * @param name Logical name of the <code>Log</code> instance to be
  261        *  returned (the meaning of this name is only known to the underlying
  262        *  logging implementation that is being wrapped)
  263        *
  264        * @exception LogConfigurationException if a suitable <code>Log</code>
  265        *  instance cannot be returned
  266        */
  267       public abstract Log getInstance(String name)
  268           throws LogConfigurationException;
  269   
  270   
  271       /**
  272        * Release any internal references to previously created {@link Log}
  273        * instances returned by this factory.  This is useful in environments
  274        * like servlet containers, which implement application reloading by
  275        * throwing away a ClassLoader.  Dangling references to objects in that
  276        * class loader would prevent garbage collection.
  277        */
  278       public abstract void release();
  279   
  280   
  281       /**
  282        * Remove any configuration attribute associated with the specified name.
  283        * If there is no such attribute, no action is taken.
  284        *
  285        * @param name Name of the attribute to remove
  286        */
  287       public abstract void removeAttribute(String name);
  288   
  289   
  290       /**
  291        * Set the configuration attribute with the specified name.  Calling
  292        * this with a <code>null</code> value is equivalent to calling
  293        * <code>removeAttribute(name)</code>.
  294        *
  295        * @param name Name of the attribute to set
  296        * @param value Value of the attribute to set, or <code>null</code>
  297        *  to remove any setting for this attribute
  298        */
  299       public abstract void setAttribute(String name, Object value);
  300   
  301   
  302       // ------------------------------------------------------- Static Variables
  303   
  304   
  305       /**
  306        * The previously constructed <code>LogFactory</code> instances, keyed by
  307        * the <code>ClassLoader</code> with which it was created.
  308        */
  309       protected static Hashtable factories = null;
  310       
  311       /**
  312        * Prevously constructed <code>LogFactory</code> instance as in the
  313        * <code>factories</code> map, but for the case where
  314        * <code>getClassLoader</code> returns <code>null</code>.
  315        * This can happen when:
  316        * <ul>
  317        * <li>using JDK1.1 and the calling code is loaded via the system
  318        *  classloader (very common)</li>
  319        * <li>using JDK1.2+ and the calling code is loaded via the boot
  320        *  classloader (only likely for embedded systems work).</li>
  321        * </ul>
  322        * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
  323        * and hashtables don't allow null as a key.
  324        */
  325       protected static LogFactory nullClassLoaderFactory = null;
  326   
  327       /**
  328        * Create the hashtable which will be used to store a map of
  329        * (context-classloader -> logfactory-object). Version 1.2+ of Java
  330        * supports "weak references", allowing a custom Hashtable class
  331        * to be used which uses only weak references to its keys. Using weak
  332        * references can fix memory leaks on webapp unload in some cases (though
  333        * not all). Version 1.1 of Java does not support weak references, so we
  334        * must dynamically determine which we are using. And just for fun, this
  335        * code also supports the ability for a system property to specify an
  336        * arbitrary Hashtable implementation name.
  337        * <p>
  338        * Note that the correct way to ensure no memory leaks occur is to ensure
  339        * that LogFactory.release(contextClassLoader) is called whenever a 
  340        * webapp is undeployed.
  341        */
  342       private static final Hashtable createFactoryStore() {
  343           Hashtable result = null;
  344           String storeImplementationClass;
  345           try {
  346               storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);
  347           } catch(SecurityException ex) {
  348               // Permissions don't allow this to be accessed. Default to the "modern"
  349               // weak hashtable implementation if it is available.
  350               storeImplementationClass = null;
  351           }
  352   
  353           if (storeImplementationClass == null) {
  354               storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
  355           }
  356           try {
  357               Class implementationClass = Class.forName(storeImplementationClass);
  358               result = (Hashtable) implementationClass.newInstance();
  359               
  360           } catch (Throwable t) {
  361               // ignore
  362               if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
  363                   // if the user's trying to set up a custom implementation, give a clue
  364                   if (isDiagnosticsEnabled()) {
  365                       // use internal logging to issue the warning
  366                       logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
  367                   } else {
  368                       // we *really* want this output, even if diagnostics weren't
  369                       // explicitly enabled by the user.
  370                       System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
  371                   }
  372               }
  373           }
  374           if (result == null) {
  375               result = new Hashtable();
  376           }
  377           return result;
  378       }
  379   
  380   
  381       // --------------------------------------------------------- Static Methods
  382   
  383       /** Utility method to safely trim a string. */
  384       private static String trim(String src) {
  385       	if (src == null) {
  386       		return null;
  387       	}
  388       	return src.trim();
  389       }
  390   
  391       /**
  392        * <p>Construct (if necessary) and return a <code>LogFactory</code>
  393        * instance, using the following ordered lookup procedure to determine
  394        * the name of the implementation class to be loaded.</p>
  395        * <ul>
  396        * <li>The <code>org.apache.commons.logging.LogFactory</code> system
  397        *     property.</li>
  398        * <li>The JDK 1.3 Service Discovery mechanism</li>
  399        * <li>Use the properties file <code>commons-logging.properties</code>
  400        *     file, if found in the class path of this class.  The configuration
  401        *     file is in standard <code>java.util.Properties</code> format and
  402        *     contains the fully qualified name of the implementation class
  403        *     with the key being the system property defined above.</li>
  404        * <li>Fall back to a default implementation class
  405        *     (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
  406        * </ul>
  407        *
  408        * <p><em>NOTE</em> - If the properties file method of identifying the
  409        * <code>LogFactory</code> implementation class is utilized, all of the
  410        * properties defined in this file will be set as configuration attributes
  411        * on the corresponding <code>LogFactory</code> instance.</p>
  412        * 
  413        * <p><em>NOTE</em> - In a multithreaded environment it is possible 
  414        * that two different instances will be returned for the same 
  415        * classloader environment. 
  416        * </p>
  417        *
  418        * @exception LogConfigurationException if the implementation class is not
  419        *  available or cannot be instantiated.
  420        */
  421       public static LogFactory getFactory() throws LogConfigurationException {
  422           // Identify the class loader we will be using
  423           ClassLoader contextClassLoader = getContextClassLoaderInternal();
  424   
  425           if (contextClassLoader == null) {
  426               // This is an odd enough situation to report about. This
  427               // output will be a nuisance on JDK1.1, as the system
  428               // classloader is null in that environment.
  429               if (isDiagnosticsEnabled()) {
  430                   logDiagnostic("Context classloader is null.");
  431               }
  432           }
  433   
  434           // Return any previously registered factory for this class loader
  435           LogFactory factory = getCachedFactory(contextClassLoader);
  436           if (factory != null) {
  437               return factory;
  438           }
  439   
  440           if (isDiagnosticsEnabled()) {
  441               logDiagnostic(
  442                       "[LOOKUP] LogFactory implementation requested for the first time for context classloader "
  443                       + objectId(contextClassLoader));
  444               logHierarchy("[LOOKUP] ", contextClassLoader);
  445           }
  446   
  447           // Load properties file.
  448           //
  449           // If the properties file exists, then its contents are used as
  450           // "attributes" on the LogFactory implementation class. One particular
  451           // property may also control which LogFactory concrete subclass is
  452           // used, but only if other discovery mechanisms fail..
  453           //
  454           // As the properties file (if it exists) will be used one way or 
  455           // another in the end we may as well look for it first.
  456   
  457           Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
  458   
  459           // Determine whether we will be using the thread context class loader to
  460           // load logging classes or not by checking the loaded properties file (if any).
  461           ClassLoader baseClassLoader = contextClassLoader;
  462           if (props != null) {
  463               String useTCCLStr = props.getProperty(TCCL_KEY);
  464               if (useTCCLStr != null) {
  465                   // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
  466                   // is required for Java 1.2 compatability.
  467                   if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
  468                       // Don't use current context classloader when locating any
  469                       // LogFactory or Log classes, just use the class that loaded
  470                       // this abstract class. When this class is deployed in a shared
  471                       // classpath of a container, it means webapps cannot deploy their
  472                       // own logging implementations. It also means that it is up to the
  473                       // implementation whether to load library-specific config files
  474                       // from the TCCL or not.
  475                       baseClassLoader = thisClassLoader; 
  476                   }
  477               }
  478           }
  479   
  480           // Determine which concrete LogFactory subclass to use.
  481           // First, try a global system property
  482           if (isDiagnosticsEnabled()) {
  483               logDiagnostic(
  484                       "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY 
  485                       + "] to define the LogFactory subclass to use...");
  486           }
  487           
  488           try {
  489               String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
  490               if (factoryClass != null) {
  491                   if (isDiagnosticsEnabled()) {
  492                       logDiagnostic(
  493                               "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass
  494                               + "' as specified by system property " + FACTORY_PROPERTY);
  495                   }
  496                   
  497                   factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
  498               } else {
  499                   if (isDiagnosticsEnabled()) {
  500                       logDiagnostic(
  501                               "[LOOKUP] No system property [" + FACTORY_PROPERTY 
  502                               + "] defined.");
  503                   }
  504               }
  505           } catch (SecurityException e) {
  506               if (isDiagnosticsEnabled()) {
  507                   logDiagnostic(
  508                           "[LOOKUP] A security exception occurred while trying to create an"
  509                           + " instance of the custom factory class"
  510                           + ": [" + trim(e.getMessage())
  511                           + "]. Trying alternative implementations...");
  512               }
  513               ;  // ignore
  514           } catch(RuntimeException e) {
  515               // This is not consistent with the behaviour when a bad LogFactory class is
  516               // specified in a services file.
  517               //
  518               // One possible exception that can occur here is a ClassCastException when
  519               // the specified class wasn't castable to this LogFactory type.
  520               if (isDiagnosticsEnabled()) {
  521                   logDiagnostic(
  522                           "[LOOKUP] An exception occurred while trying to create an"
  523                           + " instance of the custom factory class"
  524                           + ": [" + trim(e.getMessage())
  525                           + "] as specified by a system property.");
  526               }
  527               throw e;
  528           }
  529   
  530   
  531           // Second, try to find a service by using the JDK1.3 class
  532           // discovery mechanism, which involves putting a file with the name
  533           // of an interface class in the META-INF/services directory, where the
  534           // contents of the file is a single line specifying a concrete class 
  535           // that implements the desired interface.
  536   
  537           if (factory == null) {
  538               if (isDiagnosticsEnabled()) {
  539                   logDiagnostic(
  540                           "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID
  541                           + "] to define the LogFactory subclass to use...");
  542               }
  543               try {
  544                   InputStream is = getResourceAsStream(contextClassLoader,
  545                                                        SERVICE_ID);
  546   
  547                   if( is != null ) {
  548                       // This code is needed by EBCDIC and other strange systems.
  549                       // It's a fix for bugs reported in xerces
  550                       BufferedReader rd;
  551                       try {
  552                           rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  553                       } catch (java.io.UnsupportedEncodingException e) {
  554                           rd = new BufferedReader(new InputStreamReader(is));
  555                       }
  556   
  557                       String factoryClassName = rd.readLine();
  558                       rd.close();
  559   
  560                       if (factoryClassName != null &&
  561                           ! "".equals(factoryClassName)) {
  562                           if (isDiagnosticsEnabled()) {
  563                               logDiagnostic(
  564                                       "[LOOKUP]  Creating an instance of LogFactory class " + factoryClassName
  565                                       + " as specified by file '" + SERVICE_ID 
  566                                       + "' which was present in the path of the context"
  567                                       + " classloader.");
  568                           }
  569                           factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
  570                       }
  571                   } else {
  572                       // is == null
  573                       if (isDiagnosticsEnabled()) {
  574                           logDiagnostic(
  575                               "[LOOKUP] No resource file with name '" + SERVICE_ID
  576                               + "' found.");
  577                       }
  578                   }
  579               } catch( Exception ex ) {
  580                   // note: if the specified LogFactory class wasn't compatible with LogFactory
  581                   // for some reason, a ClassCastException will be caught here, and attempts will
  582                   // continue to find a compatible class.
  583                   if (isDiagnosticsEnabled()) {
  584                       logDiagnostic(
  585                           "[LOOKUP] A security exception occurred while trying to create an"
  586                           + " instance of the custom factory class"
  587                           + ": [" + trim(ex.getMessage())
  588                           + "]. Trying alternative implementations...");
  589                   }
  590                   ; // ignore
  591               }
  592           }
  593   
  594   
  595           // Third try looking into the properties file read earlier (if found)
  596   
  597           if (factory == null) {
  598               if (props != null) {
  599                   if (isDiagnosticsEnabled()) {
  600                       logDiagnostic(
  601                           "[LOOKUP] Looking in properties file for entry with key '" 
  602                           + FACTORY_PROPERTY
  603                           + "' to define the LogFactory subclass to use...");
  604                   }
  605                   String factoryClass = props.getProperty(FACTORY_PROPERTY);
  606                   if (factoryClass != null) {
  607                       if (isDiagnosticsEnabled()) {
  608                           logDiagnostic(
  609                               "[LOOKUP] Properties file specifies LogFactory subclass '" 
  610                               + factoryClass + "'");
  611                       }
  612                       factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
  613                       
  614                       // TODO: think about whether we need to handle exceptions from newFactory
  615                   } else {
  616                       if (isDiagnosticsEnabled()) {
  617                           logDiagnostic(
  618                               "[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
  619                       }
  620                   }
  621               } else {
  622                   if (isDiagnosticsEnabled()) {
  623                       logDiagnostic(
  624                           "[LOOKUP] No properties file available to determine"
  625                           + " LogFactory subclass from..");
  626                   }
  627               }
  628           }
  629   
  630   
  631           // Fourth, try the fallback implementation class
  632   
  633           if (factory == null) {
  634               if (isDiagnosticsEnabled()) {
  635                   logDiagnostic(
  636                   "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT
  637                   + "' via the same classloader that loaded this LogFactory"
  638                   + " class (ie not looking in the context classloader).");
  639               }
  640               
  641               // Note: unlike the above code which can try to load custom LogFactory
  642               // implementations via the TCCL, we don't try to load the default LogFactory
  643               // implementation via the context classloader because:
  644               // * that can cause problems (see comments in newFactory method)
  645               // * no-one should be customising the code of the default class
  646               // Yes, we do give up the ability for the child to ship a newer
  647               // version of the LogFactoryImpl class and have it used dynamically
  648               // by an old LogFactory class in the parent, but that isn't 
  649               // necessarily a good idea anyway.
  650               factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
  651           }
  652   
  653           if (factory != null) {
  654               /**
  655                * Always cache using context class loader.
  656                */
  657               cacheFactory(contextClassLoader, factory);
  658   
  659               if( props!=null ) {
  660                   Enumeration names = props.propertyNames();
  661                   while (names.hasMoreElements()) {
  662                       String name = (String) names.nextElement();
  663                       String value = props.getProperty(name);
  664                       factory.setAttribute(name, value);
  665                   }
  666               }
  667           }
  668   
  669           return factory;
  670       }
  671   
  672   
  673       /**
  674        * Convenience method to return a named logger, without the application
  675        * having to care about factories.
  676        *
  677        * @param clazz Class from which a log name will be derived
  678        *
  679        * @exception LogConfigurationException if a suitable <code>Log</code>
  680        *  instance cannot be returned
  681        */
  682       public static Log getLog(Class clazz)
  683           throws LogConfigurationException {
  684   
  685           return (getFactory().getInstance(clazz));
  686   
  687       }
  688   
  689   
  690       /**
  691        * Convenience method to return a named logger, without the application
  692        * having to care about factories.
  693        *
  694        * @param name Logical name of the <code>Log</code> instance to be
  695        *  returned (the meaning of this name is only known to the underlying
  696        *  logging implementation that is being wrapped)
  697        *
  698        * @exception LogConfigurationException if a suitable <code>Log</code>
  699        *  instance cannot be returned
  700        */
  701       public static Log getLog(String name)
  702           throws LogConfigurationException {
  703   
  704           return (getFactory().getInstance(name));
  705   
  706       }
  707   
  708   
  709       /**
  710        * Release any internal references to previously created {@link LogFactory}
  711        * instances that have been associated with the specified class loader
  712        * (if any), after calling the instance method <code>release()</code> on
  713        * each of them.
  714        *
  715        * @param classLoader ClassLoader for which to release the LogFactory
  716        */
  717       public static void release(ClassLoader classLoader) {
  718   
  719           if (isDiagnosticsEnabled()) {
  720               logDiagnostic("Releasing factory for classloader " + objectId(classLoader));
  721           }
  722           synchronized (factories) {
  723               if (classLoader == null) {
  724                   if (nullClassLoaderFactory != null) {
  725                       nullClassLoaderFactory.release();
  726                       nullClassLoaderFactory = null;
  727                   }
  728               } else {
  729                   LogFactory factory = (LogFactory) factories.get(classLoader);
  730                   if (factory != null) {
  731                       factory.release();
  732                       factories.remove(classLoader);
  733                   }
  734               }
  735           }
  736   
  737       }
  738   
  739   
  740       /**
  741        * Release any internal references to previously created {@link LogFactory}
  742        * instances, after calling the instance method <code>release()</code> on
  743        * each of them.  This is useful in environments like servlet containers,
  744        * which implement application reloading by throwing away a ClassLoader.
  745        * Dangling references to objects in that class loader would prevent
  746        * garbage collection.
  747        */
  748       public static void releaseAll() {
  749   
  750           if (isDiagnosticsEnabled()) {
  751               logDiagnostic("Releasing factory for all classloaders.");
  752           }
  753           synchronized (factories) {
  754               Enumeration elements = factories.elements();
  755               while (elements.hasMoreElements()) {
  756                   LogFactory element = (LogFactory) elements.nextElement();
  757                   element.release();
  758               }
  759               factories.clear();
  760   
  761               if (nullClassLoaderFactory != null) {
  762                   nullClassLoaderFactory.release();
  763                   nullClassLoaderFactory = null;
  764               }
  765           }
  766   
  767       }
  768   
  769   
  770       // ------------------------------------------------------ Protected Methods
  771   
  772       /**
  773        * Safely get access to the classloader for the specified class.
  774        * <p>
  775        * Theoretically, calling getClassLoader can throw a security exception,
  776        * and so should be done under an AccessController in order to provide
  777        * maximum flexibility. However in practice people don't appear to use
  778        * security policies that forbid getClassLoader calls. So for the moment
  779        * all code is written to call this method rather than Class.getClassLoader,
  780        * so that we could put AccessController stuff in this method without any
  781        * disruption later if we need to.
  782        * <p>
  783        * Even when using an AccessController, however, this method can still
  784        * throw SecurityException. Commons-logging basically relies on the
  785        * ability to access classloaders, ie a policy that forbids all
  786        * classloader access will also prevent commons-logging from working: 
  787        * currently this method will throw an exception preventing the entire app
  788        * from starting up. Maybe it would be good to detect this situation and
  789        * just disable all commons-logging? Not high priority though - as stated
  790        * above, security policies that prevent classloader access aren't common.
  791        * <p>
  792        * Note that returning an object fetched via an AccessController would
  793        * technically be a security flaw anyway; untrusted code that has access
  794        * to a trusted JCL library could use it to fetch the classloader for
  795        * a class even when forbidden to do so directly.
  796        * 
  797        * @since 1.1
  798        */
  799       protected static ClassLoader getClassLoader(Class clazz) {
  800           try {
  801               return clazz.getClassLoader();
  802           } catch(SecurityException ex) {
  803               if (isDiagnosticsEnabled()) {
  804                   logDiagnostic(
  805                           "Unable to get classloader for class '" + clazz
  806                           + "' due to security restrictions - " + ex.getMessage());
  807               }
  808               throw ex;
  809           }
  810       }
  811   
  812       /**
  813        * Returns the current context classloader.
  814        * <p>
  815        * In versions prior to 1.1, this method did not use an AccessController.
  816        * In version 1.1, an AccessController wrapper was incorrectly added to
  817        * this method, causing a minor security flaw.
  818        * <p>
  819        * In version 1.1.1 this change was reverted; this method no longer uses
  820        * an AccessController. User code wishing to obtain the context classloader
  821        * must invoke this method via AccessController.doPrivileged if it needs
  822        * support for that.
  823        *  
  824        * @return the context classloader associated with the current thread,
  825        * or null if security doesn't allow it.
  826        * 
  827        * @throws LogConfigurationException if there was some weird error while
  828        * attempting to get the context classloader.
  829        * 
  830        * @throws SecurityException if the current java security policy doesn't
  831        * allow this class to access the context classloader.
  832        */
  833       protected static ClassLoader getContextClassLoader()
  834           throws LogConfigurationException {
  835   
  836           return directGetContextClassLoader();
  837       }
  838   
  839       /**
  840        * Calls LogFactory.directGetContextClassLoader under the control of an
  841        * AccessController class. This means that java code running under a
  842        * security manager that forbids access to ClassLoaders will still work
  843        * if this class is given appropriate privileges, even when the caller
  844        * doesn't have such privileges. Without using an AccessController, the
  845        * the entire call stack must have the privilege before the call is
  846        * allowed.
  847        *  
  848        * @return the context classloader associated with the current thread,
  849        * or null if security doesn't allow it.
  850        * 
  851        * @throws LogConfigurationException if there was some weird error while
  852        * attempting to get the context classloader.
  853        * 
  854        * @throws SecurityException if the current java security policy doesn't
  855        * allow this class to access the context classloader.
  856        */
  857       private static ClassLoader getContextClassLoaderInternal()
  858       throws LogConfigurationException {
  859           return (ClassLoader)AccessController.doPrivileged(
  860               new PrivilegedAction() {
  861                   public Object run() {
  862                       return directGetContextClassLoader();
  863                   }
  864               });
  865       }
  866   
  867       /**
  868        * Return the thread context class loader if available; otherwise return 
  869        * null. 
  870        * <p>
  871        * Most/all code should call getContextClassLoaderInternal rather than
  872        * calling this method directly.
  873        * <p>
  874        * The thread context class loader is available for JDK 1.2
  875        * or later, if certain security conditions are met.
  876        * <p>
  877        * Note that no internal logging is done within this method because
  878        * this method is called every time LogFactory.getLogger() is called,
  879        * and we don't want too much output generated here.
  880        * 
  881        * @exception LogConfigurationException if a suitable class loader
  882        * cannot be identified.
  883        * 
  884        * @exception SecurityException if the java security policy forbids
  885        * access to the context classloader from one of the classes in the
  886        * current call stack. 
  887        * @since 1.1
  888        */
  889       protected static ClassLoader directGetContextClassLoader()
  890           throws LogConfigurationException
  891       {
  892           ClassLoader classLoader = null;
  893   
  894           try {
  895               // Are we running on a JDK 1.2 or later system?
  896               Method method = Thread.class.getMethod("getContextClassLoader", 
  897                       (Class[]) null);
  898   
  899               // Get the thread context class loader (if there is one)
  900               try {
  901                   classLoader = (ClassLoader)method.invoke(Thread.currentThread(), 
  902                           (Object[]) null);
  903               } catch (IllegalAccessException e) {
  904                   throw new LogConfigurationException
  905                       ("Unexpected IllegalAccessException", e);
  906               } catch (InvocationTargetException e) {
  907                   /**
  908                    * InvocationTargetException is thrown by 'invoke' when
  909                    * the method being invoked (getContextClassLoader) throws
  910                    * an exception.
  911                    *
  912                    * getContextClassLoader() throws SecurityException when
  913                    * the context class loader isn't an ancestor of the
  914                    * calling class's class loader, or if security
  915                    * permissions are restricted.
  916                    *
  917                    * In the first case (not related), we want to ignore and
  918                    * keep going.  We cannot help but also ignore the second
  919                    * with the logic below, but other calls elsewhere (to
  920                    * obtain a class loader) will trigger this exception where
  921                    * we can make a distinction.
  922                    */
  923                   if (e.getTargetException() instanceof SecurityException) {
  924                       ;  // ignore
  925                   } else {
  926                       // Capture 'e.getTargetException()' exception for details
  927                       // alternate: log 'e.getTargetException()', and pass back 'e'.
  928                       throw new LogConfigurationException
  929                           ("Unexpected InvocationTargetException", e.getTargetException());
  930                   }
  931               }
  932           } catch (NoSuchMethodException e) {
  933               // Assume we are running on JDK 1.1
  934               classLoader = getClassLoader(LogFactory.class);
  935   
  936               // We deliberately don't log a message here to outputStream;
  937               // this message would be output for every call to LogFactory.getLog()
  938               // when running on JDK1.1
  939               //
  940               // if (outputStream != null) {
  941               //    outputStream.println(
  942               //        "Method Thread.getContextClassLoader does not exist;"
  943               //         + " assuming this is JDK 1.1, and that the context"
  944               //         + " classloader is the same as the class that loaded"
  945               //         + " the concrete LogFactory class.");
  946               // }
  947               
  948           }
  949   
  950           // Return the selected class loader
  951           return classLoader;
  952       }
  953   
  954       /**
  955        * Check cached factories (keyed by contextClassLoader)
  956        *
  957        * @param contextClassLoader is the context classloader associated
  958        * with the current thread. This allows separate LogFactory objects
  959        * per component within a container, provided each component has
  960        * a distinct context classloader set. This parameter may be null
  961        * in JDK1.1, and in embedded systems where jcl-using code is
  962        * placed in the bootclasspath.
  963        * 
  964        * @return the factory associated with the specified classloader if
  965        * one has previously been created, or null if this is the first time
  966        * we have seen this particular classloader.
  967        */
  968       private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
  969       {
  970           LogFactory factory = null;
  971   
  972           if (contextClassLoader == null) {
  973               // We have to handle this specially, as factories is a Hashtable
  974               // and those don't accept null as a key value.
  975               //
  976               // nb: nullClassLoaderFactory might be null. That's ok.
  977               factory = nullClassLoaderFactory;
  978           } else {
  979               factory = (LogFactory) factories.get(contextClassLoader);
  980           }
  981   
  982           return factory;
  983       }
  984   
  985       /**
  986        * Remember this factory, so later calls to LogFactory.getCachedFactory
  987        * can return the previously created object (together with all its
  988        * cached Log objects).
  989        *
  990        * @param classLoader should be the current context classloader. Note that
  991        * this can be null under some circumstances; this is ok.
  992        *
  993        * @param factory should be the factory to cache. This should never be null.
  994        */
  995       private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
  996       {
  997           // Ideally we would assert(factory != null) here. However reporting
  998           // errors from within a logging implementation is a little tricky!
  999   
 1000           if (factory != null) {
 1001               if (classLoader == null) {
 1002                   nullClassLoaderFactory = factory;
 1003               } else {
 1004                   factories.put(classLoader, factory);
 1005               }
 1006           }
 1007       }
 1008   
 1009       /**
 1010        * Return a new instance of the specified <code>LogFactory</code>
 1011        * implementation class, loaded by the specified class loader.
 1012        * If that fails, try the class loader used to load this
 1013        * (abstract) LogFactory.
 1014        * <p>
 1015        * <h2>ClassLoader conflicts</h2>
 1016        * Note that there can be problems if the specified ClassLoader is not the 
 1017        * same as the classloader that loaded this class, ie when loading a
 1018        * concrete LogFactory subclass via a context classloader.
 1019        * <p>
 1020        * The problem is the same one that can occur when loading a concrete Log
 1021        * subclass via a context classloader.
 1022        * <p>
 1023        * The problem occurs when code running in the context classloader calls
 1024        * class X which was loaded via a parent classloader, and class X then calls
 1025        * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
 1026        * class X was loaded via the parent, it binds to LogFactory loaded via
 1027        * the parent. When the code in this method finds some LogFactoryYYYY
 1028        * class in the child (context) classloader, and there also happens to be a
 1029        * LogFactory class defined in the child classloader, then LogFactoryYYYY
 1030        * will be bound to LogFactory@childloader. It cannot be cast to
 1031        * LogFactory@parentloader, ie this method cannot return the object as
 1032        * the desired type. Note that it doesn't matter if the LogFactory class
 1033        * in the child classloader is identical to the LogFactory class in the
 1034        * parent classloader, they are not compatible.
 1035        * <p>
 1036        * The solution taken here is to simply print out an error message when
 1037        * this occurs then throw an exception. The deployer of the application
 1038        * must ensure they remove all occurrences of the LogFactory class from
 1039        * the child classloader in order to resolve the issue. Note that they
 1040        * do not have to move the custom LogFactory subclass; that is ok as
 1041        * long as the only LogFactory class it can find to bind to is in the
 1042        * parent classloader.
 1043        * <p>
 1044        * @param factoryClass Fully qualified name of the <code>LogFactory</code>
 1045        *  implementation class
 1046        * @param classLoader ClassLoader from which to load this class
 1047        * @param contextClassLoader is the context that this new factory will
 1048        * manage logging for.
 1049        *
 1050        * @exception LogConfigurationException if a suitable instance
 1051        *  cannot be created
 1052        * @since 1.1
 1053        */
 1054       protected static LogFactory newFactory(final String factoryClass,
 1055                                              final ClassLoader classLoader,
 1056                                              final ClassLoader contextClassLoader)
 1057           throws LogConfigurationException
 1058       {
 1059           // Note that any unchecked exceptions thrown by the createFactory
 1060           // method will propagate out of this method; in particular a
 1061           // ClassCastException can be thrown.
 1062           Object result = AccessController.doPrivileged(
 1063               new PrivilegedAction() {
 1064                   public Object run() {
 1065                       return createFactory(factoryClass, classLoader);
 1066                   }
 1067               });
 1068   
 1069           if (result instanceof LogConfigurationException) {
 1070               LogConfigurationException ex = (LogConfigurationException) result;
 1071               if (isDiagnosticsEnabled()) {
 1072                   logDiagnostic(
 1073                           "An error occurred while loading the factory class:"
 1074                           + ex.getMessage());
 1075               }
 1076               throw ex;
 1077           }
 1078           if (isDiagnosticsEnabled()) {
 1079               logDiagnostic(
 1080                       "Created object " + objectId(result)
 1081                       + " to manage classloader " + objectId(contextClassLoader));
 1082           }
 1083           return (LogFactory)result;
 1084       }
 1085   
 1086       /**
 1087        * Method provided for backwards compatibility; see newFactory version that
 1088        * takes 3 parameters.
 1089        * <p>
 1090        * This method would only ever be called in some rather odd situation.
 1091        * Note that this method is static, so overriding in a subclass doesn't
 1092        * have any effect unless this method is called from a method in that
 1093        * subclass. However this method only makes sense to use from the
 1094        * getFactory method, and as that is almost always invoked via
 1095        * LogFactory.getFactory, any custom definition in a subclass would be
 1096        * pointless. Only a class with a custom getFactory method, then invoked
 1097        * directly via CustomFactoryImpl.getFactory or similar would ever call
 1098        * this. Anyway, it's here just in case, though the "managed class loader"
 1099        * value output to the diagnostics will not report the correct value.
 1100        */
 1101       protected static LogFactory newFactory(final String factoryClass,
 1102                                              final ClassLoader classLoader) {
 1103           return newFactory(factoryClass, classLoader, null);
 1104       }
 1105   
 1106       /**
 1107        * Implements the operations described in the javadoc for newFactory.
 1108        * 
 1109        * @param factoryClass
 1110        * 
 1111        * @param classLoader used to load the specified factory class. This is
 1112        * expected to be either the TCCL or the classloader which loaded this
 1113        * class. Note that the classloader which loaded this class might be
 1114        * "null" (ie the bootloader) for embedded systems.
 1115        * 
 1116        * @return either a LogFactory object or a LogConfigurationException object.
 1117        * @since 1.1
 1118        */
 1119       protected static Object createFactory(String factoryClass, ClassLoader classLoader) {
 1120   
 1121           // This will be used to diagnose bad configurations
 1122           // and allow a useful message to be sent to the user
 1123           Class logFactoryClass = null;
 1124           try {
 1125               if (classLoader != null) {
 1126                   try {
 1127                       // First the given class loader param (thread class loader)
 1128   
 1129                       // Warning: must typecast here & allow exception
 1130                       // to be generated/caught & recast properly.
 1131                       logFactoryClass = classLoader.loadClass(factoryClass);
 1132                       if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
 1133                           if (isDiagnosticsEnabled()) {
 1134                               logDiagnostic(
 1135                                       "Loaded class " + logFactoryClass.getName()
 1136                                       + " from classloader " + objectId(classLoader));
 1137                           }
 1138                       } else {
 1139                           //
 1140                           // This indicates a problem with the ClassLoader tree.
 1141                           // An incompatible ClassLoader was used to load the 
 1142                           // implementation. 
 1143                           // As the same classes
 1144                           // must be available in multiple class loaders,
 1145                           // it is very likely that multiple JCL jars are present.
 1146                           // The most likely fix for this
 1147                           // problem is to remove the extra JCL jars from the 
 1148                           // ClassLoader hierarchy. 
 1149                           //
 1150                           if (isDiagnosticsEnabled()) {
 1151                               logDiagnostic(
 1152                                       "Factory class " + logFactoryClass.getName()
 1153                                   + " loaded from classloader " + objectId(logFactoryClass.getClassLoader())
 1154                                   + " does not extend '" + LogFactory.class.getName()
 1155                                   + "' as loaded by this classloader.");
 1156                               logHierarchy("[BAD CL TREE] ", classLoader);
 1157                           }
 1158                       }
 1159                       
 1160                       return (LogFactory) logFactoryClass.newInstance();
 1161   
 1162                   } catch (ClassNotFoundException ex) {
 1163                       if (classLoader == thisClassLoader) {
 1164                           // Nothing more to try, onwards.
 1165                           if (isDiagnosticsEnabled()) {
 1166                               logDiagnostic(
 1167                                       "Unable to locate any class called '" + factoryClass
 1168                                       + "' via classloader " + objectId(classLoader));
 1169                           }
 1170                           throw ex;
 1171                       }
 1172                       // ignore exception, continue
 1173                   } catch (NoClassDefFoundError e) {
 1174                       if (classLoader == thisClassLoader) {
 1175                           // Nothing more to try, onwards.
 1176                           if (isDiagnosticsEnabled()) {
 1177                               logDiagnostic(
 1178                                       "Class '" + factoryClass + "' cannot be loaded"
 1179                                       + " via classloader " + objectId(classLoader)
 1180                                       + " - it depends on some other class that cannot"
 1181                                       + " be found.");
 1182                           }
 1183                           throw e;
 1184                       }
 1185                       // ignore exception, continue
 1186                   } catch(ClassCastException e) {
 1187                       if (classLoader == thisClassLoader) {
 1188                           // There's no point in falling through to the code below that
 1189                           // tries again with thisClassLoader, because we've just tried
 1190                           // loading with that loader (not the TCCL). Just throw an
 1191                           // appropriate exception here.
 1192   
 1193                           final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
 1194                           
 1195                           //
 1196                           // Construct a good message: users may not actual expect that a custom implementation 
 1197                           // has been specified. Several well known containers use this mechanism to adapt JCL 
 1198                           // to their native logging system. 
 1199                           // 
 1200                           String msg = 
 1201                               "The application has specified that a custom LogFactory implementation should be used but " +
 1202                               "Class '" + factoryClass + "' cannot be converted to '"
 1203                               + LogFactory.class.getName() + "'. ";
 1204                           if (implementsLogFactory) {
 1205                               msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " +
 1206                                   "Background can be found in http://commons.apache.org/logging/tech.html. " +
 1207                                   "If you have not explicitly specified a custom LogFactory then it is likely that " +
 1208                                   "the container has set one without your knowledge. " +
 1209                                   "In this case, consider using the commons-logging-adapters.jar file or " +
 1210                                   "specifying the standard LogFactory from the command line. ";
 1211                           } else {
 1212                               msg = msg + "Please check the custom implementation. ";
 1213                           }
 1214                           msg = msg + "Help can be found @http://commons.apache.org/logging/troubleshooting.html.";
 1215                           
 1216                           if (isDiagnosticsEnabled()) {
 1217                               logDiagnostic(msg);
 1218                           }
 1219                           
 1220                           ClassCastException ex = new ClassCastException(msg);
 1221                           throw ex;
 1222                       }
 1223                       
 1224                       // Ignore exception, continue. Presumably the classloader was the
 1225                       // TCCL; the code below will try to load the class via thisClassLoader.
 1226                       // This will handle the case where the original calling class is in
 1227                       // a shared classpath but the TCCL has a copy of LogFactory and the
 1228                       // specified LogFactory implementation; we will fall back to using the
 1229                       // LogFactory implementation from the same classloader as this class.
 1230                       //
 1231                       // Issue: this doesn't handle the reverse case, where this LogFactory
 1232                       // is in the webapp, and the specified LogFactory implementation is
 1233                       // in a shared classpath. In that case:
 1234                       // (a) the class really does implement LogFactory (bad log msg above)
 1235                       // (b) the fallback code will result in exactly the same problem.
 1236                   }
 1237               }
 1238   
 1239               /* At this point, either classLoader == null, OR
 1240                * classLoader was unable to load factoryClass.
 1241                *
 1242                * In either case, we call Class.forName, which is equivalent
 1243                * to LogFactory.class.getClassLoader().load(name), ie we ignore
 1244                * the classloader parameter the caller passed, and fall back
 1245                * to trying the classloader associated with this class. See the
 1246                * javadoc for the newFactory method for more info on the 
 1247                * consequences of this.
 1248                *
 1249                * Notes:
 1250                * * LogFactory.class.getClassLoader() may return 'null'
 1251                *   if LogFactory is loaded by the bootstrap classloader.
 1252                */
 1253               // Warning: must typecast here & allow exception
 1254               // to be generated/caught & recast properly.
 1255               if (isDiagnosticsEnabled()) {
 1256                   logDiagnostic(
 1257                       "Unable to load factory class via classloader " 
 1258                       + objectId(classLoader)
 1259                       + " - trying the classloader associated with this LogFactory.");
 1260               }
 1261               logFactoryClass = Class.forName(factoryClass);
 1262               return (LogFactory) logFactoryClass.newInstance();
 1263           } catch (Exception e) {
 1264               // Check to see if we've got a bad configuration
 1265               if (isDiagnosticsEnabled()) {
 1266                   logDiagnostic("Unable to create LogFactory instance.");
 1267               }
 1268               if (logFactoryClass != null
 1269                   && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
 1270                   
 1271                   return new LogConfigurationException(
 1272                       "The chosen LogFactory implementation does not extend LogFactory."
 1273                       + " Please check your configuration.",
 1274                       e);
 1275               }
 1276               return new LogConfigurationException(e);
 1277           }
 1278       }
 1279   
 1280       /**
 1281        * Determines whether the given class actually implements <code>LogFactory</code>.
 1282        * Diagnostic information is also logged.
 1283        * <p>
 1284        * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
 1285        * of incompatibility. The test used is whether the class is assignable from
 1286        * the <code>LogFactory</code> class loaded by the class's classloader.
 1287        * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
 1288        * @return true if the <code>logFactoryClass</code> does extend
 1289        * <code>LogFactory</code> when that class is loaded via the same
 1290        * classloader that loaded the <code>logFactoryClass</code>.
 1291        */
 1292       private static boolean implementsLogFactory(Class logFactoryClass) {
 1293           boolean implementsLogFactory = false;
 1294           if (logFactoryClass != null) {
 1295               try {
 1296                   ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
 1297                   if (logFactoryClassLoader == null) {
 1298                       logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
 1299                   } else {
 1300                       logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
 1301                       Class factoryFromCustomLoader
 1302                           = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
 1303                       implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
 1304                       if (implementsLogFactory) {
 1305                           logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
 1306                                   + " implements LogFactory but was loaded by an incompatible classloader.");
 1307                       } else {
 1308                           logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
 1309                                   + " does not implement LogFactory.");
 1310                       }
 1311                   }
 1312               } catch (SecurityException e) {
 1313                   //
 1314                   // The application is running within a hostile security environment.
 1315                   // This will make it very hard to diagnose issues with JCL.
 1316                   // Consider running less securely whilst debugging this issue.
 1317                   //
 1318                   logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " +
 1319                           "the compatibility was caused by a classloader conflict: "
 1320                           + e.getMessage());
 1321               } catch (LinkageError e) {
 1322                   //
 1323                   // This should be an unusual circumstance.
 1324                   // LinkageError's usually indicate that a dependent class has incompatibly changed.
 1325                   // Another possibility may be an exception thrown by an initializer.
 1326                   // Time for a clean rebuild?
 1327                   //
 1328                   logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " +
 1329                           "the compatibility was caused by a classloader conflict: "
 1330                           + e.getMessage());
 1331               } catch (ClassNotFoundException e) {
 1332                   //
 1333                   // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation.
 1334                   // The custom implementation is not viable until this is corrected.
 1335                   // Ensure that the JCL jar and the custom class are available from the same classloader.
 1336                   // Running with diagnostics on should give information about the classloaders used
 1337                   // to load the custom factory.
 1338                   //
 1339                   logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the " +
 1340                           "custom LogFactory implementation. Is the custom factory in the right classloader?");
 1341               }
 1342           }
 1343           return implementsLogFactory;
 1344       }
 1345   
 1346       /**
 1347        * Applets may run in an environment where accessing resources of a loader is
 1348        * a secure operation, but where the commons-logging library has explicitly
 1349        * been granted permission for that operation. In this case, we need to 
 1350        * run the operation using an AccessController.
 1351        */
 1352       private static InputStream getResourceAsStream(final ClassLoader loader,
 1353                                                      final String name)
 1354       {
 1355           return (InputStream)AccessController.doPrivileged(
 1356               new PrivilegedAction() {
 1357                   public Object run() {
 1358                       if (loader != null) {
 1359                           return loader.getResourceAsStream(name);
 1360                       } else {
 1361                           return ClassLoader.getSystemResourceAsStream(name);
 1362                       }
 1363                   }
 1364               });
 1365       }
 1366   
 1367       /**
 1368        * Given a filename, return an enumeration of URLs pointing to
 1369        * all the occurrences of that filename in the classpath.
 1370        * <p>
 1371        * This is just like ClassLoader.getResources except that the
 1372        * operation is done under an AccessController so that this method will
 1373        * succeed when this jarfile is privileged but the caller is not.
 1374        * This method must therefore remain private to avoid security issues.
 1375        * <p>
 1376        * If no instances are found, an Enumeration is returned whose
 1377        * hasMoreElements method returns false (ie an "empty" enumeration).
 1378        * If resources could not be listed for some reason, null is returned.
 1379        */
 1380       private static Enumeration getResources(final ClassLoader loader,
 1381               final String name)
 1382       {
 1383           PrivilegedAction action = 
 1384               new PrivilegedAction() {
 1385                   public Object run() {
 1386                       try {
 1387                           if (loader != null) {
 1388                               return loader.getResources(name);
 1389                           } else {
 1390                               return ClassLoader.getSystemResources(name);
 1391                           }
 1392                       } catch(IOException e) {
 1393                           if (isDiagnosticsEnabled()) {
 1394                               logDiagnostic(
 1395                                   "Exception while trying to find configuration file "
 1396                                   + name + ":" + e.getMessage());
 1397                           }
 1398                           return null;
 1399                       } catch(NoSuchMethodError e) {
 1400                           // we must be running on a 1.1 JVM which doesn't support
 1401                           // ClassLoader.getSystemResources; just return null in
 1402                           // this case.
 1403                           return null;
 1404                       }
 1405                   }
 1406               };
 1407           Object result = AccessController.doPrivileged(action);
 1408           return (Enumeration) result;
 1409       }
 1410   
 1411       /**
 1412        * Given a URL that refers to a .properties file, load that file.
 1413        * This is done under an AccessController so that this method will
 1414        * succeed when this jarfile is privileged but the caller is not.
 1415        * This method must therefore remain private to avoid security issues.
 1416        * <p>
 1417        * Null is returned if the URL cannot be opened.
 1418        */
 1419       private static Properties getProperties(final URL url) {
 1420           PrivilegedAction action = 
 1421               new PrivilegedAction() {
 1422                   public Object run() {
 1423                       try {
 1424                           InputStream stream = url.openStream();
 1425                           if (stream != null) {
 1426                               Properties props = new Properties();
 1427                               props.load(stream);
 1428                               stream.close();
 1429                               return props;
 1430                           }
 1431                       } catch(IOException e) {
 1432                           if (isDiagnosticsEnabled()) {
 1433                               logDiagnostic("Unable to read URL " + url);
 1434                           }
 1435                       }
 1436   
 1437                       return null;
 1438                   }
 1439               };
 1440           return (Properties) AccessController.doPrivileged(action);
 1441       }
 1442   
 1443       /**
 1444        * Locate a user-provided configuration file.
 1445        * <p>
 1446        * The classpath of the specified classLoader (usually the context classloader)
 1447        * is searched for properties files of the specified name. If none is found,
 1448        * null is returned. If more than one is found, then the file with the greatest
 1449        * value for its PRIORITY property is returned. If multiple files have the
 1450        * same PRIORITY value then the first in the classpath is returned.
 1451        * <p> 
 1452        * This differs from the 1.0.x releases; those always use the first one found.
 1453        * However as the priority is a new field, this change is backwards compatible.
 1454        * <p>
 1455        * The purpose of the priority field is to allow a webserver administrator to
 1456        * override logging settings in all webapps by placing a commons-logging.properties
 1457        * file in a shared classpath location with a priority > 0; this overrides any
 1458        * commons-logging.properties files without priorities which are in the
 1459        * webapps. Webapps can also use explicit priorities to override a configuration
 1460        * file in the shared classpath if needed. 
 1461        */
 1462       private static final Properties getConfigurationFile(
 1463               ClassLoader classLoader, String fileName) {
 1464   
 1465           Properties props = null;
 1466           double priority = 0.0;
 1467           URL propsUrl = null;
 1468           try {
 1469               Enumeration urls = getResources(classLoader, fileName);
 1470   
 1471               if (urls == null) {
 1472                   return null;
 1473               }
 1474               
 1475               while (urls.hasMoreElements()) {
 1476                   URL url = (URL) urls.nextElement();
 1477                   
 1478                   Properties newProps = getProperties(url);
 1479                   if (newProps != null) {
 1480                       if (props == null) {
 1481                           propsUrl = url; 
 1482                           props = newProps;
 1483                           String priorityStr = props.getProperty(PRIORITY_KEY);
 1484                           priority = 0.0;
 1485                           if (priorityStr != null) {
 1486                               priority = Double.parseDouble(priorityStr);
 1487                           }
 1488   
 1489                           if (isDiagnosticsEnabled()) {
 1490                               logDiagnostic(
 1491                                   "[LOOKUP] Properties file found at '" + url + "'"
 1492                                   + " with priority " + priority); 
 1493                           }
 1494                       } else {
 1495                           String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
 1496                           double newPriority = 0.0;
 1497                           if (newPriorityStr != null) {
 1498                               newPriority = Double.parseDouble(newPriorityStr);
 1499                           }
 1500   
 1501                           if (newPriority > priority) {
 1502                               if (isDiagnosticsEnabled()) {
 1503                                   logDiagnostic(
 1504                                       "[LOOKUP] Properties file at '" + url + "'"
 1505                                       + " with priority " + newPriority 
 1506                                       + " overrides file at '" + propsUrl + "'"
 1507                                       + " with priority " + priority);
 1508                               }
 1509   
 1510                               propsUrl = url; 
 1511                               props = newProps;
 1512                               priority = newPriority;
 1513                           } else {
 1514                               if (isDiagnosticsEnabled()) {
 1515                                   logDiagnostic(
 1516                                       "[LOOKUP] Properties file at '" + url + "'"
 1517                                       + " with priority " + newPriority 
 1518                                       + " does not override file at '" + propsUrl + "'"
 1519                                       + " with priority " + priority);
 1520                               }
 1521                           }
 1522                       }
 1523   
 1524                   }
 1525               }
 1526           } catch (SecurityException e) {
 1527               if (isDiagnosticsEnabled()) {
 1528                   logDiagnostic("SecurityException thrown while trying to find/read config files.");
 1529               }
 1530           }
 1531   
 1532           if (isDiagnosticsEnabled()) {
 1533               if (props == null) {
 1534                   logDiagnostic(
 1535                       "[LOOKUP] No properties file of name '" + fileName
 1536                       + "' found.");
 1537               } else {
 1538                   logDiagnostic(
 1539                       "[LOOKUP] Properties file of name '" + fileName
 1540                       + "' found at '" + propsUrl + '"');
 1541               }
 1542           }
 1543   
 1544           return props;
 1545       }
 1546   
 1547       /**
 1548        * Read the specified system property, using an AccessController so that 
 1549        * the property can be read if JCL has been granted the appropriate
 1550        * security rights even if the calling code has not.
 1551        * <p>
 1552        * Take care not to expose the value returned by this method to the
 1553        * calling application in any way; otherwise the calling app can use that
 1554        * info to access data that should not be available to it.
 1555        */
 1556       private static String getSystemProperty(final String key, final String def)
 1557       throws SecurityException {
 1558           return (String) AccessController.doPrivileged(
 1559                   new PrivilegedAction() {
 1560                       public Object run() {
 1561                           return System.getProperty(key, def);
 1562                       }
 1563                   });
 1564       }
 1565   
 1566       /**
 1567        * Determines whether the user wants internal diagnostic output. If so,
 1568        * returns an appropriate writer object. Users can enable diagnostic
 1569        * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
 1570        * a filename, or the special values STDOUT or STDERR. 
 1571        */
 1572       private static void initDiagnostics() {
 1573           String dest;
 1574           try {
 1575               dest = getSystemProperty(DIAGNOSTICS_DEST_PROPERTY, null);
 1576               if (dest == null) {
 1577                   return;
 1578               }
 1579           } catch(SecurityException ex) {
 1580               // We must be running in some very secure environment.
 1581               // We just have to assume output is not wanted..
 1582               return;
 1583           }
 1584   
 1585           if (dest.equals("STDOUT")) {
 1586               diagnosticsStream = System.out;
 1587           } else if (dest.equals("STDERR")) {
 1588               diagnosticsStream = System.err;
 1589           } else {
 1590               try {
 1591                   // open the file in append mode
 1592                   FileOutputStream fos = new FileOutputStream(dest, true);
 1593                   diagnosticsStream = new PrintStream(fos);
 1594               } catch(IOException ex) {
 1595                   // We should report this to the user - but how?
 1596                   return;
 1597               }
 1598           }
 1599   
 1600           // In order to avoid confusion where multiple instances of JCL are
 1601           // being used via different classloaders within the same app, we
 1602           // ensure each logged message has a prefix of form
 1603           // [LogFactory from classloader OID]
 1604           //
 1605           // Note that this prefix should be kept consistent with that 
 1606           // in LogFactoryImpl. However here we don't need to output info
 1607           // about the actual *instance* of LogFactory, as all methods that
 1608           // output diagnostics from this class are static.
 1609           String classLoaderName;
 1610           try {
 1611               ClassLoader classLoader = thisClassLoader;
 1612               if (thisClassLoader == null) {
 1613                   classLoaderName = "BOOTLOADER";
 1614               } else {
 1615                   classLoaderName = objectId(classLoader);
 1616               }
 1617           } catch(SecurityException e) {
 1618               classLoaderName = "UNKNOWN";
 1619           }
 1620           diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
 1621       }
 1622   
 1623       /**
 1624        * Indicates true if the user has enabled internal logging.
 1625        * <p>
 1626        * By the way, sorry for the incorrect grammar, but calling this method
 1627        * areDiagnosticsEnabled just isn't java beans style.
 1628        * 
 1629        * @return true if calls to logDiagnostic will have any effect.
 1630        * @since 1.1
 1631        */
 1632       protected static boolean isDiagnosticsEnabled() {
 1633           return diagnosticsStream != null;
 1634       }
 1635   
 1636       /**
 1637        * Write the specified message to the internal logging destination.
 1638        * <p>
 1639        * Note that this method is private; concrete subclasses of this class
 1640        * should not call it because the diagnosticPrefix string this
 1641        * method puts in front of all its messages is LogFactory@....,
 1642        * while subclasses should put SomeSubClass@...
 1643        * <p>
 1644        * Subclasses should instead compute their own prefix, then call
 1645        * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
 1646        * fine for subclasses.
 1647        * <p>
 1648        * Note that it is safe to call this method before initDiagnostics
 1649        * is called; any output will just be ignored (as isDiagnosticsEnabled
 1650        * will return false).
 1651        * 
 1652        * @param msg is the diagnostic message to be output.
 1653        */
 1654       private static final void logDiagnostic(String msg) {
 1655           if (diagnosticsStream != null) {
 1656               diagnosticsStream.print(diagnosticPrefix);
 1657               diagnosticsStream.println(msg);
 1658               diagnosticsStream.flush();
 1659           }
 1660       }
 1661   
 1662       /**
 1663        * Write the specified message to the internal logging destination.
 1664        * 
 1665        * @param msg is the diagnostic message to be output.
 1666        * @since 1.1
 1667        */
 1668       protected static final void logRawDiagnostic(String msg) {
 1669           if (diagnosticsStream != null) {
 1670               diagnosticsStream.println(msg);
 1671               diagnosticsStream.flush();
 1672           }
 1673       }
 1674   
 1675       /**
 1676        * Generate useful diagnostics regarding the classloader tree for
 1677        * the specified class.
 1678        * <p>
 1679        * As an example, if the specified class was loaded via a webapp's
 1680        * classloader, then you may get the following output:
 1681        * <pre>
 1682        * Class com.acme.Foo was loaded via classloader 11111
 1683        * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT 
 1684        * </pre>
 1685        * <p>
 1686        * This method returns immediately if isDiagnosticsEnabled()
 1687        * returns false.
 1688        * 
 1689        * @param clazz is the class whose classloader + tree are to be
 1690        * output.
 1691        */
 1692       private static void logClassLoaderEnvironment(Class clazz) {
 1693           if (!isDiagnosticsEnabled()) {
 1694               return;
 1695           }
 1696           
 1697           try {
 1698               // Deliberately use System.getProperty here instead of getSystemProperty; if
 1699               // the overall security policy for the calling application forbids access to
 1700               // these variables then we do not want to output them to the diagnostic stream. 
 1701               logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
 1702               logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
 1703           } catch(SecurityException ex) {
 1704               logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
 1705           }
 1706           
 1707           String className = clazz.getName();
 1708           ClassLoader classLoader;
 1709           
 1710           try {
 1711               classLoader = getClassLoader(clazz);
 1712           } catch(SecurityException ex) {
 1713               // not much useful diagnostics we can print here!
 1714               logDiagnostic(
 1715                   "[ENV] Security forbids determining the classloader for " + className);
 1716               return;
 1717           }
 1718   
 1719           logDiagnostic(
 1720               "[ENV] Class " + className + " was loaded via classloader "
 1721               + objectId(classLoader));
 1722           logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader);
 1723       }
 1724   
 1725       /**
 1726        * Logs diagnostic messages about the given classloader
 1727        * and it's hierarchy. The prefix is prepended to the message
 1728        * and is intended to make it easier to understand the logs.
 1729        * @param prefix 
 1730        * @param classLoader
 1731        */
 1732       private static void logHierarchy(String prefix, ClassLoader classLoader) {
 1733           if (!isDiagnosticsEnabled()) {
 1734               return;
 1735           }
 1736           ClassLoader systemClassLoader;
 1737           if (classLoader != null) {
 1738               final String classLoaderString = classLoader.toString();
 1739               logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
 1740           }
 1741           
 1742           try {
 1743               systemClassLoader = ClassLoader.getSystemClassLoader();
 1744           } catch(SecurityException ex) {
 1745               logDiagnostic(
 1746                       prefix + "Security forbids determining the system classloader.");
 1747               return;
 1748           }        
 1749           if (classLoader != null) {
 1750               StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
 1751               for(;;) {
 1752                   buf.append(objectId(classLoader));
 1753                   if (classLoader == systemClassLoader) {
 1754                       buf.append(" (SYSTEM) ");
 1755                   }
 1756   
 1757                   try {
 1758                       classLoader = classLoader.getParent();
 1759                   } catch(SecurityException ex) {
 1760                       buf.append(" --> SECRET");
 1761                       break;
 1762                   }
 1763   
 1764                   buf.append(" --> ");
 1765                   if (classLoader == null) {
 1766                       buf.append("BOOT");
 1767                       break;
 1768                   }
 1769               }
 1770               logDiagnostic(buf.toString());
 1771           }
 1772       }
 1773   
 1774       /**
 1775        * Returns a string that uniquely identifies the specified object, including
 1776        * its class.
 1777        * <p>
 1778        * The returned string is of form "classname@hashcode", ie is the same as
 1779        * the return value of the Object.toString() method, but works even when
 1780        * the specified object's class has overidden the toString method.
 1781        * 
 1782        * @param o may be null.
 1783        * @return a string of form classname@hashcode, or "null" if param o is null.
 1784        * @since 1.1
 1785        */
 1786       public static String objectId(Object o) {
 1787           if (o == null) {
 1788               return "null";
 1789           } else {
 1790               return o.getClass().getName() + "@" + System.identityHashCode(o);
 1791           }
 1792       }
 1793   
 1794       // ----------------------------------------------------------------------
 1795       // Static initialiser block to perform initialisation at class load time.
 1796       //
 1797       // We can't do this in the class constructor, as there are many 
 1798       // static methods on this class that can be called before any
 1799       // LogFactory instances are created, and they depend upon this
 1800       // stuff having been set up.
 1801       //
 1802       // Note that this block must come after any variable declarations used
 1803       // by any methods called from this block, as we want any static initialiser
 1804       // associated with the variable to run first. If static initialisers for
 1805       // variables run after this code, then (a) their value might be needed
 1806       // by methods called from here, and (b) they might *override* any value
 1807       // computed here!
 1808       //
 1809       // So the wisest thing to do is just to place this code at the very end
 1810       // of the class file.
 1811       // ----------------------------------------------------------------------
 1812   
 1813       static {
 1814           // note: it's safe to call methods before initDiagnostics (though
 1815           // diagnostic output gets discarded).
 1816           thisClassLoader = getClassLoader(LogFactory.class);
 1817           initDiagnostics();
 1818           logClassLoaderEnvironment(LogFactory.class);
 1819           factories = createFactoryStore();
 1820           if (isDiagnosticsEnabled()) {
 1821               logDiagnostic("BOOTSTRAP COMPLETED");
 1822           }
 1823       }
 1824   }

Home » commons-logging-1.1.1-src » org.apache.commons » logging » [javadoc | source]