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