LogFactory.java

  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. package org.apache.commons.logging;

  18. import java.io.FileOutputStream;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.PrintStream;
  22. import java.lang.ref.WeakReference;
  23. import java.net.URL;
  24. import java.net.URLConnection;
  25. import java.nio.charset.StandardCharsets;
  26. import java.security.AccessController;
  27. import java.security.PrivilegedAction;
  28. import java.util.Enumeration;
  29. import java.util.Hashtable;
  30. import java.util.Iterator;
  31. import java.util.Objects;
  32. import java.util.Properties;
  33. import java.util.ServiceConfigurationError;
  34. import java.util.ServiceLoader;
  35. import java.util.function.Supplier;

  36. /**
  37.  * Factory for creating {@link Log} instances, with discovery and
  38.  * configuration features similar to that employed by standard Java APIs
  39.  * such as JAXP.
  40.  * <p>
  41.  * <strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
  42.  * based on the SAXParserFactory and DocumentBuilderFactory implementations
  43.  * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
  44.  * </p>
  45.  */
  46. public abstract class LogFactory {
  47.     // Implementation note re AccessController usage
  48.     //
  49.     // It is important to keep code invoked via an AccessController to small
  50.     // auditable blocks. Such code must carefully evaluate all user input
  51.     // (parameters, system properties, configuration file contents, etc). As an
  52.     // example, a Log implementation should not write to its log file
  53.     // with an AccessController anywhere in the call stack, otherwise an
  54.     // insecure application could configure the log implementation to write
  55.     // to a protected file using the privileges granted to JCL rather than
  56.     // to the calling application.
  57.     //
  58.     // Under no circumstance should a non-private method return data that is
  59.     // retrieved via an AccessController. That would allow an insecure application
  60.     // to invoke that method and obtain data that it is not permitted to have.
  61.     //
  62.     // Invoking user-supplied code with an AccessController set is not a major
  63.     // issue (for example, invoking the constructor of the class specified by
  64.     // HASHTABLE_IMPLEMENTATION_PROPERTY). That class will be in a different
  65.     // trust domain, and therefore must have permissions to do whatever it
  66.     // is trying to do regardless of the permissions granted to JCL. There is
  67.     // a slight issue in that untrusted code may point that environment variable
  68.     // to another trusted library, in which case the code runs if both that
  69.     // library and JCL have the necessary permissions even when the untrusted
  70.     // caller does not. That's a pretty hard route to exploit though.

  71.     /**
  72.      * The name ({@code priority}) of the key in the configuration file used to
  73.      * specify the priority of that particular configuration file. The associated value
  74.      * is a floating-point number; higher values take priority over lower values.
  75.      */
  76.     public static final String PRIORITY_KEY = "priority";

  77.     /**
  78.      * The name ({@code use_tccl}) of the key in the configuration file used
  79.      * to specify whether logging classes should be loaded via the thread
  80.      * context class loader (TCCL), or not. By default, the TCCL is used.
  81.      */
  82.     public static final String TCCL_KEY = "use_tccl";

  83.     /**
  84.      * The name ({@code org.apache.commons.logging.LogFactory}) of the property
  85.      * used to identify the LogFactory implementation
  86.      * class name. This can be used as a system property, or as an entry in a
  87.      * configuration properties file.
  88.      */
  89.     public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory";

  90.     private static final String FACTORY_LOG4J_API = "org.apache.commons.logging.impl.Log4jApiLogFactory";

  91.     private static final String LOG4J_TO_SLF4J_BRIDGE = "org.apache.logging.slf4j.SLF4JProvider";

  92.     private static final String FACTORY_SLF4J = "org.apache.commons.logging.impl.Slf4jLogFactory";

  93.     /**
  94.      * The fully qualified class name of the fallback {@code LogFactory}
  95.      * implementation class to use, if no other can be found.
  96.      */
  97.     public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl";

  98.     /**
  99.      * The name ({@code commons-logging.properties}) of the properties file to search for.
  100.      */
  101.     public static final String FACTORY_PROPERTIES = "commons-logging.properties";

  102.     /**
  103.      * JDK 1.3+ <a href="https://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
  104.      * 'Service Provider' specification</a>.
  105.      */
  106.     protected static final String SERVICE_ID = "META-INF/services/org.apache.commons.logging.LogFactory";

  107.     /**
  108.      * The name ({@code org.apache.commons.logging.diagnostics.dest})
  109.      * of the property used to enable internal commons-logging
  110.      * diagnostic output, in order to get information on what logging
  111.      * implementations are being discovered, what class loaders they
  112.      * are loaded through, etc.
  113.      * <p>
  114.      * If a system property of this name is set then the value is
  115.      * assumed to be the name of a file. The special strings
  116.      * STDOUT or STDERR (case-sensitive) indicate output to
  117.      * System.out and System.err respectively.
  118.      * <p>
  119.      * Diagnostic logging should be used only to debug problematic
  120.      * configurations and should not be set in normal production use.
  121.      */
  122.     public static final String DIAGNOSTICS_DEST_PROPERTY = "org.apache.commons.logging.diagnostics.dest";

  123.     /**
  124.      * When null (the usual case), no diagnostic output will be
  125.      * generated by LogFactory or LogFactoryImpl. When non-null,
  126.      * interesting events will be written to the specified object.
  127.      */
  128.     private static final PrintStream DIAGNOSTICS_STREAM;

  129.     /**
  130.      * A string that gets prefixed to every message output by the
  131.      * logDiagnostic method, so that users can clearly see which
  132.      * LogFactory class is generating the output.
  133.      */
  134.     private static final String DIAGNOSTICS_PREFIX;

  135.     /**
  136.      * Setting this system property
  137.      * ({@code org.apache.commons.logging.LogFactory.HashtableImpl})
  138.      * value allows the {@code Hashtable} used to store
  139.      * class loaders to be substituted by an alternative implementation.
  140.      * <p>
  141.      * <strong>Note:</strong> {@code LogFactory} will print:
  142.      * </p>
  143.      * <pre>
  144.      * [ERROR] LogFactory: Load of custom hash table failed
  145.      * </pre>
  146.      * <p>
  147.      * to system error and then continue using a standard Hashtable.
  148.      * </p>
  149.      * <p>
  150.      * <strong>Usage:</strong> Set this property when Java is invoked
  151.      * and {@code LogFactory} will attempt to load a new instance
  152.      * of the given implementation class.
  153.      * For example, running the following ant scriplet:
  154.      * </p>
  155.      * <pre>
  156.      *  &lt;java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}"&gt;
  157.      *     ...
  158.      *     &lt;sysproperty
  159.      *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
  160.      *        value="org.apache.commons.logging.AltHashtable"/&gt;
  161.      *  &lt;/java&gt;
  162.      * </pre>
  163.      * <p>
  164.      * will mean that {@code LogFactory} will load an instance of
  165.      * {@code org.apache.commons.logging.AltHashtable}.
  166.      * </p>
  167.      * <p>
  168.      * A typical use case is to allow a custom
  169.      * Hashtable implementation using weak references to be substituted.
  170.      * This will allow class loaders to be garbage collected without
  171.      * the need to release them (on 1.3+ JVMs only, of course ;).
  172.      * </p>
  173.      */
  174.     public static final String HASHTABLE_IMPLEMENTATION_PROPERTY = "org.apache.commons.logging.LogFactory.HashtableImpl";

  175.     /** Name used to load the weak hash table implementation by names. */
  176.     private static final String WEAK_HASHTABLE_CLASSNAME = "org.apache.commons.logging.impl.WeakHashtable";

  177.     /**
  178.      * A reference to the class loader that loaded this class. This is the
  179.      * same as LogFactory.class.getClassLoader(). However computing this
  180.      * value isn't quite as simple as that, as we potentially need to use
  181.      * AccessControllers etc. It's more efficient to compute it once and
  182.      * cache it here.
  183.      */
  184.     private static final WeakReference<ClassLoader> thisClassLoaderRef;

  185.     /**
  186.      * Maximum number of {@link ServiceLoader} errors to ignore, while
  187.      * looking for an implementation.
  188.      */
  189.     private static final int MAX_BROKEN_SERVICES = 3;

  190.     /**
  191.      * The previously constructed {@code LogFactory} instances, keyed by
  192.      * the {@code ClassLoader} with which it was created.
  193.      */
  194.     protected static Hashtable<ClassLoader, LogFactory> factories;

  195.     /**
  196.      * Previously constructed {@code LogFactory} instance as in the
  197.      * {@code factories} map, but for the case where
  198.      * {@code getClassLoader} returns {@code null}.
  199.      * This can happen when:
  200.      * <ul>
  201.      * <li>using JDK1.1 and the calling code is loaded via the system
  202.      *  class loader (very common)</li>
  203.      * <li>using JDK1.2+ and the calling code is loaded via the boot
  204.      *  class loader (only likely for embedded systems work).</li>
  205.      * </ul>
  206.      * Note that {@code factories} is a <em>Hashtable</em> (not a HashMap),
  207.      * and hash tables don't allow null as a key.
  208.      * @deprecated since 1.1.2
  209.      */
  210.     @Deprecated
  211.     protected static volatile LogFactory nullClassLoaderFactory;

  212.     static {
  213.         // note: it's safe to call methods before initDiagnostics (though
  214.         // diagnostic output gets discarded).
  215.         final ClassLoader thisClassLoader = getClassLoader(LogFactory.class);
  216.         thisClassLoaderRef = new WeakReference<>(thisClassLoader);
  217.         // In order to avoid confusion where multiple instances of JCL are
  218.         // being used via different class loaders within the same app, we
  219.         // ensure each logged message has a prefix of form
  220.         // [LogFactory from class loader OID]
  221.         //
  222.         // Note that this prefix should be kept consistent with that
  223.         // in LogFactoryImpl. However here we don't need to output info
  224.         // about the actual *instance* of LogFactory, as all methods that
  225.         // output diagnostics from this class are static.
  226.         String classLoaderName;
  227.         try {
  228.             classLoaderName = thisClassLoader != null ? objectId(thisClassLoader) : "BOOTLOADER";
  229.         } catch (final SecurityException e) {
  230.             classLoaderName = "UNKNOWN";
  231.         }
  232.         DIAGNOSTICS_PREFIX = "[LogFactory from " + classLoaderName + "] ";
  233.         DIAGNOSTICS_STREAM = initDiagnostics();
  234.         logClassLoaderEnvironment(LogFactory.class);
  235.         factories = createFactoryStore();
  236.         logDiagnostic("BOOTSTRAP COMPLETED");
  237.     }

  238.     /**
  239.      * Remember this factory, so later calls to LogFactory.getCachedFactory
  240.      * can return the previously created object (together with all its
  241.      * cached Log objects).
  242.      *
  243.      * @param classLoader should be the current context class loader. Note that
  244.      *  this can be null under some circumstances; this is ok.
  245.      * @param factory should be the factory to cache. This should never be null.
  246.      */
  247.     private static void cacheFactory(final ClassLoader classLoader, final LogFactory factory) {
  248.         // Ideally we would assert(factory != null) here. However reporting
  249.         // errors from within a logging implementation is a little tricky!
  250.         if (factory != null) {
  251.             if (classLoader == null) {
  252.                 nullClassLoaderFactory = factory;
  253.             } else {
  254.                 factories.put(classLoader, factory);
  255.             }
  256.         }
  257.     }

  258.     /**
  259.      * Creates a LogFactory object or a LogConfigurationException object.
  260.      *
  261.      * @param factoryClassName Factory class.
  262.      * @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.
  263.      *                         Note that the class loader which loaded this class might be "null" (for example, the boot loader) for embedded systems.
  264.      * @return either a LogFactory object or a LogConfigurationException object.
  265.      * @since 1.1
  266.      */
  267.     protected static Object createFactory(final String factoryClassName, final ClassLoader classLoader) {
  268.         // This will be used to diagnose bad configurations
  269.         // and allow a useful message to be sent to the user
  270.         Class<?> logFactoryClass = null;
  271.         try {
  272.             if (classLoader != null) {
  273.                 try {
  274.                     // First the given class loader param (thread class loader)

  275.                     // Warning: must typecast here & allow exception
  276.                     // to be generated/caught & recast properly.
  277.                     logFactoryClass = classLoader.loadClass(factoryClassName);
  278.                     if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
  279.                         if (isDiagnosticsEnabled()) {
  280.                             logDiagnostic("Loaded class " + logFactoryClass.getName() + " from class loader " + objectId(classLoader));
  281.                         }
  282.                     } else //
  283.                     // This indicates a problem with the ClassLoader tree.
  284.                     // An incompatible ClassLoader was used to load the
  285.                     // implementation.
  286.                     // As the same classes
  287.                     // must be available in multiple class loaders,
  288.                     // it is very likely that multiple JCL jars are present.
  289.                     // The most likely fix for this
  290.                     // problem is to remove the extra JCL jars from the
  291.                     // ClassLoader hierarchy.
  292.                     //
  293.                     if (isDiagnosticsEnabled()) {
  294.                         logDiagnostic("Factory class " + logFactoryClass.getName() + " loaded from class loader " + objectId(logFactoryClass.getClassLoader())
  295.                                 + " does not extend '" + LogFactory.class.getName() + "' as loaded by this class loader.");
  296.                         logHierarchy("[BAD CL TREE] ", classLoader);
  297.                     }
  298.                     // Force a ClassCastException
  299.                     return LogFactory.class.cast(logFactoryClass.getConstructor().newInstance());

  300.                 } catch (final ClassNotFoundException ex) {
  301.                     if (classLoader == thisClassLoaderRef.get()) {
  302.                         // Nothing more to try, onwards.
  303.                         if (isDiagnosticsEnabled()) {
  304.                             logDiagnostic("Unable to locate any class called '" + factoryClassName + "' via class loader " + objectId(classLoader));
  305.                         }
  306.                         throw ex;
  307.                     }
  308.                     // ignore exception, continue
  309.                 } catch (final NoClassDefFoundError e) {
  310.                     if (classLoader == thisClassLoaderRef.get()) {
  311.                         // Nothing more to try, onwards.
  312.                         if (isDiagnosticsEnabled()) {
  313.                             logDiagnostic("Class '" + factoryClassName + "' cannot be loaded" + " via class loader " + objectId(classLoader)
  314.                                     + " - it depends on some other class that cannot be found.");
  315.                         }
  316.                         throw e;
  317.                     }
  318.                     // ignore exception, continue
  319.                 } catch (final ClassCastException e) {
  320.                     if (classLoader == thisClassLoaderRef.get()) {
  321.                         // There's no point in falling through to the code below that
  322.                         // tries again with thisClassLoaderRef, because we've just tried
  323.                         // loading with that loader (not the TCCL). Just throw an
  324.                         // appropriate exception here.
  325.                         final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
  326.                         //
  327.                         // Construct a good message: users may not actual expect that a custom implementation
  328.                         // has been specified. Several well known containers use this mechanism to adapt JCL
  329.                         // to their native logging system.
  330.                         //
  331.                         final StringBuilder msg = new StringBuilder();
  332.                         msg.append("The application has specified that a custom LogFactory implementation should be used but Class '");
  333.                         msg.append(factoryClassName);
  334.                         msg.append("' cannot be converted to '");
  335.                         msg.append(LogFactory.class.getName());
  336.                         msg.append("'. ");
  337.                         if (implementsLogFactory) {
  338.                             msg.append("The conflict is caused by the presence of multiple LogFactory classes in incompatible class loaders. Background can");
  339.                             msg.append(" be found in https://commons.apache.org/logging/tech.html. If you have not explicitly specified a custom LogFactory");
  340.                             msg.append(" then it is likely that the container has set one without your knowledge. In this case, consider using the ");
  341.                             msg.append("commons-logging-adapters.jar file or specifying the standard LogFactory from the command line. ");
  342.                         } else {
  343.                             msg.append("Please check the custom implementation. ");
  344.                         }
  345.                         msg.append("Help can be found at https://commons.apache.org/logging/troubleshooting.html.");
  346.                         logDiagnostic(msg.toString());
  347.                         throw new ClassCastException(msg.toString());
  348.                     }
  349.                     // Ignore exception, continue. Presumably the class loader was the
  350.                     // TCCL; the code below will try to load the class via thisClassLoaderRef.
  351.                     // This will handle the case where the original calling class is in
  352.                     // a shared classpath but the TCCL has a copy of LogFactory and the
  353.                     // specified LogFactory implementation; we will fall back to using the
  354.                     // LogFactory implementation from the same class loader as this class.
  355.                     //
  356.                     // Issue: this doesn't handle the reverse case, where this LogFactory
  357.                     // is in the webapp, and the specified LogFactory implementation is
  358.                     // in a shared classpath. In that case:
  359.                     // (a) the class really does implement LogFactory (bad log msg above)
  360.                     // (b) the fallback code will result in exactly the same problem.
  361.                 }
  362.             }

  363.             /*
  364.              * At this point, either classLoader == null, OR classLoader was unable to load factoryClass.
  365.              *
  366.              * In either case, we call Class.forName, which is equivalent to LogFactory.class.getClassLoader().load(name), that is, we ignore the class loader
  367.              * parameter the caller passed, and fall back to trying the class loader associated with this class. See the Javadoc for the newFactory method for
  368.              * more info on the consequences of this.
  369.              *
  370.              * Notes: * LogFactory.class.getClassLoader() may return 'null' if LogFactory is loaded by the bootstrap class loader.
  371.              */
  372.             // Warning: must typecast here & allow exception
  373.             // to be generated/caught & recast properly.
  374.             if (isDiagnosticsEnabled()) {
  375.                 logDiagnostic(
  376.                         "Unable to load factory class via class loader " + objectId(classLoader) + " - trying the class loader associated with this LogFactory.");
  377.             }
  378.             logFactoryClass = Class.forName(factoryClassName);
  379.             // Force a ClassCastException
  380.             return LogFactory.class.cast(logFactoryClass.getConstructor().newInstance());
  381.         } catch (final Exception e) {
  382.             // Check to see if we've got a bad configuration
  383.             if (isDiagnosticsEnabled()) {
  384.                 logDiagnostic("Unable to create LogFactory instance.");
  385.             }
  386.             if (logFactoryClass != null && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
  387.                 return new LogConfigurationException("The chosen LogFactory implementation does not extend LogFactory. Please check your configuration.", e);
  388.             }
  389.             return new LogConfigurationException(e);
  390.         }
  391.     }

  392.     /**
  393.      * Creates the hash table which will be used to store a map of
  394.      * (context class loader -> logfactory-object). Version 1.2+ of Java
  395.      * supports "weak references", allowing a custom Hashtable class
  396.      * to be used which uses only weak references to its keys. Using weak
  397.      * references can fix memory leaks on webapp unload in some cases (though
  398.      * not all). Version 1.1 of Java does not support weak references, so we
  399.      * must dynamically determine which we are using. And just for fun, this
  400.      * code also supports the ability for a system property to specify an
  401.      * arbitrary Hashtable implementation name.
  402.      * <p>
  403.      * Note that the correct way to ensure no memory leaks occur is to ensure
  404.      * that LogFactory.release(contextClassLoader) is called whenever a
  405.      * webapp is undeployed.
  406.      * </p>
  407.      */
  408.     private static Hashtable<ClassLoader, LogFactory> createFactoryStore() {
  409.         Hashtable<ClassLoader, LogFactory> result = null;
  410.         String storeImplementationClass;
  411.         try {
  412.             storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);
  413.         } catch (final SecurityException ex) {
  414.             // Permissions don't allow this to be accessed. Default to the "modern"
  415.             // weak hash table implementation if it is available.
  416.             storeImplementationClass = null;
  417.         }
  418.         if (storeImplementationClass == null) {
  419.             storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
  420.         }
  421.         try {
  422.             final Class<Hashtable<ClassLoader, LogFactory>> implementationClass = (Class<Hashtable<ClassLoader, LogFactory>>) Class
  423.                     .forName(storeImplementationClass);
  424.             result = implementationClass.getConstructor().newInstance();
  425.         } catch (final Throwable t) {
  426.             handleThrowable(t); // may re-throw t
  427.             // ignore
  428.             if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
  429.                 // if the user's trying to set up a custom implementation, give a clue
  430.                 if (isDiagnosticsEnabled()) {
  431.                     // use internal logging to issue the warning
  432.                     logDiagnostic("[ERROR] LogFactory: Load of custom Hashtable failed");
  433.                 } else {
  434.                     // we *really* want this output, even if diagnostics weren't
  435.                     // explicitly enabled by the user.
  436.                     System.err.println("[ERROR] LogFactory: Load of custom Hashtable failed");
  437.                 }
  438.             }
  439.         }
  440.         if (result == null) {
  441.             result = new Hashtable<>();
  442.         }
  443.         return result;
  444.     }

  445.     /**
  446.      * Gets the thread context class loader if available; otherwise return null.
  447.      * <p>
  448.      * Most/all code should call getContextClassLoaderInternal rather than
  449.      * calling this method directly.
  450.      * </p>
  451.      * <p>
  452.      * The thread context class loader is available for JDK 1.2
  453.      * or later, if certain security conditions are met.
  454.      * </p>
  455.      * <p>
  456.      * Note that no internal logging is done within this method because
  457.      * this method is called every time LogFactory.getLogger() is called,
  458.      * and we don't want too much output generated here.
  459.      * </p>
  460.      *
  461.      * @throws LogConfigurationException if a suitable class loader
  462.      *  cannot be identified.
  463.      * @return the thread's context class loader or {@code null} if the Java security
  464.      *  policy forbids access to the context class loader from one of the classes
  465.      *  in the current call stack.
  466.      * @since 1.1
  467.      */
  468.     protected static ClassLoader directGetContextClassLoader() throws LogConfigurationException {
  469.         ClassLoader classLoader = null;
  470.         try {
  471.             classLoader = Thread.currentThread().getContextClassLoader();
  472.         } catch (final SecurityException ignore) {
  473.             // getContextClassLoader() throws SecurityException when
  474.             // the context class loader isn't an ancestor of the
  475.             // calling class's class loader, or if security
  476.             // permissions are restricted.
  477.             //
  478.             // We ignore this exception to be consistent with the previous
  479.             // behavior (e.g. 1.1.3 and earlier).
  480.         }
  481.         // Return the selected class loader
  482.         return classLoader;
  483.     }

  484.     /**
  485.      * Gets a cached log factory (keyed by contextClassLoader)
  486.      *
  487.      * @param contextClassLoader is the context class loader associated
  488.      * with the current thread. This allows separate LogFactory objects
  489.      * per component within a container, provided each component has
  490.      * a distinct context class loader set. This parameter may be null
  491.      * in JDK1.1, and in embedded systems where jcl-using code is
  492.      * placed in the bootclasspath.
  493.      *
  494.      * @return the factory associated with the specified class loader if
  495.      *  one has previously been created, or null if this is the first time
  496.      *  we have seen this particular class loader.
  497.      */
  498.     private static LogFactory getCachedFactory(final ClassLoader contextClassLoader) {
  499.         if (contextClassLoader == null) {
  500.             // We have to handle this specially, as factories is a Hashtable
  501.             // and those don't accept null as a key value.
  502.             //
  503.             // nb: nullClassLoaderFactory might be null. That's ok.
  504.             return nullClassLoaderFactory;
  505.         }
  506.         return factories.get(contextClassLoader);
  507.     }

  508.     /**
  509.      * Safely get access to the class loader for the specified class.
  510.      * <p>
  511.      * Theoretically, calling getClassLoader can throw a security exception,
  512.      * and so should be done under an AccessController in order to provide
  513.      * maximum flexibility. However in practice people don't appear to use
  514.      * security policies that forbid getClassLoader calls. So for the moment
  515.      * all code is written to call this method rather than Class.getClassLoader,
  516.      * so that we could put AccessController stuff in this method without any
  517.      * disruption later if we need to.
  518.      * </p>
  519.      * <p>
  520.      * Even when using an AccessController, however, this method can still
  521.      * throw SecurityException. Commons Logging basically relies on the
  522.      * ability to access class loaders. A policy that forbids all
  523.      * class loader access will also prevent commons-logging from working:
  524.      * currently this method will throw an exception preventing the entire app
  525.      * from starting up. Maybe it would be good to detect this situation and
  526.      * just disable all commons-logging? Not high priority though - as stated
  527.      * above, security policies that prevent class loader access aren't common.
  528.      * </p>
  529.      * <p>
  530.      * Note that returning an object fetched via an AccessController would
  531.      * technically be a security flaw anyway; untrusted code that has access
  532.      * to a trusted JCL library could use it to fetch the class loader for
  533.      * a class even when forbidden to do so directly.
  534.      * </p>
  535.      *
  536.      * @param clazz Class.
  537.      * @return a ClassLoader.
  538.      * @since 1.1
  539.      */
  540.     protected static ClassLoader getClassLoader(final Class<?> clazz) {
  541.         try {
  542.             return clazz.getClassLoader();
  543.         } catch (final SecurityException ex) {
  544.             logDiagnostic(() -> "Unable to get class loader for class '" + clazz + "' due to security restrictions - " + ex.getMessage());
  545.             throw ex;
  546.         }
  547.     }

  548.     /**
  549.      * Gets a user-provided configuration file.
  550.      * <p>
  551.      * The classpath of the specified classLoader (usually the context class loader)
  552.      * is searched for properties files of the specified name. If none is found,
  553.      * null is returned. If more than one is found, then the file with the greatest
  554.      * value for its PRIORITY property is returned. If multiple files have the
  555.      * same PRIORITY value then the first in the classpath is returned.
  556.      * </p>
  557.      * <p>
  558.      * This differs from the 1.0.x releases; those always use the first one found.
  559.      * However as the priority is a new field, this change is backwards compatible.
  560.      * </p>
  561.      * <p>
  562.      * The purpose of the priority field is to allow a webserver administrator to
  563.      * override logging settings in all webapps by placing a commons-logging.properties
  564.      * file in a shared classpath location with a priority > 0; this overrides any
  565.      * commons-logging.properties files without priorities which are in the
  566.      * webapps. Webapps can also use explicit priorities to override a configuration
  567.      * file in the shared classpath if needed.
  568.      * </p>
  569.      */
  570.     private static Properties getConfigurationFile(final ClassLoader classLoader, final String fileName) {
  571.         Properties props = null;
  572.         double priority = 0.0;
  573.         URL propsUrl = null;
  574.         try {
  575.             final Enumeration<URL> urls = getResources(classLoader, fileName);
  576.             if (urls == null) {
  577.                 return null;
  578.             }
  579.             while (urls.hasMoreElements()) {
  580.                 final URL url = urls.nextElement();
  581.                 final Properties newProps = getProperties(url);
  582.                 if (newProps != null) {
  583.                     if (props == null) {
  584.                         propsUrl = url;
  585.                         props = newProps;
  586.                         final String priorityStr = props.getProperty(PRIORITY_KEY);
  587.                         priority = 0.0;
  588.                         if (priorityStr != null) {
  589.                             priority = Double.parseDouble(priorityStr);
  590.                         }
  591.                         if (isDiagnosticsEnabled()) {
  592.                             logDiagnostic("[LOOKUP] Properties file found at '" + url + "'" + " with priority " + priority);
  593.                         }
  594.                     } else {
  595.                         final String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
  596.                         double newPriority = 0.0;
  597.                         if (newPriorityStr != null) {
  598.                             newPriority = Double.parseDouble(newPriorityStr);
  599.                         }
  600.                         if (newPriority > priority) {
  601.                             if (isDiagnosticsEnabled()) {
  602.                                 logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + " with priority " + newPriority + " overrides file at '" + propsUrl
  603.                                         + "'" + " with priority " + priority);
  604.                             }
  605.                             propsUrl = url;
  606.                             props = newProps;
  607.                             priority = newPriority;
  608.                         } else if (isDiagnosticsEnabled()) {
  609.                             logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + " with priority " + newPriority + " does not override file at '"
  610.                                     + propsUrl + "'" + " with priority " + priority);
  611.                         }
  612.                     }

  613.                 }
  614.             }
  615.         } catch (final SecurityException e) {
  616.             logDiagnostic("SecurityException thrown while trying to find/read config files.");
  617.         }
  618.         if (isDiagnosticsEnabled()) {
  619.             if (props == null) {
  620.                 logDiagnostic("[LOOKUP] No properties file of name '" + fileName + "' found.");
  621.             } else {
  622.                 logDiagnostic("[LOOKUP] Properties file of name '" + fileName + "' found at '" + propsUrl + '"');
  623.             }
  624.         }
  625.         return props;
  626.     }

  627.     /**
  628.      * Gets the current context class loader.
  629.      * <p>
  630.      * In versions prior to 1.1, this method did not use an AccessController.
  631.      * In version 1.1, an AccessController wrapper was incorrectly added to
  632.      * this method, causing a minor security flaw.
  633.      * </p>
  634.      * <p>
  635.      * In version 1.1.1 this change was reverted; this method no longer uses
  636.      * an AccessController. User code wishing to obtain the context class loader
  637.      * must invoke this method via AccessController.doPrivileged if it needs
  638.      * support for that.
  639.      * </p>
  640.      *
  641.      * @return the context class loader associated with the current thread,
  642.      *  or null if security doesn't allow it.
  643.      * @throws LogConfigurationException if there was some weird error while
  644.      *  attempting to get the context class loader.
  645.      */
  646.     protected static ClassLoader getContextClassLoader() throws LogConfigurationException {
  647.         return directGetContextClassLoader();
  648.     }

  649.     /**
  650.      * Calls {@link LogFactory#directGetContextClassLoader()} under the control of an
  651.      * AccessController class. This means that Java code running under a
  652.      * security manager that forbids access to ClassLoaders will still work
  653.      * if this class is given appropriate privileges, even when the caller
  654.      * doesn't have such privileges. Without using an AccessController, the
  655.      * the entire call stack must have the privilege before the call is
  656.      * allowed.
  657.      *
  658.      * @return the context class loader associated with the current thread,
  659.      *  or null if security doesn't allow it.
  660.      * @throws LogConfigurationException if there was some weird error while
  661.      *  attempting to get the context class loader.
  662.      */
  663.     private static ClassLoader getContextClassLoaderInternal() throws LogConfigurationException {
  664.         return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) LogFactory::directGetContextClassLoader);
  665.     }

  666.     /**
  667.      * Constructs (if necessary) and return a {@code LogFactory} instance, using the following ordered lookup procedure to determine the name of the
  668.      * implementation class to be loaded.
  669.      * <ul>
  670.      * <li>The {@code org.apache.commons.logging.LogFactory} system property.</li>
  671.      * <li>The JDK 1.3 Service Discovery mechanism</li>
  672.      * <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
  673.      * {@link java.util.Properties} format and contains the fully qualified name of the implementation class with the key being the system property defined
  674.      * above.</li>
  675.      * <li>Fall back to a default implementation class ({@code org.apache.commons.logging.impl.LogFactoryImpl}).</li>
  676.      * </ul>
  677.      * <p>
  678.      * <em>NOTE</em> - If the properties file method of identifying the {@code LogFactory} implementation class is utilized, all of the properties defined in
  679.      * this file will be set as configuration attributes on the corresponding {@code LogFactory} instance.
  680.      * </p>
  681.      * <p>
  682.      * <em>NOTE</em> - In a multi-threaded environment it is possible that two different instances will be returned for the same class loader environment.
  683.      * </p>
  684.      *
  685.      * @return a {@code LogFactory}.
  686.      * @throws LogConfigurationException if the implementation class is not available or cannot be instantiated.
  687.      */
  688.     public static LogFactory getFactory() throws LogConfigurationException {
  689.         // Identify the class loader we will be using
  690.         final ClassLoader contextClassLoader = getContextClassLoaderInternal();

  691.         // This is an odd enough situation to report about. This
  692.         // output will be a nuisance on JDK1.1, as the system
  693.         // class loader is null in that environment.
  694.         if (contextClassLoader == null) {
  695.             logDiagnostic("Context class loader is null.");
  696.         }

  697.         // Return any previously registered factory for this class loader
  698.         LogFactory factory = getCachedFactory(contextClassLoader);
  699.         if (factory != null) {
  700.             return factory;
  701.         }

  702.         if (isDiagnosticsEnabled()) {
  703.             logDiagnostic(
  704.                     "[LOOKUP] LogFactory implementation requested for the first time for context class loader " +
  705.                     objectId(contextClassLoader));
  706.             logHierarchy("[LOOKUP] ", contextClassLoader);
  707.         }

  708.         // Load properties file.
  709.         //
  710.         // If the properties file exists, then its contents are used as
  711.         // "attributes" on the LogFactory implementation class. One particular
  712.         // property may also control which LogFactory concrete subclass is
  713.         // used, but only if other discovery mechanisms fail.
  714.         //
  715.         // As the properties file (if it exists) will be used one way or
  716.         // another in the end we may as well look for it first.

  717.         final Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);

  718.         // Determine whether we will be using the thread context class loader to
  719.         // load logging classes or not by checking the loaded properties file (if any).
  720.         boolean useTccl = contextClassLoader != null;
  721.         if (props != null) {
  722.             final String useTCCLStr = props.getProperty(TCCL_KEY);
  723.             useTccl &= useTCCLStr == null || Boolean.parseBoolean(useTCCLStr);
  724.         }
  725.         // If TCCL is still enabled at this point, we check if it resolves this class
  726.         if (useTccl) {
  727.             try {
  728.                 if (!LogFactory.class.equals(Class.forName(LogFactory.class.getName(), false, contextClassLoader))) {
  729.                     logDiagnostic(() -> "The class " + LogFactory.class.getName() + " loaded by the context class loader " + objectId(contextClassLoader)
  730.                             + " and this class differ. Disabling the usage of the context class loader."
  731.                             + "Background can be found in https://commons.apache.org/logging/tech.html. ");
  732.                     logHierarchy("[BAD CL TREE] ", contextClassLoader);
  733.                     useTccl = false;
  734.                 }
  735.             } catch (final ClassNotFoundException ignored) {
  736.                 logDiagnostic(() -> "The class " + LogFactory.class.getName() + " is not present in the the context class loader "
  737.                         + objectId(contextClassLoader) + ". Disabling the usage of the context class loader."
  738.                         + "Background can be found in https://commons.apache.org/logging/tech.html. ");
  739.                 logHierarchy("[BAD CL TREE] ", contextClassLoader);
  740.                 useTccl = false;
  741.             }
  742.         }
  743.         final ClassLoader baseClassLoader = useTccl ? contextClassLoader : thisClassLoaderRef.get();

  744.         // Determine which concrete LogFactory subclass to use.
  745.         // First, try a global system property
  746.         logDiagnostic(() -> "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
  747.                       "] to define the LogFactory subclass to use...");

  748.         try {
  749.             final String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
  750.             if (factoryClass != null) {
  751.                 logDiagnostic(() -> "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +
  752.                               "' as specified by system property " + FACTORY_PROPERTY);
  753.                 factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
  754.             } else {
  755.                 logDiagnostic(() -> "[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");
  756.             }
  757.         } catch (final SecurityException e) {
  758.             logDiagnostic(() -> "[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class" + ": ["
  759.                     + trim(e.getMessage()) + "]. Trying alternative implementations...");
  760.             // ignore
  761.         } catch (final RuntimeException e) {
  762.             // This is not consistent with the behavior when a bad LogFactory class is
  763.             // specified in a services file.
  764.             //
  765.             // One possible exception that can occur here is a ClassCastException when
  766.             // the specified class wasn't castable to this LogFactory type.
  767.             logDiagnostic(() -> "[LOOKUP] An exception occurred while trying to create an instance of the custom factory class: [" + trim(e.getMessage())
  768.                     + "] as specified by a system property.");
  769.             throw e;
  770.         }
  771.         //
  772.         // Second, try to find a service by using the JDK 1.3 class
  773.         // discovery mechanism, which involves putting a file with the name
  774.         // of an interface class in the META-INF/services directory, where the
  775.         // contents of the file is a single line specifying a concrete class
  776.         // that implements the desired interface.
  777.         if (factory == null) {
  778.             logDiagnostic("[LOOKUP] Using ServiceLoader  to define the LogFactory subclass to use...");
  779.             try {
  780.                 final ServiceLoader<LogFactory> serviceLoader = ServiceLoader.load(LogFactory.class, baseClassLoader);
  781.                 final Iterator<LogFactory> iterator = serviceLoader.iterator();

  782.                 int i = MAX_BROKEN_SERVICES;
  783.                 while (factory == null && i-- > 0) {
  784.                     try {
  785.                         if (iterator.hasNext()) {
  786.                             factory = iterator.next();
  787.                         }
  788.                     } catch (final ServiceConfigurationError | LinkageError ex) {
  789.                         logDiagnostic(() -> "[LOOKUP] An exception occurred while trying to find an instance of LogFactory: [" + trim(ex.getMessage())
  790.                                 + "]. Trying alternative implementations...");
  791.                     }
  792.                 }
  793.             } catch (final Exception ex) {
  794.                 // note: if the specified LogFactory class wasn't compatible with LogFactory
  795.                 // for some reason, a ClassCastException will be caught here, and attempts will
  796.                 // continue to find a compatible class.
  797.                 logDiagnostic(() -> "[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class: ["
  798.                         + trim(ex.getMessage()) + "]. Trying alternative implementations...");
  799.                 // ignore
  800.             }
  801.         }
  802.         //
  803.         // Third try looking into the properties file read earlier (if found)
  804.         if (factory == null) {
  805.             if (props != null) {
  806.                 logDiagnostic(() ->
  807.                     "[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +
  808.                     "' to define the LogFactory subclass to use...");
  809.                 final String factoryClass = props.getProperty(FACTORY_PROPERTY);
  810.                 if (factoryClass != null) {
  811.                     logDiagnostic(() ->
  812.                         "[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
  813.                     factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
  814.                     // TODO: think about whether we need to handle exceptions from newFactory
  815.                 } else {
  816.                     logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
  817.                 }
  818.             } else {
  819.                 logDiagnostic("[LOOKUP] No properties file available to determine LogFactory subclass from..");
  820.             }
  821.         }
  822.         //
  823.         // Fourth, try one of the three provided factories first from the specified classloader
  824.         // and then from the current one.
  825.         if (factory == null) {
  826.             factory = newStandardFactory(baseClassLoader);
  827.         }
  828.         if (factory == null && baseClassLoader != thisClassLoaderRef.get()) {
  829.             factory = newStandardFactory(thisClassLoaderRef.get());
  830.         }
  831.         if (factory != null) {
  832.             if (isDiagnosticsEnabled()) {
  833.                 logDiagnostic("Created object " + objectId(factory) + " to manage class loader " + objectId(contextClassLoader));
  834.             }
  835.         } else {
  836.             logDiagnostic(() ->
  837.                 "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +
  838.                 "' via the same class loader that loaded this LogFactory class (ie not looking in the context class loader).");
  839.             // Note: unlike the above code which can try to load custom LogFactory
  840.             // implementations via the TCCL, we don't try to load the default LogFactory
  841.             // implementation via the context class loader because:
  842.             // * that can cause problems (see comments in newFactory method)
  843.             // * no-one should be customizing the code of the default class
  844.             // Yes, we do give up the ability for the child to ship a newer
  845.             // version of the LogFactoryImpl class and have it used dynamically
  846.             // by an old LogFactory class in the parent, but that isn't
  847.             // necessarily a good idea anyway.
  848.             factory = newFactory(FACTORY_DEFAULT, thisClassLoaderRef.get(), contextClassLoader);
  849.         }
  850.         if (factory != null) {
  851.             /**
  852.              * Always cache using context class loader.
  853.              */
  854.             cacheFactory(contextClassLoader, factory);
  855.             if (props != null) {
  856.                 final Enumeration<?> names = props.propertyNames();
  857.                 while (names.hasMoreElements()) {
  858.                     final String name = Objects.toString(names.nextElement(), null);
  859.                     final String value = props.getProperty(name);
  860.                     factory.setAttribute(name, value);
  861.                 }
  862.             }
  863.         }
  864.         return factory;
  865.     }

  866.     /**
  867.      * Gets a named logger, without the application having to care about factories.
  868.      *
  869.      * @param clazz Class from which a log name will be derived
  870.      * @return a named logger.
  871.      * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned
  872.      */
  873.     public static Log getLog(final Class<?> clazz) throws LogConfigurationException {
  874.         return getFactory().getInstance(clazz);
  875.     }

  876.     /**
  877.      * Gets a named logger, without the application having to care about factories.
  878.      *
  879.      * @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
  880.      *             is being wrapped)
  881.      * @return a named logger.
  882.      * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned
  883.      */
  884.     public static Log getLog(final String name) throws LogConfigurationException {
  885.         return getFactory().getInstance(name);
  886.     }

  887.     /**
  888.      * Given a URL that refers to a .properties file, load that file.
  889.      * This is done under an AccessController so that this method will
  890.      * succeed when this jarfile is privileged but the caller is not.
  891.      * This method must therefore remain private to avoid security issues.
  892.      * <p>
  893.      * {@code Null} is returned if the URL cannot be opened.
  894.      * </p>
  895.      */
  896.     private static Properties getProperties(final URL url) {
  897.         return AccessController.doPrivileged((PrivilegedAction<Properties>) () -> {
  898.             // We must ensure that useCaches is set to false, as the
  899.             // default behavior of java is to cache file handles, and
  900.             // this "locks" files, preventing hot-redeploy on windows.
  901.             try {
  902.                 final URLConnection connection = url.openConnection();
  903.                 connection.setUseCaches(false);
  904.                 try (InputStream stream = connection.getInputStream()) {
  905.                     if (stream != null) {
  906.                         final Properties props = new Properties();
  907.                         props.load(stream);
  908.                         return props;
  909.                     }
  910.                 } catch (final IOException e) {
  911.                     logDiagnostic(() -> "Unable to close stream for URL " + url);
  912.                 }
  913.             } catch (final IOException e) {
  914.                 logDiagnostic(() -> "Unable to read URL " + url);
  915.             }

  916.             return null;
  917.         });
  918.     }

  919.     /**
  920.      * Given a file name, return an enumeration of URLs pointing to
  921.      * all the occurrences of that file name in the classpath.
  922.      * <p>
  923.      * This is just like ClassLoader.getResources except that the
  924.      * operation is done under an AccessController so that this method will
  925.      * succeed when this jarfile is privileged but the caller is not.
  926.      * This method must therefore remain private to avoid security issues.
  927.      * </p>
  928.      * <p>
  929.      * If no instances are found, an Enumeration is returned whose
  930.      * hasMoreElements method returns false (ie an "empty" enumeration).
  931.      * If resources could not be listed for some reason, null is returned.
  932.      * </p>
  933.      */
  934.     private static Enumeration<URL> getResources(final ClassLoader loader, final String name) {
  935.         return AccessController.doPrivileged((PrivilegedAction<Enumeration<URL>>) () -> {
  936.             try {
  937.                 if (loader != null) {
  938.                     return loader.getResources(name);
  939.                 }
  940.                 return ClassLoader.getSystemResources(name);
  941.             } catch (final IOException e) {
  942.                 logDiagnostic(() -> "Exception while trying to find configuration file " + name + ":" + e.getMessage());
  943.                 return null;
  944.             } catch (final NoSuchMethodError e) {
  945.                 // we must be running on a 1.1 JVM which doesn't support
  946.                 // ClassLoader.getSystemResources; just return null in
  947.                 // this case.
  948.                 return null;
  949.             }
  950.         });
  951.     }

  952.     /**
  953.      * Read the specified system property, using an AccessController so that
  954.      * the property can be read if JCL has been granted the appropriate
  955.      * security rights even if the calling code has not.
  956.      * <p>
  957.      * Take care not to expose the value returned by this method to the
  958.      * calling application in any way; otherwise the calling app can use that
  959.      * info to access data that should not be available to it.
  960.      * </p>
  961.      */
  962.     private static String getSystemProperty(final String key, final String def)
  963.             throws SecurityException {
  964.         return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(key, def));
  965.     }

  966.     /**
  967.      * Checks whether the supplied Throwable is one that needs to be
  968.      * re-thrown and ignores all others.
  969.      *
  970.      * The following errors are re-thrown:
  971.      * <ul>
  972.      *   <li>ThreadDeath</li>
  973.      *   <li>VirtualMachineError</li>
  974.      * </ul>
  975.      *
  976.      * @param t the Throwable to check
  977.      */
  978.     protected static void handleThrowable(final Throwable t) {
  979.         if (t instanceof ThreadDeath) {
  980.             throw (ThreadDeath) t;
  981.         }
  982.         if (t instanceof VirtualMachineError) {
  983.             throw (VirtualMachineError) t;
  984.         }
  985.         // All other instances of Throwable will be silently ignored
  986.     }

  987.     /**
  988.      * Determines whether the given class actually implements {@code LogFactory}.
  989.      * Diagnostic information is also logged.
  990.      * <p>
  991.      * <strong>Usage:</strong> to diagnose whether a class loader conflict is the cause
  992.      * of incompatibility. The test used is whether the class is assignable from
  993.      * the {@code LogFactory} class loaded by the class's class loader.
  994.      * @param logFactoryClass {@code Class} which may implement {@code LogFactory}
  995.      * @return true if the {@code logFactoryClass} does extend
  996.      * {@code LogFactory} when that class is loaded via the same
  997.      * class loader that loaded the {@code logFactoryClass}.
  998.      * </p>
  999.      */
  1000.     private static boolean implementsLogFactory(final Class<?> logFactoryClass) {
  1001.         boolean implementsLogFactory = false;
  1002.         if (logFactoryClass != null) {
  1003.             try {
  1004.                 final ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
  1005.                 if (logFactoryClassLoader == null) {
  1006.                     logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot class loader");
  1007.                 } else {
  1008.                     logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
  1009.                     final Class<?> factoryFromCustomLoader = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
  1010.                     implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
  1011.                     final String logFactoryClassName = logFactoryClass.getName();
  1012.                     if (implementsLogFactory) {
  1013.                         logDiagnostic(() -> "[CUSTOM LOG FACTORY] " + logFactoryClassName + " implements LogFactory but was loaded by an incompatible class loader.");
  1014.                     } else {
  1015.                         logDiagnostic(() -> "[CUSTOM LOG FACTORY] " + logFactoryClassName + " does not implement LogFactory.");
  1016.                     }
  1017.                 }
  1018.             } catch (final SecurityException e) {
  1019.                 //
  1020.                 // The application is running within a hostile security environment.
  1021.                 // This will make it very hard to diagnose issues with JCL.
  1022.                 // Consider running less securely whilst debugging this issue.
  1023.                 //
  1024.                 logDiagnostic(
  1025.                         () -> "[CUSTOM LOG FACTORY] SecurityException caught trying to determine whether the compatibility was caused by a class loader conflict: "
  1026.                                 + e.getMessage());
  1027.             } catch (final LinkageError e) {
  1028.                 //
  1029.                 // This should be an unusual circumstance.
  1030.                 // LinkageError's usually indicate that a dependent class has incompatibly changed.
  1031.                 // Another possibility may be an exception thrown by an initializer.
  1032.                 // Time for a clean rebuild?
  1033.                 //
  1034.                 logDiagnostic(
  1035.                         () -> "[CUSTOM LOG FACTORY] LinkageError caught trying to determine whether the compatibility was caused by a class loader conflict: "
  1036.                                 + e.getMessage());
  1037.             } catch (final ClassNotFoundException e) {
  1038.                 //
  1039.                 // LogFactory cannot be loaded by the class loader which loaded the custom factory implementation.
  1040.                 // The custom implementation is not viable until this is corrected.
  1041.                 // Ensure that the JCL jar and the custom class are available from the same class loader.
  1042.                 // Running with diagnostics on should give information about the class loaders used
  1043.                 // to load the custom factory.
  1044.                 //
  1045.                 logDiagnostic(() -> "[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by the class loader which loaded "
  1046.                         + "the custom LogFactory implementation. Is the custom factory in the right class loader?");
  1047.             }
  1048.         }
  1049.         return implementsLogFactory;
  1050.     }

  1051.     /**
  1052.      * Tests whether the user wants internal diagnostic output. If so,
  1053.      * returns an appropriate writer object. Users can enable diagnostic
  1054.      * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
  1055.      * a file name, or the special values STDOUT or STDERR.
  1056.      */
  1057.     private static PrintStream initDiagnostics() {
  1058.         String dest;
  1059.         try {
  1060.             dest = getSystemProperty(DIAGNOSTICS_DEST_PROPERTY, null);
  1061.             if (dest == null) {
  1062.                 return null;
  1063.             }
  1064.         } catch (final SecurityException ex) {
  1065.             // We must be running in some very secure environment.
  1066.             // We just have to assume output is not wanted.
  1067.             return null;
  1068.         }

  1069.         if (dest.equals("STDOUT")) {
  1070.             return System.out;
  1071.         }
  1072.         if (dest.equals("STDERR")) {
  1073.             return System.err;
  1074.         }
  1075.         try {
  1076.             // open the file in append mode
  1077.             final FileOutputStream fos = new FileOutputStream(dest, true);
  1078.             return new PrintStream(fos, false, StandardCharsets.UTF_8.name());
  1079.         } catch (final IOException ex) {
  1080.             // We should report this to the user - but how?
  1081.             return null;
  1082.         }
  1083.     }

  1084.     private static boolean isClassAvailable(final String className, final ClassLoader classLoader) {
  1085.         logDiagnostic(() -> "Checking if class '" + className + "' is available in class loader " + objectId(classLoader));
  1086.         try {
  1087.             Class.forName(className, true, classLoader);
  1088.             return true;
  1089.         } catch (final ClassNotFoundException | LinkageError e) {
  1090.             logDiagnostic(() -> "Failed to load class '" + className + "' from class loader " + objectId(classLoader) + ": " + e.getMessage());
  1091.         }
  1092.         return false;
  1093.     }

  1094.     /**
  1095.      * Tests whether the user enabled internal logging.
  1096.      * <p>
  1097.      * By the way, sorry for the incorrect grammar, but calling this method
  1098.      * areDiagnosticsEnabled just isn't Java beans style.
  1099.      * </p>
  1100.      *
  1101.      * @return true if calls to logDiagnostic will have any effect.
  1102.      * @since 1.1
  1103.      */
  1104.     protected static boolean isDiagnosticsEnabled() {
  1105.         return DIAGNOSTICS_STREAM != null;
  1106.     }

  1107.     /**
  1108.      * Generates useful diagnostics regarding the class loader tree for
  1109.      * the specified class.
  1110.      * <p>
  1111.      * As an example, if the specified class was loaded via a webapp's
  1112.      * class loader, then you may get the following output:
  1113.      * </p>
  1114.      * <pre>
  1115.      * Class com.acme.Foo was loaded via class loader 11111
  1116.      * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT
  1117.      * </pre>
  1118.      * <p>
  1119.      * This method returns immediately if isDiagnosticsEnabled()
  1120.      * returns false.
  1121.      * </p>
  1122.      *
  1123.      * @param clazz is the class whose class loader + tree are to be
  1124.      * output.
  1125.      */
  1126.     private static void logClassLoaderEnvironment(final Class<?> clazz) {
  1127.         if (!isDiagnosticsEnabled()) {
  1128.             return;
  1129.         }
  1130.         try {
  1131.             // Deliberately use System.getProperty here instead of getSystemProperty; if
  1132.             // the overall security policy for the calling application forbids access to
  1133.             // these variables then we do not want to output them to the diagnostic stream.
  1134.             logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
  1135.             logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
  1136.         } catch (final SecurityException ex) {
  1137.             logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
  1138.         }
  1139.         final String className = clazz.getName();
  1140.         ClassLoader classLoader;
  1141.         try {
  1142.             classLoader = getClassLoader(clazz);
  1143.         } catch (final SecurityException ex) {
  1144.             // not much useful diagnostics we can print here!
  1145.             logDiagnostic("[ENV] Security forbids determining the class loader for " + className);
  1146.             return;
  1147.         }
  1148.         logDiagnostic("[ENV] Class " + className + " was loaded via class loader " + objectId(classLoader));
  1149.         logHierarchy("[ENV] Ancestry of class loader which loaded " + className + " is ", classLoader);
  1150.     }

  1151.     /**
  1152.      * Writes the specified message to the internal logging destination.
  1153.      * <p>
  1154.      * Note that this method is private; concrete subclasses of this class
  1155.      * should not call it because the diagnosticPrefix string this
  1156.      * method puts in front of all its messages is LogFactory@....,
  1157.      * while subclasses should put SomeSubClass@...
  1158.      * </p>
  1159.      * <p>
  1160.      * Subclasses should instead compute their own prefix, then call
  1161.      * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
  1162.      * fine for subclasses.
  1163.      * </p>
  1164.      * <p>
  1165.      * Note that it is safe to call this method before initDiagnostics
  1166.      * is called; any output will just be ignored (as isDiagnosticsEnabled
  1167.      * will return false).
  1168.      * </p>
  1169.      *
  1170.      * @param msg is the diagnostic message to be output.
  1171.      */
  1172.     private static void logDiagnostic(final String msg) {
  1173.         if (DIAGNOSTICS_STREAM != null) {
  1174.             logDiagnosticDirect(msg);
  1175.         }
  1176.     }

  1177.     /**
  1178.      * Writes the specified message to the internal logging destination.
  1179.      * <p>
  1180.      * Note that this method is private; concrete subclasses of this class
  1181.      * should not call it because the diagnosticPrefix string this
  1182.      * method puts in front of all its messages is LogFactory@....,
  1183.      * while subclasses should put SomeSubClass@...
  1184.      * </p>
  1185.      * <p>
  1186.      * Subclasses should instead compute their own prefix, then call
  1187.      * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
  1188.      * fine for subclasses.
  1189.      * </p>
  1190.      * <p>
  1191.      * Note that it is safe to call this method before initDiagnostics
  1192.      * is called; any output will just be ignored (as isDiagnosticsEnabled
  1193.      * will return false).
  1194.      * </p>
  1195.      *
  1196.      * @param msg is the diagnostic message to be output.
  1197.      */
  1198.     private static void logDiagnostic(final Supplier<String> msg) {
  1199.         if (DIAGNOSTICS_STREAM != null) {
  1200.             logDiagnosticDirect(msg.get());
  1201.         }
  1202.     }

  1203.     private static void logDiagnosticDirect(final String msg) {
  1204.         DIAGNOSTICS_STREAM.print(DIAGNOSTICS_PREFIX);
  1205.         DIAGNOSTICS_STREAM.println(msg);
  1206.         DIAGNOSTICS_STREAM.flush();
  1207.     }

  1208.     /**
  1209.      * Logs diagnostic messages about the given class loader
  1210.      * and it's hierarchy. The prefix is prepended to the message
  1211.      * and is intended to make it easier to understand the logs.
  1212.      * @param prefix
  1213.      * @param classLoader
  1214.      */
  1215.     private static void logHierarchy(final String prefix, ClassLoader classLoader) {
  1216.         if (!isDiagnosticsEnabled()) {
  1217.             return;
  1218.         }
  1219.         ClassLoader systemClassLoader;
  1220.         if (classLoader != null) {
  1221.             logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoader.toString() + "'");
  1222.         }
  1223.         try {
  1224.             systemClassLoader = ClassLoader.getSystemClassLoader();
  1225.         } catch (final SecurityException ex) {
  1226.             logDiagnostic(prefix + "Security forbids determining the system class loader.");
  1227.             return;
  1228.         }
  1229.         if (classLoader != null) {
  1230.             final StringBuilder buf = new StringBuilder(prefix + "ClassLoader tree:");
  1231.             for(;;) {
  1232.                 buf.append(objectId(classLoader));
  1233.                 if (classLoader == systemClassLoader) {
  1234.                     buf.append(" (SYSTEM) ");
  1235.                 }
  1236.                 try {
  1237.                     classLoader = classLoader.getParent();
  1238.                 } catch (final SecurityException ex) {
  1239.                     buf.append(" --> SECRET");
  1240.                     break;
  1241.                 }
  1242.                 buf.append(" --> ");
  1243.                 if (classLoader == null) {
  1244.                     buf.append("BOOT");
  1245.                     break;
  1246.                 }
  1247.             }
  1248.             logDiagnostic(buf.toString());
  1249.         }
  1250.     }

  1251.     /**
  1252.      * Writes the specified message to the internal logging destination.
  1253.      *
  1254.      * @param msg is the diagnostic message to be output.
  1255.      * @since 1.1
  1256.      */
  1257.     protected static final void logRawDiagnostic(final String msg) {
  1258.         if (DIAGNOSTICS_STREAM != null) {
  1259.             DIAGNOSTICS_STREAM.println(msg);
  1260.             DIAGNOSTICS_STREAM.flush();
  1261.         }
  1262.     }

  1263.     /**
  1264.      * Method provided for backwards compatibility; see newFactory version that
  1265.      * takes 3 parameters.
  1266.      * <p>
  1267.      * This method would only ever be called in some rather odd situation.
  1268.      * Note that this method is static, so overriding in a subclass doesn't
  1269.      * have any effect unless this method is called from a method in that
  1270.      * subclass. However this method only makes sense to use from the
  1271.      * getFactory method, and as that is almost always invoked via
  1272.      * LogFactory.getFactory, any custom definition in a subclass would be
  1273.      * pointless. Only a class with a custom getFactory method, then invoked
  1274.      * directly via CustomFactoryImpl.getFactory or similar would ever call
  1275.      * this. Anyway, it's here just in case, though the "managed class loader"
  1276.      * value output to the diagnostics will not report the correct value.
  1277.      * </p>
  1278.      *
  1279.      * @param factoryClass factory class.
  1280.      * @param classLoader class loader.
  1281.      * @return a LogFactory.
  1282.      */
  1283.     protected static LogFactory newFactory(final String factoryClass,
  1284.                                            final ClassLoader classLoader) {
  1285.         return newFactory(factoryClass, classLoader, null);
  1286.     }

  1287.     /**
  1288.      * Gets a new instance of the specified {@code LogFactory} implementation class, loaded by the specified class loader. If that fails, try the class loader
  1289.      * used to load this (abstract) LogFactory.
  1290.      * <p>
  1291.      * <strong>ClassLoader conflicts</strong>
  1292.      * </p>
  1293.      * <p>
  1294.      * 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
  1295.      * LogFactory subclass via a context class loader.
  1296.      * </p>
  1297.      * <p>
  1298.      * The problem is the same one that can occur when loading a concrete Log subclass via a context class loader.
  1299.      * </p>
  1300.      * <p>
  1301.      * 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
  1302.      * LogFactory.getFactory (either directly or via LogFactory.getLog). Because class X was loaded via the parent, it binds to LogFactory loaded via the
  1303.      * 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
  1304.      * defined in the child class loader, then LogFactoryYYYY will be bound to LogFactory@childloader. It cannot be cast to LogFactory@parentloader, that is,
  1305.      * 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
  1306.      * the LogFactory class in the parent class loader, they are not compatible.
  1307.      * </p>
  1308.      * <p>
  1309.      * 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
  1310.      * 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
  1311.      * 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.
  1312.      * </p>
  1313.      *
  1314.      * @param factoryClass       Fully qualified name of the {@code LogFactory} implementation class
  1315.      * @param classLoader        ClassLoader from which to load this class
  1316.      * @param contextClassLoader is the context that this new factory will manage logging for.
  1317.      * @return a new instance of the specified {@code LogFactory}.
  1318.      * @throws LogConfigurationException if a suitable instance cannot be created
  1319.      * @since 1.1
  1320.      */
  1321.     protected static LogFactory newFactory(final String factoryClass,
  1322.                                            final ClassLoader classLoader,
  1323.                                            final ClassLoader contextClassLoader)
  1324.             throws LogConfigurationException {
  1325.         // Note that any unchecked exceptions thrown by the createFactory
  1326.         // method will propagate out of this method; in particular a
  1327.         // ClassCastException can be thrown.
  1328.         final Object result = AccessController.doPrivileged((PrivilegedAction<?>) () -> createFactory(factoryClass, classLoader));
  1329.         if (result instanceof LogConfigurationException) {
  1330.             final LogConfigurationException ex = (LogConfigurationException) result;
  1331.             logDiagnostic(() -> "An error occurred while loading the factory class:" + ex.getMessage());
  1332.             throw ex;
  1333.         }
  1334.         logDiagnostic(() -> "Created object " + objectId(result) + " to manage class loader " + objectId(contextClassLoader));
  1335.         return (LogFactory) result;
  1336.     }

  1337.     /**
  1338.      * Tries to load one of the standard three implementations from the given classloader.
  1339.      * <p>
  1340.      *     We assume that {@code classLoader} can load this class.
  1341.      * </p>
  1342.      * @param classLoader The classloader to use.
  1343.      * @return An implementation of this class.
  1344.      */
  1345.     private static LogFactory newStandardFactory(final ClassLoader classLoader) {
  1346.         if (isClassAvailable(LOG4J_TO_SLF4J_BRIDGE, classLoader)) {
  1347.             try {
  1348.                 return (LogFactory) Class.forName(FACTORY_SLF4J, true, classLoader).getConstructor().newInstance();
  1349.             } catch (final LinkageError | ReflectiveOperationException ignored) {
  1350.             } finally {
  1351.                 logDiagnostic(() ->
  1352.                         "[LOOKUP] Log4j API to SLF4J redirection detected. Loading the SLF4J LogFactory implementation '" + FACTORY_SLF4J + "'.");
  1353.             }
  1354.         }
  1355.         try {
  1356.             return (LogFactory) Class.forName(FACTORY_LOG4J_API, true, classLoader).getConstructor().newInstance();
  1357.         } catch (final LinkageError | ReflectiveOperationException ignored) {
  1358.         } finally {
  1359.             logDiagnostic(() -> "[LOOKUP] Loading the Log4j API LogFactory implementation '" + FACTORY_LOG4J_API + "'.");
  1360.         }
  1361.         try {
  1362.             return (LogFactory) Class.forName(FACTORY_SLF4J, true, classLoader).getConstructor().newInstance();
  1363.         } catch (final LinkageError | ReflectiveOperationException ignored) {
  1364.         } finally {
  1365.             logDiagnostic(() -> "[LOOKUP] Loading the SLF4J LogFactory implementation '" + FACTORY_SLF4J + "'.");
  1366.         }
  1367.         try {
  1368.             return (LogFactory) Class.forName(FACTORY_DEFAULT, true, classLoader).getConstructor().newInstance();
  1369.         } catch (final LinkageError | ReflectiveOperationException ignored) {
  1370.         } finally {
  1371.             logDiagnostic(() -> "[LOOKUP] Loading the legacy LogFactory implementation '" + FACTORY_DEFAULT + "'.");
  1372.         }
  1373.         return null;
  1374.     }

  1375.     /**
  1376.      * Returns a string that uniquely identifies the specified object, including
  1377.      * its class.
  1378.      * <p>
  1379.      * The returned string is of form {@code "className@hashCode"}, that is, is the same as
  1380.      * the return value of the {@link Object#toString()} method, but works even when
  1381.      * the specified object's class has overridden the toString method.
  1382.      * </p>
  1383.      *
  1384.      * @param obj may be null.
  1385.      * @return a string of form {@code className@hashCode}, or "null" if obj is null.
  1386.      * @since 1.1
  1387.      */
  1388.     public static String objectId(final Object obj) {
  1389.         if (obj == null) {
  1390.             return "null";
  1391.         }
  1392.         return obj.getClass().getName() + "@" + System.identityHashCode(obj);
  1393.     }

  1394.     /**
  1395.      * Releases any internal references to previously created {@link LogFactory}
  1396.      * instances that have been associated with the specified class loader
  1397.      * (if any), after calling the instance method {@code release()} on
  1398.      * each of them.
  1399.      *
  1400.      * @param classLoader ClassLoader for which to release the LogFactory
  1401.      */
  1402.     public static void release(final ClassLoader classLoader) {
  1403.         logDiagnostic(() -> "Releasing factory for class loader " + objectId(classLoader));
  1404.         // factories is not final and could be replaced in this block.
  1405.         final Hashtable<ClassLoader, LogFactory> factories = LogFactory.factories;
  1406.         synchronized (factories) {
  1407.             if (classLoader == null) {
  1408.                 if (nullClassLoaderFactory != null) {
  1409.                     nullClassLoaderFactory.release();
  1410.                     nullClassLoaderFactory = null;
  1411.                 }
  1412.             } else {
  1413.                 final LogFactory factory = factories.get(classLoader);
  1414.                 if (factory != null) {
  1415.                     factory.release();
  1416.                     factories.remove(classLoader);
  1417.                 }
  1418.             }
  1419.         }
  1420.     }

  1421.     /**
  1422.      * Release any internal references to previously created {@link LogFactory}
  1423.      * instances, after calling the instance method {@code release()} on
  1424.      * each of them.  This is useful in environments like servlet containers,
  1425.      * which implement application reloading by throwing away a ClassLoader.
  1426.      * Dangling references to objects in that class loader would prevent
  1427.      * garbage collection.
  1428.      */
  1429.     public static void releaseAll() {
  1430.         logDiagnostic("Releasing factory for all class loaders.");
  1431.         // factories is not final and could be replaced in this block.
  1432.         final Hashtable<ClassLoader, LogFactory> factories = LogFactory.factories;
  1433.         synchronized (factories) {
  1434.             factories.values().forEach(LogFactory::release);
  1435.             factories.clear();
  1436.             if (nullClassLoaderFactory != null) {
  1437.                 nullClassLoaderFactory.release();
  1438.                 nullClassLoaderFactory = null;
  1439.             }
  1440.         }
  1441.     }

  1442.     /** Trims the given string in a null-safe manner. */
  1443.     private static String trim(final String src) {
  1444.         return src != null ? src.trim() : null;
  1445.     }

  1446.     /**
  1447.      * Constructs a new instance.
  1448.      */
  1449.     protected LogFactory() {
  1450.     }

  1451.     /**
  1452.      * Gets the configuration attribute with the specified name (if any),
  1453.      * or {@code null} if there is no such attribute.
  1454.      *
  1455.      * @param name Name of the attribute to return
  1456.      * @return the configuration attribute with the specified name.
  1457.      */
  1458.     public abstract Object getAttribute(String name);

  1459.     /**
  1460.      * Gets an array containing the names of all currently defined configuration attributes. If there are no such attributes, a zero length array is returned.
  1461.      *
  1462.      * @return an array containing the names of all currently defined configuration attributes
  1463.      */
  1464.     public abstract String[] getAttributeNames();

  1465.     /**
  1466.      * Gets a Log for the given class.
  1467.      *
  1468.      * @param clazz Class for which a suitable Log name will be derived
  1469.      * @return a name from the specified class.
  1470.      * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned
  1471.      */
  1472.     public abstract Log getInstance(Class<?> clazz) throws LogConfigurationException;

  1473.     /**
  1474.      * Gets a (possibly new) {@code Log} instance, using the factory's current set of configuration attributes.
  1475.      * <p>
  1476.      * <strong>NOTE</strong> - Depending upon the implementation of the {@code LogFactory} you are using, the {@code Log} instance you are returned may or may
  1477.      * not be local to the current application, and may or may not be returned again on a subsequent call with the same name argument.
  1478.      * </p>
  1479.      *
  1480.      * @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
  1481.      *             is being wrapped)
  1482.      * @return a {@code Log} instance.
  1483.      * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned
  1484.      */
  1485.     public abstract Log getInstance(String name)
  1486.         throws LogConfigurationException;

  1487.     /**
  1488.      * Releases any internal references to previously created {@link Log}
  1489.      * instances returned by this factory.  This is useful in environments
  1490.      * like servlet containers, which implement application reloading by
  1491.      * throwing away a ClassLoader.  Dangling references to objects in that
  1492.      * class loader would prevent garbage collection.
  1493.      */
  1494.     public abstract void release();

  1495.     /**
  1496.      * Removes any configuration attribute associated with the specified name.
  1497.      * If there is no such attribute, no action is taken.
  1498.      *
  1499.      * @param name Name of the attribute to remove
  1500.      */
  1501.     public abstract void removeAttribute(String name);

  1502.     //
  1503.     // We can't do this in the class constructor, as there are many
  1504.     // static methods on this class that can be called before any
  1505.     // LogFactory instances are created, and they depend upon this
  1506.     // stuff having been set up.
  1507.     //
  1508.     // Note that this block must come after any variable declarations used
  1509.     // by any methods called from this block, as we want any static initializer
  1510.     // associated with the variable to run first. If static initializers for
  1511.     // variables run after this code, then (a) their value might be needed
  1512.     // by methods called from here, and (b) they might *override* any value
  1513.     // computed here!
  1514.     //
  1515.     // So the wisest thing to do is just to place this code at the very end
  1516.     // of the class file.

  1517.     /**
  1518.      * Sets the configuration attribute with the specified name.  Calling
  1519.      * this with a {@code null} value is equivalent to calling
  1520.      * {@code removeAttribute(name)}.
  1521.      *
  1522.      * @param name Name of the attribute to set
  1523.      * @param value Value of the attribute to set, or {@code null}
  1524.      *  to remove any setting for this attribute
  1525.      */
  1526.     public abstract void setAttribute(String name, Object value);

  1527. }