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