You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ck...@apache.org on 2019/12/31 23:18:14 UTC

[logging-log4j2] branch release-2.x updated: LOG4J2-2754: LoaderUtil.getClassLoaders may discover additional loaders

This is an automated email from the ASF dual-hosted git repository.

ckozak pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new 8ef2b34  LOG4J2-2754: LoaderUtil.getClassLoaders may discover additional loaders
8ef2b34 is described below

commit 8ef2b34105b33869a983b0be3256b7cf59febaed
Author: Carter Kozak <ck...@ckozak.net>
AuthorDate: Tue Dec 31 15:10:15 2019 -0500

    LOG4J2-2754: LoaderUtil.getClassLoaders may discover additional loaders
    
    The utility no longer erroneously returns a result with a null element in some
    environments.
    Also updated loadClass to avoid internally throwing and catching a null
    pointer exception when getThreadContextClassLoader returns null.
---
 .../org/apache/logging/log4j/util/LoaderUtil.java  | 42 +++++++++++-----------
 src/changes/changes.xml                            |  3 ++
 2 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
index 567b3ae..e0ac42b 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
@@ -21,11 +21,9 @@ import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -106,31 +104,32 @@ public final class LoaderUtil {
     }
 
     public static ClassLoader[] getClassLoaders() {
-        final List<ClassLoader> classLoaders = new ArrayList<>();
+        final Collection<ClassLoader> classLoaders = new LinkedHashSet<>();
         final ClassLoader tcl = getThreadContextClassLoader();
-        classLoaders.add(tcl);
-        // Some implementations may use null to represent the bootstrap class loader.
-        final ClassLoader current = LoaderUtil.class.getClassLoader();
-        if (current != null && current != tcl) {
-            classLoaders.add(current);
-            final ClassLoader parent = current.getParent();
-            while (parent != null && !classLoaders.contains(parent)) {
-                classLoaders.add(parent);
-            }
-        }
-        ClassLoader parent = tcl == null ? null : tcl.getParent();
-        while (parent != null && !classLoaders.contains(parent)) {
-            classLoaders.add(parent);
-            parent = parent.getParent();
+        if (tcl != null) {
+            classLoaders.add(tcl);
         }
+        accumulateClassLoaders(LoaderUtil.class.getClassLoader(), classLoaders);
+        accumulateClassLoaders(tcl == null ? null : tcl.getParent(), classLoaders);
         final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
-		if (!classLoaders.contains(systemClassLoader)) {
+		if (systemClassLoader != null) {
             classLoaders.add(systemClassLoader);
         }
         return classLoaders.toArray(new ClassLoader[classLoaders.size()]);
     }
 
     /**
+     * Adds the provided loader to the loaders collection, and traverses up the tree until either a null
+     * value or a classloader which has already been added is encountered.
+     */
+    private static void accumulateClassLoaders(ClassLoader loader, Collection<ClassLoader> loaders) {
+        // Some implementations may use null to represent the bootstrap class loader.
+        if (loader != null && loaders.add(loader)) {
+            accumulateClassLoaders(loader.getParent(), loaders);
+        }
+    }
+
+    /**
      * Determines if a named Class can be loaded or not.
      *
      * @param className The class name.
@@ -163,10 +162,13 @@ public final class LoaderUtil {
             return Class.forName(className);
         }
         try {
-            return getThreadContextClassLoader().loadClass(className);
+            ClassLoader tccl = getThreadContextClassLoader();
+            if (tccl != null) {
+                return tccl.loadClass(className);
+            }
         } catch (final Throwable ignored) {
-            return Class.forName(className);
         }
+        return Class.forName(className);
     }
 
     /**
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 047a8c4..d4f8973 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -60,6 +60,9 @@
       <action issue="LOG4J2-2752" dev="ckozak" type="fix">
         MutableLogEvent and RingBufferLogEvent avoid StringBuffer and parameter array allocation unless reusable messages are used.
       </action>
+      <action issue="LOG4J2-2754" dev="ckozak" type="fix">
+        LoaderUtil.getClassLoaders may discover additional loaders and no longer erroneously returns a result with a null element in some environments.
+      </action>
     </release>
     <release version="2.13.0" date="2019-12-11" description="GA Release 2.13.0">
       <action issue="LOG4J2-2058" dev="rgoers" type="fix">