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