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