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

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

commit 80de81652e0d3eaf195402c2bdc28316a8c71dde
Author: Matt Sicker <[email protected]>
AuthorDate: Fri Nov 3 18:16:25 2023 -0500

    Backport LoaderUtil updates
    
    Most of what's changed in 3.0 minus the JPMS-specific code.
    
    Signed-off-by: Matt Sicker <[email protected]>
---
 .../java/org/apache/logging/log4j/util/Cast.java   |  40 +++
 .../logging/log4j/util/InternalException.java      |  54 ++++
 .../org/apache/logging/log4j/util/LoaderUtil.java  | 345 +++++++++++++++------
 .../apache/logging/log4j/util/package-info.java    |   2 +-
 4 files changed, 353 insertions(+), 88 deletions(-)

diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Cast.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/Cast.java
new file mode 100644
index 0000000000..8a1dc40975
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/Cast.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.util;
+
+@InternalApi
+public final class Cast {
+
+    /**
+     * Returns the provided object cast to the generic parameter type or null 
when the argument is null.
+     *
+     * @param o   object to cast
+     * @param <T> the type to cast
+     * @return object after casting or null if the object was null
+     * @throws ClassCastException if the object cannot be cast to the provided 
type
+     */
+    public static <T> T cast(final Object o) {
+        if (o == null) {
+            return null;
+        }
+        @SuppressWarnings("unchecked") final T t = (T) o;
+        return t;
+    }
+
+    private Cast() {
+    }
+}
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/InternalException.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/InternalException.java
new file mode 100644
index 0000000000..486aa8b84d
--- /dev/null
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/InternalException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.util;
+
+/**
+ * Exception thrown when an error occurs while accessing internal resources. 
This is generally used to
+ * convert checked exceptions to runtime exceptions.
+ */
+public class InternalException extends RuntimeException {
+
+    private static final long serialVersionUID = 6366395965071580537L;
+
+    /**
+     * Construct an exception with a message.
+     *
+     * @param message The reason for the exception
+     */
+    public InternalException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Construct an exception with a message and underlying cause.
+     *
+     * @param message The reason for the exception
+     * @param cause The underlying cause of the exception
+     */
+    public InternalException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Construct an exception with an underlying cause.
+     *
+     * @param cause The underlying cause of the exception
+     */
+    public InternalException(final Throwable cause) {
+        super(cause);
+    }
+}
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 b39291cd35..cbc5044759 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
@@ -17,6 +17,7 @@
 package org.apache.logging.log4j.util;
 
 import java.io.IOException;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
 import java.security.AccessController;
@@ -34,10 +35,9 @@ import java.util.Objects;
  * @see Thread#getContextClassLoader()
  * @see ClassLoader#getSystemClassLoader()
  */
