001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.logging.impl; 019 020 021 import java.lang.reflect.Constructor; 022 import java.lang.reflect.InvocationTargetException; 023 import java.lang.reflect.Method; 024 import java.net.URL; 025 import java.security.AccessController; 026 import java.security.PrivilegedAction; 027 import java.util.Enumeration; 028 import java.util.Hashtable; 029 import java.util.Vector; 030 031 import org.apache.commons.logging.Log; 032 import org.apache.commons.logging.LogConfigurationException; 033 import org.apache.commons.logging.LogFactory; 034 035 036 /** 037 * <p>Concrete subclass of {@link LogFactory} that implements the 038 * following algorithm to dynamically select a logging implementation 039 * class to instantiate a wrapper for.</p> 040 * <ul> 041 * <li>Use a factory configuration attribute named 042 * <code>org.apache.commons.logging.Log</code> to identify the 043 * requested implementation class.</li> 044 * <li>Use the <code>org.apache.commons.logging.Log</code> system property 045 * to identify the requested implementation class.</li> 046 * <li>If <em>Log4J</em> is available, return an instance of 047 * <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li> 048 * <li>If <em>JDK 1.4 or later</em> is available, return an instance of 049 * <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li> 050 * <li>Otherwise, return an instance of 051 * <code>org.apache.commons.logging.impl.SimpleLog</code>.</li> 052 * </ul> 053 * 054 * <p>If the selected {@link Log} implementation class has a 055 * <code>setLogFactory()</code> method that accepts a {@link LogFactory} 056 * parameter, this method will be called on each newly created instance 057 * to identify the associated factory. This makes factory configuration 058 * attributes available to the Log instance, if it so desires.</p> 059 * 060 * <p>This factory will remember previously created <code>Log</code> instances 061 * for the same name, and will return them on repeated requests to the 062 * <code>getInstance()</code> method.</p> 063 * 064 * @author Rod Waldhoff 065 * @author Craig R. McClanahan 066 * @author Richard A. Sitze 067 * @author Brian Stansberry 068 * @version $Revision: 581090 $ $Date: 2007-10-02 00:01:06 +0200 (ti, 02 okt 2007) $ 069 */ 070 071 public class LogFactoryImpl extends LogFactory { 072 073 074 /** Log4JLogger class name */ 075 private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger"; 076 /** Jdk14Logger class name */ 077 private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger"; 078 /** Jdk13LumberjackLogger class name */ 079 private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger"; 080 /** SimpleLog class name */ 081 private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog"; 082 083 private static final String PKG_IMPL="org.apache.commons.logging.impl."; 084 private static final int PKG_LEN = PKG_IMPL.length(); 085 086 // ----------------------------------------------------------- Constructors 087 088 089 090 /** 091 * Public no-arguments constructor required by the lookup mechanism. 092 */ 093 public LogFactoryImpl() { 094 super(); 095 initDiagnostics(); // method on this object 096 if (isDiagnosticsEnabled()) { 097 logDiagnostic("Instance created."); 098 } 099 } 100 101 102 // ----------------------------------------------------- Manifest Constants 103 104 105 /** 106 * The name (<code>org.apache.commons.logging.Log</code>) of the system 107 * property identifying our {@link Log} implementation class. 108 */ 109 public static final String LOG_PROPERTY = 110 "org.apache.commons.logging.Log"; 111 112 113 /** 114 * The deprecated system property used for backwards compatibility with 115 * old versions of JCL. 116 */ 117 protected static final String LOG_PROPERTY_OLD = 118 "org.apache.commons.logging.log"; 119 120 /** 121 * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>) 122 * of the system property which can be set true/false to 123 * determine system behaviour when a bad context-classloader is encountered. 124 * When set to false, a LogConfigurationException is thrown if 125 * LogFactoryImpl is loaded via a child classloader of the TCCL (this 126 * should never happen in sane systems). 127 * 128 * Default behaviour: true (tolerates bad context classloaders) 129 * 130 * See also method setAttribute. 131 */ 132 public static final String ALLOW_FLAWED_CONTEXT_PROPERTY = 133 "org.apache.commons.logging.Log.allowFlawedContext"; 134 135 /** 136 * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>) 137 * of the system property which can be set true/false to 138 * determine system behaviour when a bad logging adapter class is 139 * encountered during logging discovery. When set to false, an 140 * exception will be thrown and the app will fail to start. When set 141 * to true, discovery will continue (though the user might end up 142 * with a different logging implementation than they expected). 143 * 144 * Default behaviour: true (tolerates bad logging adapters) 145 * 146 * See also method setAttribute. 147 */ 148 public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY = 149 "org.apache.commons.logging.Log.allowFlawedDiscovery"; 150 151 /** 152 * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>) 153 * of the system property which can be set true/false to 154 * determine system behaviour when a logging adapter class is 155 * encountered which has bound to the wrong Log class implementation. 156 * When set to false, an exception will be thrown and the app will fail 157 * to start. When set to true, discovery will continue (though the user 158 * might end up with a different logging implementation than they expected). 159 * 160 * Default behaviour: true (tolerates bad Log class hierarchy) 161 * 162 * See also method setAttribute. 163 */ 164 public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY = 165 "org.apache.commons.logging.Log.allowFlawedHierarchy"; 166 167 168 /** 169 * The names of classes that will be tried (in order) as logging 170 * adapters. Each class is expected to implement the Log interface, 171 * and to throw NoClassDefFound or ExceptionInInitializerError when 172 * loaded if the underlying logging library is not available. Any 173 * other error indicates that the underlying logging library is available 174 * but broken/unusable for some reason. 175 */ 176 private static final String[] classesToDiscover = { 177 LOGGING_IMPL_LOG4J_LOGGER, 178 "org.apache.commons.logging.impl.Jdk14Logger", 179 "org.apache.commons.logging.impl.Jdk13LumberjackLogger", 180 "org.apache.commons.logging.impl.SimpleLog" 181 }; 182 183 184 // ----------------------------------------------------- Instance Variables 185 186 /** 187 * Determines whether logging classes should be loaded using the thread-context 188 * classloader, or via the classloader that loaded this LogFactoryImpl class. 189 */ 190 private boolean useTCCL = true; 191 192 /** 193 * The string prefixed to every message output by the logDiagnostic method. 194 */ 195 private String diagnosticPrefix; 196 197 198 /** 199 * Configuration attributes. 200 */ 201 protected Hashtable attributes = new Hashtable(); 202 203 204 /** 205 * The {@link org.apache.commons.logging.Log} instances that have 206 * already been created, keyed by logger name. 207 */ 208 protected Hashtable instances = new Hashtable(); 209 210 211 /** 212 * Name of the class implementing the Log interface. 213 */ 214 private String logClassName; 215 216 217 /** 218 * The one-argument constructor of the 219 * {@link org.apache.commons.logging.Log} 220 * implementation class that will be used to create new instances. 221 * This value is initialized by <code>getLogConstructor()</code>, 222 * and then returned repeatedly. 223 */ 224 protected Constructor logConstructor = null; 225 226 227 /** 228 * The signature of the Constructor to be used. 229 */ 230 protected Class logConstructorSignature[] = 231 { java.lang.String.class }; 232 233 234 /** 235 * The one-argument <code>setLogFactory</code> method of the selected 236 * {@link org.apache.commons.logging.Log} method, if it exists. 237 */ 238 protected Method logMethod = null; 239 240 241 /** 242 * The signature of the <code>setLogFactory</code> method to be used. 243 */ 244 protected Class logMethodSignature[] = 245 { LogFactory.class }; 246 247 /** 248 * See getBaseClassLoader and initConfiguration. 249 */ 250 private boolean allowFlawedContext; 251 252 /** 253 * See handleFlawedDiscovery and initConfiguration. 254 */ 255 private boolean allowFlawedDiscovery; 256 257 /** 258 * See handleFlawedHierarchy and initConfiguration. 259 */ 260 private boolean allowFlawedHierarchy; 261 262 // --------------------------------------------------------- Public Methods 263 264 265 /** 266 * Return the configuration attribute with the specified name (if any), 267 * or <code>null</code> if there is no such attribute. 268 * 269 * @param name Name of the attribute to return 270 */ 271 public Object getAttribute(String name) { 272 273 return (attributes.get(name)); 274 275 } 276 277 278 /** 279 * Return an array containing the names of all currently defined 280 * configuration attributes. If there are no such attributes, a zero 281 * length array is returned. 282 */ 283 public String[] getAttributeNames() { 284 285 Vector names = new Vector(); 286 Enumeration keys = attributes.keys(); 287 while (keys.hasMoreElements()) { 288 names.addElement((String) keys.nextElement()); 289 } 290 String results[] = new String[names.size()]; 291 for (int i = 0; i < results.length; i++) { 292 results[i] = (String) names.elementAt(i); 293 } 294 return (results); 295 296 } 297 298 299 /** 300 * Convenience method to derive a name from the specified class and 301 * call <code>getInstance(String)</code> with it. 302 * 303 * @param clazz Class for which a suitable Log name will be derived 304 * 305 * @exception LogConfigurationException if a suitable <code>Log</code> 306 * instance cannot be returned 307 */ 308 public Log getInstance(Class clazz) throws LogConfigurationException { 309 310 return (getInstance(clazz.getName())); 311 312 } 313 314 315 /** 316 * <p>Construct (if necessary) and return a <code>Log</code> instance, 317 * using the factory's current set of configuration attributes.</p> 318 * 319 * <p><strong>NOTE</strong> - Depending upon the implementation of 320 * the <code>LogFactory</code> you are using, the <code>Log</code> 321 * instance you are returned may or may not be local to the current 322 * application, and may or may not be returned again on a subsequent 323 * call with the same name argument.</p> 324 * 325 * @param name Logical name of the <code>Log</code> instance to be 326 * returned (the meaning of this name is only known to the underlying 327 * logging implementation that is being wrapped) 328 * 329 * @exception LogConfigurationException if a suitable <code>Log</code> 330 * instance cannot be returned 331 */ 332 public Log getInstance(String name) throws LogConfigurationException { 333 334 Log instance = (Log) instances.get(name); 335 if (instance == null) { 336 instance = newInstance(name); 337 instances.put(name, instance); 338 } 339 return (instance); 340 341 } 342 343 344 /** 345 * Release any internal references to previously created 346 * {@link org.apache.commons.logging.Log} 347 * instances returned by this factory. This is useful in environments 348 * like servlet containers, which implement application reloading by 349 * throwing away a ClassLoader. Dangling references to objects in that 350 * class loader would prevent garbage collection. 351 */ 352 public void release() { 353 354 logDiagnostic("Releasing all known loggers"); 355 instances.clear(); 356 } 357 358 359 /** 360 * Remove any configuration attribute associated with the specified name. 361 * If there is no such attribute, no action is taken. 362 * 363 * @param name Name of the attribute to remove 364 */ 365 public void removeAttribute(String name) { 366 367 attributes.remove(name); 368 369 } 370 371 372 /** 373 * Set the configuration attribute with the specified name. Calling 374 * this with a <code>null</code> value is equivalent to calling 375 * <code>removeAttribute(name)</code>. 376 * <p> 377 * This method can be used to set logging configuration programmatically 378 * rather than via system properties. It can also be used in code running 379 * within a container (such as a webapp) to configure behaviour on a 380 * per-component level instead of globally as system properties would do. 381 * To use this method instead of a system property, call 382 * <pre> 383 * LogFactory.getFactory().setAttribute(...) 384 * </pre> 385 * This must be done before the first Log object is created; configuration 386 * changes after that point will be ignored. 387 * <p> 388 * This method is also called automatically if LogFactory detects a 389 * commons-logging.properties file; every entry in that file is set 390 * automatically as an attribute here. 391 * 392 * @param name Name of the attribute to set 393 * @param value Value of the attribute to set, or <code>null</code> 394 * to remove any setting for this attribute 395 */ 396 public void setAttribute(String name, Object value) { 397 398 if (logConstructor != null) { 399 logDiagnostic("setAttribute: call too late; configuration already performed."); 400 } 401 402 if (value == null) { 403 attributes.remove(name); 404 } else { 405 attributes.put(name, value); 406 } 407 408 if (name.equals(TCCL_KEY)) { 409 useTCCL = Boolean.valueOf(value.toString()).booleanValue(); 410 } 411 412 } 413 414 415 // ------------------------------------------------------ 416 // Static Methods 417 // 418 // These methods only defined as workarounds for a java 1.2 bug; 419 // theoretically none of these are needed. 420 // ------------------------------------------------------ 421 422 /** 423 * Gets the context classloader. 424 * This method is a workaround for a java 1.2 compiler bug. 425 * @since 1.1 426 */ 427 protected static ClassLoader getContextClassLoader() throws LogConfigurationException { 428 return LogFactory.getContextClassLoader(); 429 } 430 431 432 /** 433 * Workaround for bug in Java1.2; in theory this method is not needed. 434 * See LogFactory.isDiagnosticsEnabled. 435 */ 436 protected static boolean isDiagnosticsEnabled() { 437 return LogFactory.isDiagnosticsEnabled(); 438 } 439 440 441 /** 442 * Workaround for bug in Java1.2; in theory this method is not needed. 443 * See LogFactory.getClassLoader. 444 * @since 1.1 445 */ 446 protected static ClassLoader getClassLoader(Class clazz) { 447 return LogFactory.getClassLoader(clazz); 448 } 449 450 451 // ------------------------------------------------------ Protected Methods 452 453 /** 454 * Calculate and cache a string that uniquely identifies this instance, 455 * including which classloader the object was loaded from. 456 * <p> 457 * This string will later be prefixed to each "internal logging" message 458 * emitted, so that users can clearly see any unexpected behaviour. 459 * <p> 460 * Note that this method does not detect whether internal logging is 461 * enabled or not, nor where to output stuff if it is; that is all 462 * handled by the parent LogFactory class. This method just computes 463 * its own unique prefix for log messages. 464 */ 465 private void initDiagnostics() { 466 // It would be nice to include an identifier of the context classloader 467 // that this LogFactoryImpl object is responsible for. However that 468 // isn't possible as that information isn't available. It is possible 469 // to figure this out by looking at the logging from LogFactory to 470 // see the context & impl ids from when this object was instantiated, 471 // in order to link the impl id output as this object's prefix back to 472 // the context it is intended to manage. 473 // Note that this prefix should be kept consistent with that 474 // in LogFactory. 475 Class clazz = this.getClass(); 476 ClassLoader classLoader = getClassLoader(clazz); 477 String classLoaderName; 478 try { 479 if (classLoader == null) { 480 classLoaderName = "BOOTLOADER"; 481 } else { 482 classLoaderName = objectId(classLoader); 483 } 484 } catch(SecurityException e) { 485 classLoaderName = "UNKNOWN"; 486 } 487 diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] "; 488 } 489 490 491 /** 492 * Output a diagnostic message to a user-specified destination (if the 493 * user has enabled diagnostic logging). 494 * 495 * @param msg diagnostic message 496 * @since 1.1 497 */ 498 protected void logDiagnostic(String msg) { 499 if (isDiagnosticsEnabled()) { 500 logRawDiagnostic(diagnosticPrefix + msg); 501 } 502 } 503 504 /** 505 * Return the fully qualified Java classname of the {@link Log} 506 * implementation we will be using. 507 * 508 * @deprecated Never invoked by this class; subclasses should not assume 509 * it will be. 510 */ 511 protected String getLogClassName() { 512 513 if (logClassName == null) { 514 discoverLogImplementation(getClass().getName()); 515 } 516 517 return logClassName; 518 } 519 520 521 /** 522 * <p>Return the <code>Constructor</code> that can be called to instantiate 523 * new {@link org.apache.commons.logging.Log} instances.</p> 524 * 525 * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by 526 * calling this method from more than one thread are ignored, because 527 * the same <code>Constructor</code> instance will ultimately be derived 528 * in all circumstances.</p> 529 * 530 * @exception LogConfigurationException if a suitable constructor 531 * cannot be returned 532 * 533 * @deprecated Never invoked by this class; subclasses should not assume 534 * it will be. 535 */ 536 protected Constructor getLogConstructor() 537 throws LogConfigurationException { 538 539 // Return the previously identified Constructor (if any) 540 if (logConstructor == null) { 541 discoverLogImplementation(getClass().getName()); 542 } 543 544 return logConstructor; 545 } 546 547 548 /** 549 * Is <em>JDK 1.3 with Lumberjack</em> logging available? 550 * 551 * @deprecated Never invoked by this class; subclasses should not assume 552 * it will be. 553 */ 554 protected boolean isJdk13LumberjackAvailable() { 555 return isLogLibraryAvailable( 556 "Jdk13Lumberjack", 557 "org.apache.commons.logging.impl.Jdk13LumberjackLogger"); 558 } 559 560 561 /** 562 * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging 563 * is available. Also checks that the <code>Throwable</code> class 564 * supports <code>getStackTrace()</code>, which is required by 565 * Jdk14Logger.</p> 566 * 567 * @deprecated Never invoked by this class; subclasses should not assume 568 * it will be. 569 */ 570 protected boolean isJdk14Available() { 571 return isLogLibraryAvailable( 572 "Jdk14", 573 "org.apache.commons.logging.impl.Jdk14Logger"); 574 } 575 576 577 /** 578 * Is a <em>Log4J</em> implementation available? 579 * 580 * @deprecated Never invoked by this class; subclasses should not assume 581 * it will be. 582 */ 583 protected boolean isLog4JAvailable() { 584 return isLogLibraryAvailable( 585 "Log4J", 586 LOGGING_IMPL_LOG4J_LOGGER); 587 } 588 589 590 /** 591 * Create and return a new {@link org.apache.commons.logging.Log} 592 * instance for the specified name. 593 * 594 * @param name Name of the new logger 595 * 596 * @exception LogConfigurationException if a new instance cannot 597 * be created 598 */ 599 protected Log newInstance(String name) throws LogConfigurationException { 600 601 Log instance = null; 602 try { 603 if (logConstructor == null) { 604 instance = discoverLogImplementation(name); 605 } 606 else { 607 Object params[] = { name }; 608 instance = (Log) logConstructor.newInstance(params); 609 } 610 611 if (logMethod != null) { 612 Object params[] = { this }; 613 logMethod.invoke(instance, params); 614 } 615 616 return (instance); 617 618 } catch (LogConfigurationException lce) { 619 620 // this type of exception means there was a problem in discovery 621 // and we've already output diagnostics about the issue, etc.; 622 // just pass it on 623 throw (LogConfigurationException) lce; 624 625 } catch (InvocationTargetException e) { 626 // A problem occurred invoking the Constructor or Method 627 // previously discovered 628 Throwable c = e.getTargetException(); 629 if (c != null) { 630 throw new LogConfigurationException(c); 631 } else { 632 throw new LogConfigurationException(e); 633 } 634 } catch (Throwable t) { 635 // A problem occurred invoking the Constructor or Method 636 // previously discovered 637 throw new LogConfigurationException(t); 638 } 639 } 640 641 642 // ------------------------------------------------------ Private Methods 643 644 /** 645 * Calls LogFactory.directGetContextClassLoader under the control of an 646 * AccessController class. This means that java code running under a 647 * security manager that forbids access to ClassLoaders will still work 648 * if this class is given appropriate privileges, even when the caller 649 * doesn't have such privileges. Without using an AccessController, the 650 * the entire call stack must have the privilege before the call is 651 * allowed. 652 * 653 * @return the context classloader associated with the current thread, 654 * or null if security doesn't allow it. 655 * 656 * @throws LogConfigurationException if there was some weird error while 657 * attempting to get the context classloader. 658 * 659 * @throws SecurityException if the current java security policy doesn't 660 * allow this class to access the context classloader. 661 */ 662 private static ClassLoader getContextClassLoaderInternal() 663 throws LogConfigurationException { 664 return (ClassLoader)AccessController.doPrivileged( 665 new PrivilegedAction() { 666 public Object run() { 667 return LogFactory.directGetContextClassLoader(); 668 } 669 }); 670 } 671 672 /** 673 * Read the specified system property, using an AccessController so that 674 * the property can be read if JCL has been granted the appropriate 675 * security rights even if the calling code has not. 676 * <p> 677 * Take care not to expose the value returned by this method to the 678 * calling application in any way; otherwise the calling app can use that 679 * info to access data that should not be available to it. 680 */ 681 private static String getSystemProperty(final String key, final String def) 682 throws SecurityException { 683 return (String) AccessController.doPrivileged( 684 new PrivilegedAction() { 685 public Object run() { 686 return System.getProperty(key, def); 687 } 688 }); 689 } 690 691 /** 692 * Fetch the parent classloader of a specified classloader. 693 * <p> 694 * If a SecurityException occurs, null is returned. 695 * <p> 696 * Note that this method is non-static merely so logDiagnostic is available. 697 */ 698 private ClassLoader getParentClassLoader(final ClassLoader cl) { 699 try { 700 return (ClassLoader)AccessController.doPrivileged( 701 new PrivilegedAction() { 702 public Object run() { 703 return cl.getParent(); 704 } 705 }); 706 } catch(SecurityException ex) { 707 logDiagnostic("[SECURITY] Unable to obtain parent classloader"); 708 return null; 709 } 710 711 } 712 713 /** 714 * Utility method to check whether a particular logging library is 715 * present and available for use. Note that this does <i>not</i> 716 * affect the future behaviour of this class. 717 */ 718 private boolean isLogLibraryAvailable(String name, String classname) { 719 if (isDiagnosticsEnabled()) { 720 logDiagnostic("Checking for '" + name + "'."); 721 } 722 try { 723 Log log = createLogFromClass( 724 classname, 725 this.getClass().getName(), // dummy category 726 false); 727 728 if (log == null) { 729 if (isDiagnosticsEnabled()) { 730 logDiagnostic("Did not find '" + name + "'."); 731 } 732 return false; 733 } else { 734 if (isDiagnosticsEnabled()) { 735 logDiagnostic("Found '" + name + "'."); 736 } 737 return true; 738 } 739 } catch(LogConfigurationException e) { 740 if (isDiagnosticsEnabled()) { 741 logDiagnostic("Logging system '" + name + "' is available but not useable."); 742 } 743 return false; 744 } 745 } 746 747 /** 748 * Attempt to find an attribute (see method setAttribute) or a 749 * system property with the provided name and return its value. 750 * <p> 751 * The attributes associated with this object are checked before 752 * system properties in case someone has explicitly called setAttribute, 753 * or a configuration property has been set in a commons-logging.properties 754 * file. 755 * 756 * @return the value associated with the property, or null. 757 */ 758 private String getConfigurationValue(String property) { 759 if (isDiagnosticsEnabled()) { 760 logDiagnostic("[ENV] Trying to get configuration for item " + property); 761 } 762 763 Object valueObj = getAttribute(property); 764 if (valueObj != null) { 765 if (isDiagnosticsEnabled()) { 766 logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property); 767 } 768 return valueObj.toString(); 769 } 770 771 if (isDiagnosticsEnabled()) { 772 logDiagnostic("[ENV] No LogFactory attribute found for " + property); 773 } 774 775 try { 776 // warning: minor security hole here, in that we potentially read a system 777 // property that the caller cannot, then output it in readable form as a 778 // diagnostic message. However it's only ever JCL-specific properties 779 // involved here, so the harm is truly trivial. 780 String value = getSystemProperty(property, null); 781 if (value != null) { 782 if (isDiagnosticsEnabled()) { 783 logDiagnostic("[ENV] Found system property [" + value + "] for " + property); 784 } 785 return value; 786 } 787 788 if (isDiagnosticsEnabled()) { 789 logDiagnostic("[ENV] No system property found for property " + property); 790 } 791 } catch (SecurityException e) { 792 if (isDiagnosticsEnabled()) { 793 logDiagnostic("[ENV] Security prevented reading system property " + property); 794 } 795 } 796 797 if (isDiagnosticsEnabled()) { 798 logDiagnostic("[ENV] No configuration defined for item " + property); 799 } 800 801 return null; 802 } 803 804 /** 805 * Get the setting for the user-configurable behaviour specified by key. 806 * If nothing has explicitly been set, then return dflt. 807 */ 808 private boolean getBooleanConfiguration(String key, boolean dflt) { 809 String val = getConfigurationValue(key); 810 if (val == null) 811 return dflt; 812 return Boolean.valueOf(val).booleanValue(); 813 } 814 815 /** 816 * Initialize a number of variables that control the behaviour of this 817 * class and that can be tweaked by the user. This is done when the first 818 * logger is created, not in the constructor of this class, because we 819 * need to give the user a chance to call method setAttribute in order to 820 * configure this object. 821 */ 822 private void initConfiguration() { 823 allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true); 824 allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true); 825 allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true); 826 } 827 828 829 /** 830 * Attempts to create a Log instance for the given category name. 831 * Follows the discovery process described in the class javadoc. 832 * 833 * @param logCategory the name of the log category 834 * 835 * @throws LogConfigurationException if an error in discovery occurs, 836 * or if no adapter at all can be instantiated 837 */ 838 private Log discoverLogImplementation(String logCategory) 839 throws LogConfigurationException 840 { 841 if (isDiagnosticsEnabled()) { 842 logDiagnostic("Discovering a Log implementation..."); 843 } 844 845 initConfiguration(); 846 847 Log result = null; 848 849 // See if the user specified the Log implementation to use 850 String specifiedLogClassName = findUserSpecifiedLogClassName(); 851 852 if (specifiedLogClassName != null) { 853 if (isDiagnosticsEnabled()) { 854 logDiagnostic("Attempting to load user-specified log class '" + 855 specifiedLogClassName + "'..."); 856 } 857 858 result = createLogFromClass(specifiedLogClassName, 859 logCategory, 860 true); 861 if (result == null) { 862 StringBuffer messageBuffer = new StringBuffer("User-specified log class '"); 863 messageBuffer.append(specifiedLogClassName); 864 messageBuffer.append("' cannot be found or is not useable."); 865 866 // Mistyping or misspelling names is a common fault. 867 // Construct a good error message, if we can 868 if (specifiedLogClassName != null) { 869 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER); 870 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER); 871 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER); 872 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER); 873 } 874 throw new LogConfigurationException(messageBuffer.toString()); 875 } 876 877 return result; 878 } 879 880 // No user specified log; try to discover what's on the classpath 881 // 882 // Note that we deliberately loop here over classesToDiscover and 883 // expect method createLogFromClass to loop over the possible source 884 // classloaders. The effect is: 885 // for each discoverable log adapter 886 // for each possible classloader 887 // see if it works 888 // 889 // It appears reasonable at first glance to do the opposite: 890 // for each possible classloader 891 // for each discoverable log adapter 892 // see if it works 893 // 894 // The latter certainly has advantages for user-installable logging 895 // libraries such as log4j; in a webapp for example this code should 896 // first check whether the user has provided any of the possible 897 // logging libraries before looking in the parent classloader. 898 // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4, 899 // and SimpleLog will always work in any JVM. So the loop would never 900 // ever look for logging libraries in the parent classpath. Yet many 901 // users would expect that putting log4j there would cause it to be 902 // detected (and this is the historical JCL behaviour). So we go with 903 // the first approach. A user that has bundled a specific logging lib 904 // in a webapp should use a commons-logging.properties file or a 905 // service file in META-INF to force use of that logging lib anyway, 906 // rather than relying on discovery. 907 908 if (isDiagnosticsEnabled()) { 909 logDiagnostic( 910 "No user-specified Log implementation; performing discovery" + 911 " using the standard supported logging implementations..."); 912 } 913 for(int i=0; (i<classesToDiscover.length) && (result == null); ++i) { 914 result = createLogFromClass(classesToDiscover[i], logCategory, true); 915 } 916 917 if (result == null) { 918 throw new LogConfigurationException 919 ("No suitable Log implementation"); 920 } 921 922 return result; 923 } 924 925 926 /** 927 * Appends message if the given name is similar to the candidate. 928 * @param messageBuffer <code>StringBuffer</code> the message should be appended to, 929 * not null 930 * @param name the (trimmed) name to be test against the candidate, not null 931 * @param candidate the candidate name (not null) 932 */ 933 private void informUponSimilarName(final StringBuffer messageBuffer, final String name, 934 final String candidate) { 935 if (name.equals(candidate)) { 936 // Don't suggest a name that is exactly the same as the one the 937 // user tried... 938 return; 939 } 940 941 // If the user provides a name that is in the right package, and gets 942 // the first 5 characters of the adapter class right (ignoring case), 943 // then suggest the candidate adapter class name. 944 if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) { 945 messageBuffer.append(" Did you mean '"); 946 messageBuffer.append(candidate); 947 messageBuffer.append("'?"); 948 } 949 } 950 951 952 /** 953 * Checks system properties and the attribute map for 954 * a Log implementation specified by the user under the 955 * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}. 956 * 957 * @return classname specified by the user, or <code>null</code> 958 */ 959 private String findUserSpecifiedLogClassName() 960 { 961 if (isDiagnosticsEnabled()) { 962 logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'"); 963 } 964 String specifiedClass = (String) getAttribute(LOG_PROPERTY); 965 966 if (specifiedClass == null) { // @deprecated 967 if (isDiagnosticsEnabled()) { 968 logDiagnostic("Trying to get log class from attribute '" + 969 LOG_PROPERTY_OLD + "'"); 970 } 971 specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD); 972 } 973 974 if (specifiedClass == null) { 975 if (isDiagnosticsEnabled()) { 976 logDiagnostic("Trying to get log class from system property '" + 977 LOG_PROPERTY + "'"); 978 } 979 try { 980 specifiedClass = getSystemProperty(LOG_PROPERTY, null); 981 } catch (SecurityException e) { 982 if (isDiagnosticsEnabled()) { 983 logDiagnostic("No access allowed to system property '" + 984 LOG_PROPERTY + "' - " + e.getMessage()); 985 } 986 } 987 } 988 989 if (specifiedClass == null) { // @deprecated 990 if (isDiagnosticsEnabled()) { 991 logDiagnostic("Trying to get log class from system property '" + 992 LOG_PROPERTY_OLD + "'"); 993 } 994 try { 995 specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null); 996 } catch (SecurityException e) { 997 if (isDiagnosticsEnabled()) { 998 logDiagnostic("No access allowed to system property '" + 999 LOG_PROPERTY_OLD + "' - " + e.getMessage()); 1000 } 1001 } 1002 } 1003 1004 // Remove any whitespace; it's never valid in a classname so its 1005 // presence just means a user mistake. As we know what they meant, 1006 // we may as well strip the spaces. 1007 if (specifiedClass != null) { 1008 specifiedClass = specifiedClass.trim(); 1009 } 1010 1011 return specifiedClass; 1012 } 1013 1014 1015 /** 1016 * Attempts to load the given class, find a suitable constructor, 1017 * and instantiate an instance of Log. 1018 * 1019 * @param logAdapterClassName classname of the Log implementation 1020 * 1021 * @param logCategory argument to pass to the Log implementation's 1022 * constructor 1023 * 1024 * @param affectState <code>true</code> if this object's state should 1025 * be affected by this method call, <code>false</code> otherwise. 1026 * 1027 * @return an instance of the given class, or null if the logging 1028 * library associated with the specified adapter is not available. 1029 * 1030 * @throws LogConfigurationException if there was a serious error with 1031 * configuration and the handleFlawedDiscovery method decided this 1032 * problem was fatal. 1033 */ 1034 private Log createLogFromClass(String logAdapterClassName, 1035 String logCategory, 1036 boolean affectState) 1037 throws LogConfigurationException { 1038 1039 if (isDiagnosticsEnabled()) { 1040 logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'"); 1041 } 1042 1043 Object[] params = { logCategory }; 1044 Log logAdapter = null; 1045 Constructor constructor = null; 1046 1047 Class logAdapterClass = null; 1048 ClassLoader currentCL = getBaseClassLoader(); 1049 1050 for(;;) { 1051 // Loop through the classloader hierarchy trying to find 1052 // a viable classloader. 1053 logDiagnostic( 1054 "Trying to load '" 1055 + logAdapterClassName 1056 + "' from classloader " 1057 + objectId(currentCL)); 1058 try { 1059 if (isDiagnosticsEnabled()) { 1060 // Show the location of the first occurrence of the .class file 1061 // in the classpath. This is the location that ClassLoader.loadClass 1062 // will load the class from -- unless the classloader is doing 1063 // something weird. 1064 URL url; 1065 String resourceName = logAdapterClassName.replace('.', '/') + ".class"; 1066 if (currentCL != null) { 1067 url = currentCL.getResource(resourceName ); 1068 } else { 1069 url = ClassLoader.getSystemResource(resourceName + ".class"); 1070 } 1071 1072 if (url == null) { 1073 logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found."); 1074 } else { 1075 logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'"); 1076 } 1077 } 1078 1079 Class c = null; 1080 try { 1081 c = Class.forName(logAdapterClassName, true, currentCL); 1082 } catch (ClassNotFoundException originalClassNotFoundException) { 1083 // The current classloader was unable to find the log adapter 1084 // in this or any ancestor classloader. There's no point in 1085 // trying higher up in the hierarchy in this case.. 1086 String msg = "" + originalClassNotFoundException.getMessage(); 1087 logDiagnostic( 1088 "The log adapter '" 1089 + logAdapterClassName 1090 + "' is not available via classloader " 1091 + objectId(currentCL) 1092 + ": " 1093 + msg.trim()); 1094 try { 1095 // Try the class classloader. 1096 // This may work in cases where the TCCL 1097 // does not contain the code executed or JCL. 1098 // This behaviour indicates that the application 1099 // classloading strategy is not consistent with the 1100 // Java 1.2 classloading guidelines but JCL can 1101 // and so should handle this case. 1102 c = Class.forName(logAdapterClassName); 1103 } catch (ClassNotFoundException secondaryClassNotFoundException) { 1104 // no point continuing: this adapter isn't available 1105 msg = "" + secondaryClassNotFoundException.getMessage(); 1106 logDiagnostic( 1107 "The log adapter '" 1108 + logAdapterClassName 1109 + "' is not available via the LogFactoryImpl class classloader: " 1110 + msg.trim()); 1111 break; 1112 } 1113 } 1114 1115 constructor = c.getConstructor(logConstructorSignature); 1116 Object o = constructor.newInstance(params); 1117 1118 // Note that we do this test after trying to create an instance 1119 // [rather than testing Log.class.isAssignableFrom(c)] so that 1120 // we don't complain about Log hierarchy problems when the 1121 // adapter couldn't be instantiated anyway. 1122 if (o instanceof Log) { 1123 logAdapterClass = c; 1124 logAdapter = (Log) o; 1125 break; 1126 } 1127 1128 // Oops, we have a potential problem here. An adapter class 1129 // has been found and its underlying lib is present too, but 1130 // there are multiple Log interface classes available making it 1131 // impossible to cast to the type the caller wanted. We 1132 // certainly can't use this logger, but we need to know whether 1133 // to keep on discovering or terminate now. 1134 // 1135 // The handleFlawedHierarchy method will throw 1136 // LogConfigurationException if it regards this problem as 1137 // fatal, and just return if not. 1138 handleFlawedHierarchy(currentCL, c); 1139 } catch (NoClassDefFoundError e) { 1140 // We were able to load the adapter but it had references to 1141 // other classes that could not be found. This simply means that 1142 // the underlying logger library is not present in this or any 1143 // ancestor classloader. There's no point in trying higher up 1144 // in the hierarchy in this case.. 1145 String msg = "" + e.getMessage(); 1146 logDiagnostic( 1147 "The log adapter '" 1148 + logAdapterClassName 1149 + "' is missing dependencies when loaded via classloader " 1150 + objectId(currentCL) 1151 + ": " 1152 + msg.trim()); 1153 break; 1154 } catch (ExceptionInInitializerError e) { 1155 // A static initializer block or the initializer code associated 1156 // with a static variable on the log adapter class has thrown 1157 // an exception. 1158 // 1159 // We treat this as meaning the adapter's underlying logging 1160 // library could not be found. 1161 String msg = "" + e.getMessage(); 1162 logDiagnostic( 1163 "The log adapter '" 1164 + logAdapterClassName 1165 + "' is unable to initialize itself when loaded via classloader " 1166 + objectId(currentCL) 1167 + ": " 1168 + msg.trim()); 1169 break; 1170 } catch(LogConfigurationException e) { 1171 // call to handleFlawedHierarchy above must have thrown 1172 // a LogConfigurationException, so just throw it on 1173 throw e; 1174 } catch(Throwable t) { 1175 // handleFlawedDiscovery will determine whether this is a fatal 1176 // problem or not. If it is fatal, then a LogConfigurationException 1177 // will be thrown. 1178 handleFlawedDiscovery(logAdapterClassName, currentCL, t); 1179 } 1180 1181 if (currentCL == null) { 1182 break; 1183 } 1184 1185 // try the parent classloader 1186 // currentCL = currentCL.getParent(); 1187 currentCL = getParentClassLoader(currentCL); 1188 } 1189 1190 if ((logAdapter != null) && affectState) { 1191 // We've succeeded, so set instance fields 1192 this.logClassName = logAdapterClassName; 1193 this.logConstructor = constructor; 1194 1195 // Identify the <code>setLogFactory</code> method (if there is one) 1196 try { 1197 this.logMethod = logAdapterClass.getMethod("setLogFactory", 1198 logMethodSignature); 1199 logDiagnostic("Found method setLogFactory(LogFactory) in '" 1200 + logAdapterClassName + "'"); 1201 } catch (Throwable t) { 1202 this.logMethod = null; 1203 logDiagnostic( 1204 "[INFO] '" + logAdapterClassName 1205 + "' from classloader " + objectId(currentCL) 1206 + " does not declare optional method " 1207 + "setLogFactory(LogFactory)"); 1208 } 1209 1210 logDiagnostic( 1211 "Log adapter '" + logAdapterClassName 1212 + "' from classloader " + objectId(logAdapterClass.getClassLoader()) 1213 + " has been selected for use."); 1214 } 1215 1216 return logAdapter; 1217 } 1218 1219 1220 /** 1221 * Return the classloader from which we should try to load the logging 1222 * adapter classes. 1223 * <p> 1224 * This method usually returns the context classloader. However if it 1225 * is discovered that the classloader which loaded this class is a child 1226 * of the context classloader <i>and</i> the allowFlawedContext option 1227 * has been set then the classloader which loaded this class is returned 1228 * instead. 1229 * <p> 1230 * The only time when the classloader which loaded this class is a 1231 * descendant (rather than the same as or an ancestor of the context 1232 * classloader) is when an app has created custom classloaders but 1233 * failed to correctly set the context classloader. This is a bug in 1234 * the calling application; however we provide the option for JCL to 1235 * simply generate a warning rather than fail outright. 1236 * 1237 */ 1238 private ClassLoader getBaseClassLoader() throws LogConfigurationException { 1239 ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class); 1240 1241 if (useTCCL == false) { 1242 return thisClassLoader; 1243 } 1244 1245 ClassLoader contextClassLoader = getContextClassLoaderInternal(); 1246 1247 ClassLoader baseClassLoader = getLowestClassLoader( 1248 contextClassLoader, thisClassLoader); 1249 1250 if (baseClassLoader == null) { 1251 // The two classloaders are not part of a parent child relationship. 1252 // In some classloading setups (e.g. JBoss with its 1253 // UnifiedLoaderRepository) this can still work, so if user hasn't 1254 // forbidden it, just return the contextClassLoader. 1255 if (allowFlawedContext) { 1256 if (isDiagnosticsEnabled()) { 1257 logDiagnostic( 1258 "[WARNING] the context classloader is not part of a" 1259 + " parent-child relationship with the classloader that" 1260 + " loaded LogFactoryImpl."); 1261 } 1262 // If contextClassLoader were null, getLowestClassLoader() would 1263 // have returned thisClassLoader. The fact we are here means 1264 // contextClassLoader is not null, so we can just return it. 1265 return contextClassLoader; 1266 } 1267 else { 1268 throw new LogConfigurationException( 1269 "Bad classloader hierarchy; LogFactoryImpl was loaded via" 1270 + " a classloader that is not related to the current context" 1271 + " classloader."); 1272 } 1273 } 1274 1275 if (baseClassLoader != contextClassLoader) { 1276 // We really should just use the contextClassLoader as the starting 1277 // point for scanning for log adapter classes. However it is expected 1278 // that there are a number of broken systems out there which create 1279 // custom classloaders but fail to set the context classloader so 1280 // we handle those flawed systems anyway. 1281 if (allowFlawedContext) { 1282 if (isDiagnosticsEnabled()) { 1283 logDiagnostic( 1284 "Warning: the context classloader is an ancestor of the" 1285 + " classloader that loaded LogFactoryImpl; it should be" 1286 + " the same or a descendant. The application using" 1287 + " commons-logging should ensure the context classloader" 1288 + " is used correctly."); 1289 } 1290 } else { 1291 throw new LogConfigurationException( 1292 "Bad classloader hierarchy; LogFactoryImpl was loaded via" 1293 + " a classloader that is not related to the current context" 1294 + " classloader."); 1295 } 1296 } 1297 1298 return baseClassLoader; 1299 } 1300 1301 /** 1302 * Given two related classloaders, return the one which is a child of 1303 * the other. 1304 * <p> 1305 * @param c1 is a classloader (including the null classloader) 1306 * @param c2 is a classloader (including the null classloader) 1307 * 1308 * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor, 1309 * and null if neither is an ancestor of the other. 1310 */ 1311 private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) { 1312 // TODO: use AccessController when dealing with classloaders here 1313 1314 if (c1 == null) 1315 return c2; 1316 1317 if (c2 == null) 1318 return c1; 1319 1320 ClassLoader current; 1321 1322 // scan c1's ancestors to find c2 1323 current = c1; 1324 while (current != null) { 1325 if (current == c2) 1326 return c1; 1327 current = current.getParent(); 1328 } 1329 1330 // scan c2's ancestors to find c1 1331 current = c2; 1332 while (current != null) { 1333 if (current == c1) 1334 return c2; 1335 current = current.getParent(); 1336 } 1337 1338 return null; 1339 } 1340 1341 /** 1342 * Generates an internal diagnostic logging of the discovery failure and 1343 * then throws a <code>LogConfigurationException</code> that wraps 1344 * the passed <code>Throwable</code>. 1345 * 1346 * @param logAdapterClassName is the class name of the Log implementation 1347 * that could not be instantiated. Cannot be <code>null</code>. 1348 * 1349 * @param classLoader is the classloader that we were trying to load the 1350 * logAdapterClassName from when the exception occurred. 1351 * 1352 * @param discoveryFlaw is the Throwable created by the classloader 1353 * 1354 * @throws LogConfigurationException ALWAYS 1355 */ 1356 private void handleFlawedDiscovery(String logAdapterClassName, 1357 ClassLoader classLoader, 1358 Throwable discoveryFlaw) { 1359 1360 if (isDiagnosticsEnabled()) { 1361 logDiagnostic("Could not instantiate Log '" 1362 + logAdapterClassName + "' -- " 1363 + discoveryFlaw.getClass().getName() + ": " 1364 + discoveryFlaw.getLocalizedMessage()); 1365 1366 if (discoveryFlaw instanceof InvocationTargetException ) { 1367 // Ok, the lib is there but while trying to create a real underlying 1368 // logger something failed in the underlying lib; display info about 1369 // that if possible. 1370 InvocationTargetException ite = (InvocationTargetException)discoveryFlaw; 1371 Throwable cause = ite.getTargetException(); 1372 if (cause != null) { 1373 logDiagnostic("... InvocationTargetException: " + 1374 cause.getClass().getName() + ": " + 1375 cause.getLocalizedMessage()); 1376 1377 if (cause instanceof ExceptionInInitializerError) { 1378 ExceptionInInitializerError eiie = (ExceptionInInitializerError)cause; 1379 Throwable cause2 = eiie.getException(); 1380 if (cause2 != null) { 1381 logDiagnostic("... ExceptionInInitializerError: " + 1382 cause2.getClass().getName() + ": " + 1383 cause2.getLocalizedMessage()); 1384 } 1385 } 1386 } 1387 } 1388 } 1389 1390 if (!allowFlawedDiscovery) { 1391 throw new LogConfigurationException(discoveryFlaw); 1392 } 1393 } 1394 1395 1396 /** 1397 * Report a problem loading the log adapter, then either return 1398 * (if the situation is considered recoverable) or throw a 1399 * LogConfigurationException. 1400 * <p> 1401 * There are two possible reasons why we successfully loaded the 1402 * specified log adapter class then failed to cast it to a Log object: 1403 * <ol> 1404 * <li>the specific class just doesn't implement the Log interface 1405 * (user screwed up), or 1406 * <li> the specified class has bound to a Log class loaded by some other 1407 * classloader; Log@classloaderX cannot be cast to Log@classloaderY. 1408 * </ol> 1409 * <p> 1410 * Here we try to figure out which case has occurred so we can give the 1411 * user some reasonable feedback. 1412 * 1413 * @param badClassLoader is the classloader we loaded the problem class from, 1414 * ie it is equivalent to badClass.getClassLoader(). 1415 * 1416 * @param badClass is a Class object with the desired name, but which 1417 * does not implement Log correctly. 1418 * 1419 * @throws LogConfigurationException when the situation 1420 * should not be recovered from. 1421 */ 1422 private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass) 1423 throws LogConfigurationException { 1424 1425 boolean implementsLog = false; 1426 String logInterfaceName = Log.class.getName(); 1427 Class interfaces[] = badClass.getInterfaces(); 1428 for (int i = 0; i < interfaces.length; i++) { 1429 if (logInterfaceName.equals(interfaces[i].getName())) { 1430 implementsLog = true; 1431 break; 1432 } 1433 } 1434 1435 if (implementsLog) { 1436 // the class does implement an interface called Log, but 1437 // it is in the wrong classloader 1438 if (isDiagnosticsEnabled()) { 1439 try { 1440 ClassLoader logInterfaceClassLoader = getClassLoader(Log.class); 1441 logDiagnostic( 1442 "Class '" + badClass.getName() 1443 + "' was found in classloader " 1444 + objectId(badClassLoader) 1445 + ". It is bound to a Log interface which is not" 1446 + " the one loaded from classloader " 1447 + objectId(logInterfaceClassLoader)); 1448 } catch (Throwable t) { 1449 logDiagnostic( 1450 "Error while trying to output diagnostics about" 1451 + " bad class '" + badClass + "'"); 1452 } 1453 } 1454 1455 if (!allowFlawedHierarchy) { 1456 StringBuffer msg = new StringBuffer(); 1457 msg.append("Terminating logging for this context "); 1458 msg.append("due to bad log hierarchy. "); 1459 msg.append("You have more than one version of '"); 1460 msg.append(Log.class.getName()); 1461 msg.append("' visible."); 1462 if (isDiagnosticsEnabled()) { 1463 logDiagnostic(msg.toString()); 1464 } 1465 throw new LogConfigurationException(msg.toString()); 1466 } 1467 1468 if (isDiagnosticsEnabled()) { 1469 StringBuffer msg = new StringBuffer(); 1470 msg.append("Warning: bad log hierarchy. "); 1471 msg.append("You have more than one version of '"); 1472 msg.append(Log.class.getName()); 1473 msg.append("' visible."); 1474 logDiagnostic(msg.toString()); 1475 } 1476 } else { 1477 // this is just a bad adapter class 1478 if (!allowFlawedDiscovery) { 1479 StringBuffer msg = new StringBuffer(); 1480 msg.append("Terminating logging for this context. "); 1481 msg.append("Log class '"); 1482 msg.append(badClass.getName()); 1483 msg.append("' does not implement the Log interface."); 1484 if (isDiagnosticsEnabled()) { 1485 logDiagnostic(msg.toString()); 1486 } 1487 1488 throw new LogConfigurationException(msg.toString()); 1489 } 1490 1491 if (isDiagnosticsEnabled()) { 1492 StringBuffer msg = new StringBuffer(); 1493 msg.append("[WARNING] Log class '"); 1494 msg.append(badClass.getName()); 1495 msg.append("' does not implement the Log interface."); 1496 logDiagnostic(msg.toString()); 1497 } 1498 } 1499 } 1500 }