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