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