+@InternalApi
 public final class LoaderUtil {
 
-    private static final ClassLoader[] EMPTY_CLASS_LOADER_ARRAY = {};
-
     /**
      * System property to set to ignore the thread context ClassLoader.
      *
@@ -45,24 +45,34 @@ public final class LoaderUtil {
      */
     public static final String IGNORE_TCCL_PROPERTY = "log4j.ignoreTCL";
 
-    private static final SecurityManager SECURITY_MANAGER = 
System.getSecurityManager();
-
     // this variable must be lazily loaded; otherwise, we get a nice circular 
class loading problem where LoaderUtil
     // wants to use PropertiesUtil, but then PropertiesUtil wants to use 
LoaderUtil.
     private static Boolean ignoreTCCL;
 
+    private static final RuntimePermission GET_CLASS_LOADER = new 
RuntimePermission("getClassLoader");
     private static final boolean GET_CLASS_LOADER_DISABLED;
 
     private static final PrivilegedAction<ClassLoader> TCCL_GETTER = new 
ThreadContextClassLoaderGetter();
 
     static {
-        if (SECURITY_MANAGER != null) {
+        if (System.getSecurityManager() != null) {
             boolean getClassLoaderDisabled;
             try {
-                SECURITY_MANAGER.checkPermission(new 
RuntimePermission("getClassLoader"));
+                AccessController.checkPermission(GET_CLASS_LOADER);
+                // seems like we'll be ok
                 getClassLoaderDisabled = false;
             } catch (final SecurityException ignored) {
-                getClassLoaderDisabled = true;
+                try {
+                    // let's see if we can obtain that permission
+                    AccessController.doPrivileged((PrivilegedAction<Void>) () 
-> {
+                        AccessController.checkPermission(GET_CLASS_LOADER);
+                        return null;
+                    }, null, GET_CLASS_LOADER);
+                    getClassLoaderDisabled = false;
+                } catch (final SecurityException ignore) {
+                    // no chance
+                    getClassLoaderDisabled = true;
+                }
             }
             GET_CLASS_LOADER_DISABLED = getClassLoaderDisabled;
         } else {
@@ -74,34 +84,90 @@ public final class LoaderUtil {
     }
 
     /**
-     * Gets the current Thread ClassLoader. Returns the system ClassLoader if 
the TCCL is {@code null}. If the system
-     * ClassLoader is {@code null} as well, then the ClassLoader for this 
class is returned. If running with a
-     * {@link SecurityManager} that does not allow access to the Thread 
ClassLoader or system ClassLoader, then the
-     * ClassLoader for this class is returned.
+     * Returns the ClassLoader to use.
+     *
+     * @return the ClassLoader.
+     */
+    public static ClassLoader getClassLoader() {
+        return getClassLoader(LoaderUtil.class, null);
+    }
+
+    // TODO: this method could use some explanation
+    public static ClassLoader getClassLoader(final Class<?> class1, final 
Class<?> class2) {
+        PrivilegedAction<ClassLoader> action = () -> {
+            final ClassLoader loader1 = class1 == null ? null : 
class1.getClassLoader();
+            final ClassLoader loader2 = class2 == null ? null : 
class2.getClassLoader();
+            final ClassLoader referenceLoader = GET_CLASS_LOADER_DISABLED
+                    ? getThisClassLoader()
+                    : Thread.currentThread().getContextClassLoader();
+            if (isChild(referenceLoader, loader1)) {
+                return isChild(referenceLoader, loader2) ? referenceLoader : 
loader2;
+            }
+            return isChild(loader1, loader2) ? loader1 : loader2;
+        };
+        return AccessController.doPrivileged(action, null, GET_CLASS_LOADER);
+    }
+
+    /**
+     * Determines if one ClassLoader is a child of another ClassLoader. Note 
that a {@code null} ClassLoader is
+     * interpreted as the system ClassLoader as per convention.
+     *
+     * @param loader1 the ClassLoader to check for childhood.
+     * @param loader2 the ClassLoader to check for parenthood.
+     * @return {@code true} if the first ClassLoader is a strict descendant of 
the second ClassLoader.
+     */
+    private static boolean isChild(final ClassLoader loader1, final 
ClassLoader loader2) {
+        if (loader1 != null && loader2 != null) {
+            ClassLoader parent = loader1.getParent();
+            while (parent != null && parent != loader2) {
+                parent = parent.getParent();
+            }
+            // once parent is null, we're at the system CL, which would 
indicate they have separate ancestry
+            return parent != null;
+        }
+        return loader1 != null;
+    }
+
+    /**
+     * Looks up the ClassLoader for this current thread. If this class does 
not have the runtime permission
+     * {@code getClassLoader}, then the only ClassLoader this attempts to look 
up is the loader behind this
+     * class. When a SecurityManager is installed, this attempts to make a 
privileged call to get the current
+     * {@linkplain Thread#getContextClassLoader() thread context ClassLoader}, 
falling back to either the
+     * ClassLoader of this class or the {@linkplain 
ClassLoader#getSystemClassLoader() system ClassLoader}.
+     * When no SecurityManager is present, the same lookups are performed 
without use of {@link AccessController}.
+     * If none of these strategies can obtain a ClassLoader, then this returns 
{@code null}.
      *
-     * @return the current ThreadContextClassLoader.
+     * @return the current thread's ClassLoader, a fallback loader, or null if 
no fallback can be determined
      */
     public static ClassLoader getThreadContextClassLoader() {
         if (GET_CLASS_LOADER_DISABLED) {
             // we can at least get this class's ClassLoader regardless of 
security context
             // however, if this is null, there's really no option left at this 
point
-            return LoaderUtil.class.getClassLoader();
+            try {
+                return getThisClassLoader();
+            } catch (final SecurityException ignored) {
+                return null;
+            }
         }
-        return SECURITY_MANAGER == null ? TCCL_GETTER.run() : 
AccessController.doPrivileged(TCCL_GETTER);
+        return AccessController.doPrivileged(TCCL_GETTER, null, 
GET_CLASS_LOADER);
+    }
+
+    private static ClassLoader getThisClassLoader() {
+        return LoaderUtil.class.getClassLoader();
     }
 
-    /**
-     *
-     */
     private static class ThreadContextClassLoaderGetter implements 
PrivilegedAction<ClassLoader> {
         @Override
         public ClassLoader run() {
-            final ClassLoader cl = 
Thread.currentThread().getContextClassLoader();
-            if (cl != null) {
-                return cl;
+            final ClassLoader contextClassLoader = 
Thread.currentThread().getContextClassLoader();
+            if (contextClassLoader != null) {
+                return contextClassLoader;
             }
-            final ClassLoader ccl = LoaderUtil.class.getClassLoader();
-            return ccl == null && !GET_CLASS_LOADER_DISABLED ? 
ClassLoader.getSystemClassLoader() : ccl;
+            final ClassLoader thisClassLoader = getThisClassLoader();
+            if (thisClassLoader != null || GET_CLASS_LOADER_DISABLED) {
+                return thisClassLoader;
+            }
+            return ClassLoader.getSystemClassLoader();
         }
     }
 
@@ -114,8 +180,8 @@ public final class LoaderUtil {
      */
     public static boolean isClassAvailable(final String className) {
         try {
-            final Class<?> clazz = loadClass(className);
-            return clazz != null;
+            loadClass(className);
+            return true;
         } catch (final ClassNotFoundException | LinkageError e) {
             return false;
         } catch (final Throwable e) {
@@ -128,81 +194,119 @@ public final class LoaderUtil {
      * Loads a class by name. This method respects the {@link 
#IGNORE_TCCL_PROPERTY} Log4j property. If this property is
      * specified and set to anything besides {@code false}, then the default 
ClassLoader will be used.
      *
-     * @param className The class name.
-     * @return the Class for the given name.
-     * @throws ClassNotFoundException if the specified class name could not be 
found
+     * @param className fully qualified class name to load
+     * @return the loaded class
+     * @throws ClassNotFoundException      if the specified class name could 
not be found
+     * @throws ExceptionInInitializerError if an exception is thrown during 
class initialization
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
      * @since 2.1
      */
     public static Class<?> loadClass(final String className) throws 
ClassNotFoundException {
-        if (isIgnoreTccl()) {
-            return Class.forName(className);
+        ClassLoader classLoader = isIgnoreTccl() ? getThisClassLoader() : 
getThreadContextClassLoader();
+        if (classLoader == null) {
+            classLoader = getThisClassLoader();
         }
+        return Class.forName(className, true, classLoader);
+    }
+
+    /**
+     * Loads and initializes a class given its fully qualified class name. All 
checked reflective operation
+     * exceptions are translated into equivalent {@link LinkageError} classes.
+     *
+     * @param className fully qualified class name to load
+     * @return the loaded class
+     * @throws NoClassDefFoundError        if the specified class name could 
not be found
+     * @throws ExceptionInInitializerError if an exception is thrown during 
class initialization
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
+     * @see #loadClass(String)
+     * @since 2.22.0
+     */
+    public static Class<?> loadClassUnchecked(final String className) {
         try {
-            final ClassLoader tccl = getThreadContextClassLoader();
-            if (tccl != null) {
-                return tccl.loadClass(className);
-            }
-        } catch (final Throwable ignored) {
+            return loadClass(className);
+        } catch (final ClassNotFoundException e) {
+            final NoClassDefFoundError error = new 
NoClassDefFoundError(e.getMessage());
+            error.initCause(e);
+            throw error;
         }
-        return Class.forName(className);
     }
 
     /**
      * Loads and instantiates a Class using the default constructor.
      *
-     * @param <T> the type of the class modeled by the {@code Class} object.
+     * @param <T>   the type of the class modeled by the {@code Class} object.
      * @param clazz The class.
      * @return new instance of the class.
-     * @throws IllegalAccessException if the class can't be instantiated 
through a public constructor
-     * @throws InstantiationException if there was an exception whilst 
instantiating the class
-     * @throws InvocationTargetException if there was an exception whilst 
constructing the class
+     * @throws NoSuchMethodException       if no zero-arg constructor exists
+     * @throws SecurityException           if this class is not allowed to 
access declared members of the provided class
+     * @throws IllegalAccessException      if the class can't be instantiated 
through a public constructor
+     * @throws InstantiationException      if the provided class is abstract 
or an interface
+     * @throws InvocationTargetException   if an exception is thrown by the 
constructor
+     * @throws ExceptionInInitializerError if an exception was thrown while 
initializing the class
      * @since 2.7
      */
     public static <T> T newInstanceOf(final Class<T> clazz)
-            throws InstantiationException, IllegalAccessException, 
InvocationTargetException {
-        try {
-            return clazz.getConstructor().newInstance();
-        } catch (final NoSuchMethodException ignored) {
-            // FIXME: looking at the code for Class.newInstance(), this seems 
to do the same thing as above
-            return clazz.newInstance();
-        }
+            throws InstantiationException, IllegalAccessException, 
InvocationTargetException, NoSuchMethodException {
+        final Constructor<T> constructor = clazz.getDeclaredConstructor();
+        return constructor.newInstance();
     }
 
     /**
-     * Loads and instantiates a Class using the default constructor.
+     * Creates an instance of the provided class using the default 
constructor. All checked reflective operation
+     * exceptions are translated into {@link LinkageError} or {@link 
InternalException}.
      *
-     * @param className The class name.
-     * @return new instance of the class.
-     * @throws ClassNotFoundException if the class isn't available to the 
usual ClassLoaders
-     * @throws IllegalAccessException if the class can't be instantiated 
through a public constructor
-     * @throws InstantiationException if there was an exception whilst 
instantiating the class
-     * @throws InvocationTargetException if there was an exception whilst 
constructing the class
-     * @since 2.1
+     * @param clazz class to instantiate
+     * @param <T>   the type of the object being instantiated
+     * @return instance of the class
+     * @throws NoSuchMethodError  if no zero-arg constructor exists
+     * @throws SecurityException  if this class is not allowed to access 
declared members of the provided class
+     * @throws InternalException  if an exception is thrown by the constructor
+     * @throws InstantiationError if the provided class is abstract or an 
interface
+     * @throws IllegalAccessError if the class cannot be accessed
+     * @since 2.22.0
      */
-    @SuppressWarnings("unchecked")
-    public static <T> T newInstanceOf(final String className) throws 
ClassNotFoundException, IllegalAccessException,
-            InstantiationException, InvocationTargetException {
-        return newInstanceOf((Class<T>) loadClass(className));
+    public static <T> T newInstanceOfUnchecked(final Class<T> clazz) {
+        try {
+            return newInstanceOf(clazz);
+        } catch (final NoSuchMethodException e) {
+            final NoSuchMethodError error = new 
NoSuchMethodError(e.getMessage());
+            error.initCause(e);
+            throw error;
+        } catch (final InvocationTargetException e) {
+            final Throwable cause = e.getCause();
+            throw new InternalException(cause);
+        } catch (final InstantiationException e) {
+            final InstantiationError error = new 
InstantiationError(e.getMessage());
+            error.initCause(e);
+            throw error;
+        } catch (final IllegalAccessException e) {
+            final IllegalAccessError error = new 
IllegalAccessError(e.getMessage());
+            error.initCause(e);
+            throw error;
+        }
     }
 
     /**
-     * Loads and instantiates a derived class using its default constructor.
+     * Loads and instantiates a Class using the default constructor.
      *
-     * @param className The class name.
-     * @param clazz The class to cast it to.
-     * @param <T> The type of the class to check.
-     * @return new instance of the class cast to {@code T}
-     * @throws ClassNotFoundException if the class isn't available to the 
usual ClassLoaders
-     * @throws IllegalAccessException if the class can't be instantiated 
through a public constructor
-     * @throws InstantiationException if there was an exception whilst 
instantiating the class
-     * @throws InvocationTargetException if there was an exception whilst 
constructing the class
-     * @throws ClassCastException if the constructed object isn't type 
compatible with {@code T}
+     * @param className fully qualified class name to load, initialize, and 
construct
+     * @param <T>       type the class must be compatible with
+     * @return new instance of the class
+     * @throws ClassNotFoundException      if the class isn't available to the 
usual ClassLoaders
+     * @throws ExceptionInInitializerError if an exception was thrown while 
initializing the class
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
+     * @throws ClassCastException          if the class is not compatible with 
the generic type parameter provided
+     * @throws NoSuchMethodException       if no zero-arg constructor exists
+     * @throws SecurityException           if this class is not allowed to 
access declared members of the provided class
+     * @throws IllegalAccessException      if the class can't be instantiated 
through a public constructor
+     * @throws InstantiationException      if the provided class is abstract 
or an interface
+     * @throws InvocationTargetException   if an exception is thrown by the 
constructor
      * @since 2.1
      */
-    public static <T> T newCheckedInstanceOf(final String className, final 
Class<T> clazz)
-            throws ClassNotFoundException, InvocationTargetException, 
InstantiationException,
-            IllegalAccessException {
-        return clazz.cast(newInstanceOf(className));
+    public static <T> T newInstanceOf(final String className) throws 
ClassNotFoundException, IllegalAccessException,
+            InstantiationException, InvocationTargetException, 
NoSuchMethodException {
+        final Class<T> clazz = Cast.cast(loadClass(className));
+        return newInstanceOf(clazz);
     }
 
     /**
@@ -212,16 +316,20 @@ public final class LoaderUtil {
      * @param clazz        The class to cast it to.
      * @param <T>          The type to cast it to.
      * @return new instance of the class given in the property or {@code null} 
if the property was unset.
-     * @throws ClassNotFoundException    if the class isn't available to the 
usual ClassLoaders
-     * @throws IllegalAccessException    if the class can't be instantiated 
through a public constructor
-     * @throws InstantiationException    if there was an exception whilst 
instantiating the class
-     * @throws InvocationTargetException if there was an exception whilst 
constructing the class
-     * @throws ClassCastException        if the constructed object isn't type 
compatible with {@code T}
+     * @throws ClassNotFoundException      if the class isn't available to the 
usual ClassLoaders
+     * @throws ExceptionInInitializerError if an exception was thrown while 
initializing the class
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
+     * @throws ClassCastException          if the class is not compatible with 
the generic type parameter provided
+     * @throws NoSuchMethodException       if no zero-arg constructor exists
+     * @throws SecurityException           if this class is not allowed to 
access declared members of the provided class
+     * @throws IllegalAccessException      if the class can't be instantiated 
through a public constructor
+     * @throws InstantiationException      if the provided class is abstract 
or an interface
+     * @throws InvocationTargetException   if an exception is thrown by the 
constructor
      * @since 2.5
      */
     public static <T> T newCheckedInstanceOfProperty(final String 
propertyName, final Class<T> clazz)
-        throws ClassNotFoundException, InvocationTargetException, 
InstantiationException,
-        IllegalAccessException {
+            throws ClassNotFoundException, InvocationTargetException, 
InstantiationException,
+            IllegalAccessException, NoSuchMethodException {
         final String className = 
PropertiesUtil.getProperties().getStringProperty(propertyName);
         if (className == null) {
             return null;
@@ -229,6 +337,76 @@ public final class LoaderUtil {
         return newCheckedInstanceOf(className, clazz);
     }
 
+    /**
+     * Loads and instantiates a class by name using its default constructor. 
All checked reflective operation
+     * exceptions are translated into corresponding {@link LinkageError} 
classes.
+     *
+     * @param className fully qualified class name to load, initialize, and 
construct
+     * @param <T>       type the class must be compatible with
+     * @return new instance of the class
+     * @throws NoClassDefFoundError        if the specified class name could 
not be found
+     * @throws ExceptionInInitializerError if an exception is thrown during 
class initialization
+     * @throws ClassCastException          if the class is not compatible with 
the generic type parameter provided
+     * @throws NoSuchMethodError           if no zero-arg constructor exists
+     * @throws SecurityException           if this class is not allowed to 
access declared members of the provided class
+     * @throws InternalException           if an exception is thrown by the 
constructor
+     * @throws InstantiationError          if the provided class is abstract 
or an interface
+     * @throws IllegalAccessError          if the class cannot be accessed
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
+     * @since 2.22.0
+     */
+    public static <T> T newInstanceOfUnchecked(final String className) {
+        final Class<T> clazz = Cast.cast(loadClassUnchecked(className));
+        return newInstanceOfUnchecked(clazz);
+    }
+
+    /**
+     * Loads and instantiates a derived class using its default constructor.
+     *
+     * @param className The class name.
+     * @param clazz     The class to cast it to.
+     * @param <T>       The type of the class to check.
+     * @return new instance of the class cast to {@code T}
+     * @throws ClassNotFoundException      if the class isn't available to the 
usual ClassLoaders
+     * @throws ExceptionInInitializerError if an exception is thrown during 
class initialization
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
+     * @throws ClassCastException          if the constructed object isn't 
type compatible with {@code T}
+     * @throws NoSuchMethodException       if no zero-arg constructor exists
+     * @throws SecurityException           if this class is not allowed to 
access declared members of the provided class
+     * @throws IllegalAccessException      if the class can't be instantiated 
through a public constructor
+     * @throws InstantiationException      if the provided class is abstract 
or an interface
+     * @throws InvocationTargetException   if there was an exception whilst 
constructing the class
+     * @since 2.1
+     */
+    public static <T> T newCheckedInstanceOf(final String className, final 
Class<T> clazz) throws ClassNotFoundException,
+            InvocationTargetException, InstantiationException, 
IllegalAccessException, NoSuchMethodException {
+        return newInstanceOf(loadClass(className).asSubclass(clazz));
+    }
+
+    /**
+     * Loads the provided class by name as a checked subtype of the given 
class. All checked reflective operation
+     * exceptions are translated into corresponding {@link LinkageError} 
classes.
+     *
+     * @param className fully qualified class name to load
+     * @param supertype supertype of the class being loaded
+     * @param <T>       type of instance to return
+     * @return new instance of the requested class
+     * @throws NoClassDefFoundError        if the provided class name could 
not be found
+     * @throws ExceptionInInitializerError if an exception is thrown during 
class initialization
+     * @throws ClassCastException          if the loaded class is not a 
subtype of the provided class
+     * @throws NoSuchMethodError           if no zero-arg constructor exists
+     * @throws SecurityException           if this class is not allowed to 
access declared members of the provided class
+     * @throws InternalException           if an exception is thrown by the 
constructor
+     * @throws InstantiationError          if the provided class is abstract 
or an interface
+     * @throws IllegalAccessError          if the class cannot be accessed
+     * @throws LinkageError                if the linkage of the class fails 
for any other reason
+     * @since 2.22.0
+     */
+    public static <T> T newInstanceOfUnchecked(final String className, final 
Class<T> supertype) {
+        final Class<? extends T> clazz = 
loadClassUnchecked(className).asSubclass(supertype);
+        return newInstanceOfUnchecked(clazz);
+    }
+
     private static boolean isIgnoreTccl() {
         // we need to lazily initialize this, but concurrent access is not an 
issue
         if (ignoreTCCL == null) {
@@ -312,14 +490,7 @@ public final class LoaderUtil {
 
             final UrlResource that = (UrlResource) o;
 
-            if (classLoader != null ? !classLoader.equals(that.classLoader) : 
that.classLoader != null) {
-                return false;
-            }
-            if (url != null ? !url.equals(that.url) : that.url != null) {
-                return false;
-            }
-
-            return true;
+            return Objects.equals(classLoader, that.classLoader) && 
Objects.equals(url, that.url);
         }
 
         @Override
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/package-info.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/package-info.java
index 99db5c911f..8265e82149 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/package-info.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/package-info.java
@@ -20,7 +20,7 @@
  * There are no guarantees for binary or logical compatibility in this package.
  */
 @Export
-@Version("2.20.1")
+@Version("2.22.0")
 package org.apache.logging.log4j.util;
 
 import org.osgi.annotation.bundle.Export;

Reply via email to