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