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

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

commit ee09f69951c0867c0ade41658effb6f48a49eec1
Author: Gary D. Gregory <[email protected]>
AuthorDate: Fri Aug 29 15:45:37 2025 -0400

    MethodUtils cannot find or invoke a vararg method without providing
    vararg types or values #1427
---
 src/changes/changes.xml                            |  1 +
 .../apache/commons/lang3/reflect/MethodUtils.java  | 38 +++++++++++---------
 .../commons/lang3/reflect/MethodUtilsTest.java     | 42 ++++++++++++++++++++--
 3 files changed, 62 insertions(+), 19 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ea332d6d3..4a4fb9dbb 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -52,6 +52,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="fix" dev="ggregory" due-to="Gary 
Gregory">MethodUtils cannot find or invoke a public method on a public class 
implemented in its package-private superclass.</action>
     <action                   type="fix" dev="ggregory" due-to="Stanislav 
Fort, Gary 
Gregory">org.apache.commons.lang3.concurrent.AtomicSafeInitializer.get() can 
spin internally if the FailableSupplier given to 
org.apache.commons.lang3.concurrent.AbstractConcurrentInitializer.AbstractBuilder.setInitializer(FailableSupplier)
 throws a RuntimeException.</action>
     <action issue="LANG-1783" type="fix" dev="ggregory" due-to="Arnout 
Engelen, Stanislav Fort, Gary Gregory">WordUtils.containsAllWords​() may throw 
PatternSyntaxException.</action>
+    <action                   type="fix" dev="ggregory" due-to="Joe Ferner, 
Gary Gregory">MethodUtils cannot find or invoke a vararg method without 
providing vararg types or values #1427.</action>
     <!-- FIX Javadoc -->
     <action                   type="fix" dev="ggregory" due-to="Gary 
Gregory">[javadoc] General improvements.</action>
     <action                   type="fix" dev="ggregory" due-to="Gary 
