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