View Javadoc
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      *  &lt;java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"&gt;
170      *     ...
171      *     &lt;sysproperty
172      *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
173      *        value="org.apache.commons.logging.AltHashtable"/&gt;
174      *  &lt;/java&gt;
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 }