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