Gregory">[javadoc] Fix thrown exception documentation for 
org.apache.commons.lang3.reflect.MethodUtils.getMethodObject(Class&lt;?&gt;, 
String, Class&lt;?&gt;...).</action>
diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java 
b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
index 78be53882..80cbaf6e1 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
@@ -320,44 +320,48 @@ private static Method getInvokeMethod(final boolean 
forceAccess, final String me
      *
      * @param cls            find method in this class.
      * @param methodName     find method with this name.
-     * @param parameterTypes find method with most compatible parameters.
+     * @param requestTypes find method with most compatible parameters.
      * @return The accessible method or null.
      * @throws SecurityException if an underlying accessible object's method 
denies the request.
      * @see SecurityManager#checkPermission
      */
-    public static Method getMatchingAccessibleMethod(final Class<?> cls, final 
String methodName, final Class<?>... parameterTypes) {
-        final Method candidate = getMethodObject(cls, methodName, 
parameterTypes);
+    public static Method getMatchingAccessibleMethod(final Class<?> cls, final 
String methodName, final Class<?>... requestTypes) {
+        final Method candidate = getMethodObject(cls, methodName, 
requestTypes);
         if (candidate != null) {
             return MemberUtils.setAccessibleWorkaround(candidate);
         }
         // search through all methods
         final Method[] methods = cls.getMethods();
         final List<Method> matchingMethods = Stream.of(methods)
-                .filter(method -> method.getName().equals(methodName) && 
MemberUtils.isMatchingMethod(method, 
parameterTypes)).collect(Collectors.toList());
+                .filter(method -> method.getName().equals(methodName) && 
MemberUtils.isMatchingMethod(method, 
requestTypes)).collect(Collectors.toList());
         // Sort methods by signature to force deterministic result
         matchingMethods.sort(METHOD_BY_SIGNATURE);
         Method bestMatch = null;
         for (final Method method : matchingMethods) {
             // get accessible version of method
             final Method accessibleMethod = getAccessibleMethod(method);
-            if (accessibleMethod != null && (bestMatch == null || 
MemberUtils.compareMethodFit(accessibleMethod, bestMatch, parameterTypes) < 0)) 
{
+            if (accessibleMethod != null && (bestMatch == null || 
MemberUtils.compareMethodFit(accessibleMethod, bestMatch, requestTypes) < 0)) {
                 bestMatch = accessibleMethod;
             }
         }
         if (bestMatch != null) {
             MemberUtils.setAccessibleWorkaround(bestMatch);
-        }
-        if (bestMatch != null && bestMatch.isVarArgs() && 
bestMatch.getParameterTypes().length > 0 && parameterTypes.length > 0) {
-            final Class<?>[] methodParameterTypes = 
bestMatch.getParameterTypes();
-            final Class<?> methodParameterComponentType = 
methodParameterTypes[methodParameterTypes.length - 1].getComponentType();
-            final String varVargTypeName = 
ClassUtils.primitiveToWrapper(methodParameterComponentType).getName();
-            final Class<?> lastParameterType = 
parameterTypes[parameterTypes.length - 1];
-            final String parameterTypeName = lastParameterType == null ? null 
: lastParameterType.getName();
-            final String parameterTypeSuperClassName = lastParameterType == 
null ? null
-                    : lastParameterType.getSuperclass() != null ? 
lastParameterType.getSuperclass().getName() : null;
-            if (parameterTypeName != null && parameterTypeSuperClassName != 
null && !varVargTypeName.equals(parameterTypeName)
-                    && !varVargTypeName.equals(parameterTypeSuperClassName)) {
-                return null;
+            if (bestMatch.isVarArgs()) {
+                final int paramLen = bestMatch.getParameterTypes().length;
+                final int requestTypesLen = requestTypes.length;
+                if (paramLen > 0 && requestTypesLen > 0 && !(requestTypesLen 
== paramLen - 1)) {
+                    final Class<?>[] methodTypes = 
bestMatch.getParameterTypes();
+                    final Class<?> componentType = 
methodTypes[methodTypes.length - 1].getComponentType();
+                    final String varVargTypeName = 
ClassUtils.primitiveToWrapper(componentType).getName();
+                    final Class<?> requestLastType = 
requestTypes[requestTypesLen - 1];
+                    final String requestTypeName = requestLastType == null ? 
null : requestLastType.getName();
+                    final String requestTypeSuperClassName = requestLastType 
== null ? null
+                            : requestLastType.getSuperclass() != null ? 
requestLastType.getSuperclass().getName() : null;
+                    if (requestTypeName != null && requestTypeSuperClassName 
!= null && !varVargTypeName.equals(requestTypeName)
+                            && 
!varVargTypeName.equals(requestTypeSuperClassName)) {
+                        return null;
+                    }
+                }
             }
         }
         return bestMatch;
diff --git 
a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java 
b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
index 458d4d59c..8d240fca7 100644
--- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
@@ -249,6 +249,14 @@ public static String staticIntStringVarArg(final int 
intArg, final String... arg
             return "static int, String...";
         }
 
+        public static String staticIntLongVarArg(final int intArg, final 
long... args) {
+            return "static int, long...";
+        }
+
+        public static String staticIntIntVarArg(final int intArg, final int... 
args) {
+            return "static int, int...";
+        }
+
         public static String varOverload(final Boolean... args) {
             return "Boolean...";
         }
@@ -378,6 +386,14 @@ public String intStringVarArg(final int intArg, final 
String... args) {
             return "int, String...";
         }
 
+        public String intLongVarArg(final int intArg, final long... args) {
+            return "int, long...";
+        }
+
+        public String intIntVarArg(final int intArg, final int... args) {
+            return "int, int...";
+        }
+
         public void oneParameter(final String s) {
             // empty
         }
@@ -1072,10 +1088,21 @@ void testInvokeMethod_VarArgsWithNullValues() throws 
Exception {
 
     @Test
     void testInvokeMethod1PlusVarArgs() throws Exception {
-        // assertEquals("int, String...", MethodUtils.invokeMethod(testBean, 
"intStringVarArg", 1));
+        // intStringVarArg
+        assertEquals("int, String...", MethodUtils.invokeMethod(testBean, 
"intStringVarArg", 1));
         assertEquals("int, String...", MethodUtils.invokeMethod(testBean, 
"intStringVarArg", 1, "s"));
         assertEquals("int, String...", MethodUtils.invokeMethod(testBean, 
"intStringVarArg", 1, "s1", "s2"));
         assertThrows(NoSuchMethodException.class, () -> 
MethodUtils.invokeMethod(testBean, "intStringVarArg", 1, "s1", 5));
+        // intLongVarArg
+        assertEquals("int, long...", MethodUtils.invokeMethod(testBean, 
"intLongVarArg", 1));
+        assertEquals("int, long...", MethodUtils.invokeMethod(testBean, 
"intLongVarArg", 1, 2L));
+        assertEquals("int, long...", MethodUtils.invokeMethod(testBean, 
"intLongVarArg", 1, 2L, 3L));
+        assertThrows(NoSuchMethodException.class, () -> 
MethodUtils.invokeMethod(testBean, "intLongVarArg", 1, "s1", 5));
+        // intIntVarArg
+        assertEquals("int, int...", MethodUtils.invokeMethod(testBean, 
"intIntVarArg", 1));
+        assertEquals("int, int...", MethodUtils.invokeMethod(testBean, 
"intIntVarArg", 1, 2));
+        assertEquals("int, int...", MethodUtils.invokeMethod(testBean, 
"intIntVarArg", 1, 2, 3));
+        assertThrows(NoSuchMethodException.class, () -> 
MethodUtils.invokeMethod(testBean, "intLongVarArg", 1, "s1", 5));
     }
 
     @Test
@@ -1191,10 +1218,21 @@ void testInvokeStaticMethod() throws Exception {
 
     @Test
     void testInvokeStaticMethod1PlusVarArgs() throws Exception {
-        // assertEquals("static int, String...", 
MethodUtils.invokeStaticMethod(TestBean.class, "staticIntStringVarArg", 1));
+        // staticIntStringVarArg
+        assertEquals("static int, String...", 
MethodUtils.invokeStaticMethod(TestBean.class, "staticIntStringVarArg", 1));
         assertEquals("static int, String...", 
MethodUtils.invokeStaticMethod(TestBean.class, "staticIntStringVarArg", 1, 
"s"));
         assertEquals("static int, String...", 
MethodUtils.invokeStaticMethod(TestBean.class, "staticIntStringVarArg", 1, 
"s1", "s2"));
         assertThrows(NoSuchMethodException.class, () -> 
MethodUtils.invokeStaticMethod(TestBean.class, "staticIntStringVarArg", 1, 
"s1", 5));
+        // staticIntLongVarArg
+        assertEquals("static int, long...", MethodUtils.invokeMethod(testBean, 
"staticIntLongVarArg", 1));
+        assertEquals("static int, long...", MethodUtils.invokeMethod(testBean, 
"staticIntLongVarArg", 1, 2L));
+        assertEquals("static int, long...", MethodUtils.invokeMethod(testBean, 
"staticIntLongVarArg", 1, 2L, 3L));
+        assertThrows(NoSuchMethodException.class, () -> 
MethodUtils.invokeMethod(testBean, "staticIntLongVarArg", 1, "s1", 5));
+        // staticIntIntVarArg
+        assertEquals("static int, int...", MethodUtils.invokeMethod(testBean, 
"staticIntIntVarArg", 1));
+        assertEquals("static int, int...", MethodUtils.invokeMethod(testBean, 
"staticIntIntVarArg", 1, 2));
+        assertEquals("static int, int...", MethodUtils.invokeMethod(testBean, 
"staticIntIntVarArg", 1, 2, 3));
+        assertThrows(NoSuchMethodException.class, () -> 
MethodUtils.invokeMethod(testBean, "staticIntIntVarArg", 1, "s1", 5));
     }
 
     @Test

Reply via email to