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<?>, String, Class<?>...).</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
