This is an automated email from the ASF dual-hosted git repository. cbrisson pushed a commit to branch VELOCITY-952 in repository https://gitbox.apache.org/repos/asf/velocity-engine.git
commit a65430e6f81144d2773764ed03548b452393dc43 Author: Claude Brisson <[email protected]> AuthorDate: Mon Aug 26 10:42:33 2024 +0200 Introspection should use the top-most declaration of methods --- .../velocity/util/introspection/ClassMap.java | 1 + .../velocity/util/introspection/MethodMap.java | 67 +++++++++++++++++++++- .../velocity/test/issues/Velocity952TestCase.java | 58 +++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java index e5fe9d7f..58176801 100644 --- a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java +++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java @@ -184,6 +184,7 @@ public class ClassMap int modifiers = method.getModifiers(); if (Modifier.isPublic(modifiers)) { + method = MethodMap.getTopMostMethodDeclaration(method); methodCache.put(method); } } diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java index 1d0408bb..b2efed98 100644 --- a/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java +++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java @@ -22,6 +22,7 @@ package org.apache.velocity.util.introspection; import org.apache.commons.lang3.reflect.TypeUtils; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; @@ -315,11 +316,75 @@ public class MethodMap switch (bestMatches.size()) { case 0: return null; - case 1: return bestMatches.get(0).method; + case 1: return getTopMostMethodDeclaration(bestMatches.get(0).method); default: throw new AmbiguousException(); } } + /** + * Once we identified a best match of a specific call, walk up the chain of inheritance to find the top-most + * declaration for this method. This is needed to avoid IllegalAccessException, when a public API method + * is implemented by a class which is not exported. + * @param method + * @return + */ + public static Method getTopMostMethodDeclaration(Method method) + { + Class<?> clazz = method.getDeclaringClass(); + String name = method.getName(); + Class<?>[] arguments = method.getParameterTypes(); + + while (clazz != null) + { + Class<?> superClass = null; + Method superMethod = null; + + // check the super class + superClass = clazz.getSuperclass(); + if (superClass != null) + { + try + { + superMethod = superClass.getDeclaredMethod(name, arguments); + if ((superMethod.getModifiers() & Modifier.PUBLIC) == 0) { + superMethod = null; + } + } + catch (NoSuchMethodException nsme) + { + } + } + + if (superMethod == null) + { + // check among the interfaces + Class<?>[] interfaces = clazz.getInterfaces(); + for (Class<?> intf : interfaces) + { + try + { + superMethod = intf.getDeclaredMethod(name, arguments); + if (superMethod != null) + { + superClass = intf; + break; + } + } + catch (NoSuchMethodException nsme) + { + } + } + } + + if (superMethod != null) + { + method = superMethod; + } + clazz = superClass; + } + return method; + } + /** * Simple distinguishable exception, used when * we run across ambiguous overloading. Caught diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity952TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity952TestCase.java new file mode 100755 index 00000000..55dda8ae --- /dev/null +++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity952TestCase.java @@ -0,0 +1,58 @@ +package org.apache.velocity.test.issues; + +/* + * 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. + */ + +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.test.BaseTestCase; +import org.apache.velocity.util.introspection.ClassMap; +import org.apache.velocity.util.introspection.MethodMap; + +import java.lang.reflect.Method; +import java.util.TimeZone; + +/** + * This class tests the fix for VELOCITY-952. + */ +public class Velocity952TestCase extends BaseTestCase +{ + public Velocity952TestCase(String name) + { + super(name); + } + + public void testMethodMap() + { + ClassMap classMap = new ClassMap(TimeZone.getDefault().getClass(), Velocity.getLog()); + Method getOffset = classMap.findMethod("getOffset", new Object[] { 1L }); + assertNotNull(getOffset); + assertEquals(TimeZone.class, getOffset.getDeclaringClass()); + } + + protected void setUpContext(VelocityContext context) + { + context.put("tz", TimeZone.getDefault()); + } + + public void testEnd2End() + { + assertEvalEquals("3600000", "$tz.getOffset(1)"); + } +}
