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

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 824837751 Fix ConstructorUtils.getMatchingAccessibleConstructor on 
non-public classes (#1793)
824837751 is described below

commit 82483775157159a8edcc9220498f034fa1621611
Author: gaurav kumar pandey <[email protected]>
AuthorDate: Sat Sep 19 20:16:42 2026 +0530

    Fix ConstructorUtils.getMatchingAccessibleConstructor on non-public classes 
(#1793)
    
    * Fix ConstructorUtils.getMatchingAccessibleConstructor on non-public 
classes
    
    * Update ConstructorUtils class-level Javadoc for non-public class 
accessibility behavior
---
 .../commons/lang3/reflect/ConstructorUtils.java    | 26 +++++++++------
 .../lang3/reflect/ConstructorUtilsTest.java        | 38 ++++++++++++++++++++++
 2 files changed, 54 insertions(+), 10 deletions(-)

diff --git 
a/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java 
b/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java
index cee2ebca5..860622f1f 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/ConstructorUtils.java
@@ -28,16 +28,13 @@
  * Utility reflection methods focused on constructors, modeled after {@link 
MethodUtils}.
  *
  * <h2>Known Limitations</h2>
- * <h3>Accessing Public Constructors In A Default Access Superclass</h3>
+ * <h3>Accessing Constructors In A Non-Public Class</h3>
  * <p>
- * There is an issue when invoking {@code public} constructors contained in a 
default access superclass. Reflection correctly locates these constructors and
- * assigns them as {@code public}. However, an {@link IllegalAccessException} 
is thrown if the constructor is invoked.
- * </p>
- *
- * <p>
- * {@link ConstructorUtils} contains a workaround for this situation: it will 
attempt to call {@link 
java.lang.reflect.AccessibleObject#setAccessible(boolean)}
- * on this constructor. If this call succeeds, then the method can be invoked 
as normal. This call will only succeed when the application has sufficient
- * security privileges. If this call fails then a warning will be logged and 
the method may fail.
+ * Constructors in non-public classes (such as package-private classes or 
classes enclosed in non-public classes) are
+ * not accessible. Methods such as {@link #getAccessibleConstructor(Class, 
Class[])} and
+ * {@link #getMatchingAccessibleConstructor(Class, Class[])} return {@code 
null} when invoked on non-public classes.
+ * Consequently, invocation methods such as {@link #invokeConstructor(Class, 
Object...)} and
+ * {@link #invokeExactConstructor(Class, Object...)} throw a {@link 
NoSuchMethodException}.
  * </p>
  *
  * @since 2.5
@@ -64,6 +61,9 @@ public class ConstructorUtils {
      */
     public static <T> Constructor<T> getAccessibleConstructor(final Class<T> 
cls, final Class<?>... parameterTypes) {
         Objects.requireNonNull(cls, "cls");
+        if (!isAccessible(cls)) {
+            return null;
+        }
         try {
             return 
getAccessibleConstructor(cls.getConstructor(parameterTypes));
         } catch (final NoSuchMethodException e) {
@@ -114,10 +114,16 @@ public static <T> Constructor<T> 
getAccessibleConstructor(final Constructor<T> c
      */
     public static <T> Constructor<T> getMatchingAccessibleConstructor(final 
Class<T> cls, final Class<?>... parameterTypes) {
         Objects.requireNonNull(cls, "cls");
+        if (!isAccessible(cls)) {
+            return null;
+        }
         // see if we can find the constructor directly
         // most of the time this works and it's much faster
         try {
-            return 
MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes));
+            final Constructor<T> ctor = 
getAccessibleConstructor(cls.getConstructor(parameterTypes));
+            if (ctor != null) {
+                return MemberUtils.setAccessibleWorkaround(ctor);
+            }
         } catch (final NoSuchMethodException ignored) {
             // ignore
         }
diff --git 
a/src/test/java/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java 
b/src/test/java/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java
index 9bb73ae98..5bbe2c627 100644
--- a/src/test/java/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java
@@ -46,6 +46,8 @@ private static class BaseClass {
     }
 
     static class PrivateClass {
+        private final String value;
+
         @SuppressWarnings("unused")
         public static class PublicInnerClass {
             public PublicInnerClass() {
@@ -54,6 +56,19 @@ public PublicInnerClass() {
 
         @SuppressWarnings("unused")
         public PrivateClass() {
+            this("default");
+        }
+
+        public PrivateClass(final String value) {
+            this.value = value;
+        }
+
+        public PrivateClass(final Number number) {
+            this.value = String.valueOf(number);
+        }
+
+        public String getValue() {
+            return value;
         }
     }
 
@@ -242,6 +257,29 @@ void testVarArgsUnboxing() throws Exception {
         assertArrayEquals(new String[] { "2", "3" }, testBean.varArgs);
     }
 
+    @Test
+    void testGetMatchingAccessibleConstructorOnNonPublicClass() {
+        
assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.class));
+        
assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.class,
 String.class));
+        
assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.class,
 Integer.class));
+        
assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.PublicInnerClass.class));
+    }
+
+    @Test
+    void testInvokeConstructorOnNonPublicClass() {
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeConstructor(PrivateClass.class));
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeConstructor(PrivateClass.class, "test"));
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeConstructor(PrivateClass.class, Integer.valueOf(1)));
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeConstructor(PrivateClass.PublicInnerClass.class));
+    }
+
+    @Test
+    void testInvokeExactConstructorOnNonPublicClass() {
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeExactConstructor(PrivateClass.class));
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeExactConstructor(PrivateClass.class, "test"));
+        assertThrows(NoSuchMethodException.class, () -> 
ConstructorUtils.invokeExactConstructor(PrivateClass.PublicInnerClass.class));
+    }
+
     private String toString(final Class<?>[] c) {
         return Arrays.asList(c).toString();
     }

Reply via email to