Hello. I'm upgrading from Tomcat 7.0.30 to 8.5.24. Everything is working fine, but I notice that BeanELResolver behaves differently when calling a bean varargs method with no argument.
My action bean has a method with the signature "public String getIncludes(final String... moduleNames)". I'm calling it on one of my JSP pages like "${actionBean.getIncludes()}", but got MethodNotFoundException. Calling it with some arguments or an empty array would work – as one would expect. Please see the stacktrace below. This method call used to be okay in 7.0.30. I've tried to find something in the EL and JSP specifications around varargs, but couldn't find anything concrete. I got the impression that it's not officially supported in the specs, but Tomcat supports it. I'm treating it as a bug as I've already created a patch for it – see below. However, I'm new to this mailing list so I thought I would ask first. Is this a bug? Thanks Ing === My Patch === Index: test/javax/el/TestBeanELResolver.java =================================================================== --- test/javax/el/TestBeanELResolver.java (revision 1817331) +++ test/javax/el/TestBeanELResolver.java (working copy) @@ -18,6 +18,7 @@ import java.beans.FeatureDescriptor; import java.beans.PropertyDescriptor; +import java.util.ArrayList; import java.util.Iterator; import org.junit.Assert; @@ -425,6 +426,83 @@ Assert.assertTrue(context.isPropertyResolved()); } + @Test + public void testInvokeMore1() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNames", + new Class<?>[] {}, new Object[] {}); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test + public void testInvokeMore2() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getMoreNames", + new Class<?>[] { Integer.class }, new Object[] { 10 }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test + public void testInvokeMore3() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getMoreNames", + new Class<?>[] { Integer.class }, new Object[] { 10 }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test + public void testInvokeMore4() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getMoreNames", + new Class<?>[] { null }, new Object[] { null }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test + public void testInvokeMore5() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getMoreNames", + new Class<?>[] { Integer.class, ArrayList.class }, new Object[] { 2, new Object[] {} }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test + public void testInvokeMore6() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNames", + new Class<?>[] { String.class, String.class, String.class }, new Object[] { "1", "2", "3" }); + + Assert.assertEquals(BEAN_NAME, result); + } + + @Test + public void testInvokeMore7() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getMoreNames", + new Class<?>[] { Integer.class, String.class, String.class, String.class }, new Object[] { 0, "1", "2", "3" }); + + Assert.assertEquals(BEAN_NAME, result); + } + /** * Tests that the method name cannot be coerced to String. */ @@ -445,6 +523,24 @@ new Object[] {}); } + @Test(expected = MethodNotFoundException.class) + public void testInvokeMore8() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + resolver.invoke(context, new TesterBean(BEAN_NAME), "getMoreNames", + new Class<?>[] { Integer.class, Integer.class, String.class }, new Object[] { 10, 20, "50" }); + } + + @Test(expected = MethodNotFoundException.class) + public void testInvokeMore9() { + BeanELResolver resolver = new BeanELResolver(); + ELContext context = new StandardELContext(ELManager.getExpressionFactory()); + + resolver.invoke(context, new TesterBean(BEAN_NAME), "getNames", + new Class<?>[] { Integer.class }, new Object[] { 10 }); + } + /** * Tests that a call to a non existing method will throw an exception. */ Index: test/javax/el/TesterBean.java =================================================================== --- test/javax/el/TesterBean.java (revision 1817331) +++ test/javax/el/TesterBean.java (working copy) @@ -29,6 +29,15 @@ return name; } + public String getNames(@SuppressWarnings("unused") String... something) { + return name; + } + + @SuppressWarnings("unused") + public String getMoreNames(Integer someNumber, String... something) { + return name; + } + public void setName(String name) { this.name = name; } Index: java/javax/el/Util.java =================================================================== --- java/javax/el/Util.java (revision 1817331) +++ java/javax/el/Util.java (working copy) @@ -247,7 +247,7 @@ // Check the number of parameters if (!(paramCount == mParamCount || - (w.isVarArgs() && paramCount >= mParamCount))) { + (w.isVarArgs() && paramCount >= (mParamCount - 1)))) { // Method has wrong number of parameters continue; } @@ -259,9 +259,12 @@ boolean noMatch = false; for (int i = 0; i < mParamCount; i++) { // Can't be null - if (mParamTypes[i].equals(paramTypes[i])) { - exactMatch++; - } else if (i == (mParamCount - 1) && w.isVarArgs()) { + if (w.isVarArgs() && i == (mParamCount - 1)) { + if (i == paramCount && paramCount == (mParamCount - 1)) { + // Nothing is passed as varargs + assignableMatch++; + break; + } Class<?> varType = mParamTypes[i].getComponentType(); for (int j = i; j < paramCount; j++) { if (isAssignableFrom(paramTypes[j], varType)) { @@ -283,18 +286,22 @@ // lead to a varArgs method matching when the result // should be ambiguous } - } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) { - assignableMatch++; } else { - if (paramValues == null) { - noMatch = true; - break; + if (mParamTypes[i].equals(paramTypes[i])) { + exactMatch++; + } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) { + assignableMatch++; } else { - if (isCoercibleFrom(paramValues[i], mParamTypes[i])) { - coercibleMatch++; - } else { + if (paramValues == null) { noMatch = true; break; + } else { + if (isCoercibleFrom(paramValues[i], mParamTypes[i])) { + coercibleMatch++; + } else { + noMatch = true; + break; + } } } } === The Stacktrace === 06 Dec 2017 16:27:39,530 | 1216397 [ERROR] {http-nio-8080-exec-5} (org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/ohp-web-test].[jsp]) Servlet.service() for servlet [jsp] threw exception javax.el.MethodNotFoundException: Method not found: class com.orchestral.commonweb.test.web.action.button.OrchestralAction.getIncludes() at javax.el.Util.findWrapper(Util.java:351) at javax.el.Util.findMethod(Util.java:213) at javax.el.BeanELResolver.invoke(BeanELResolver.java:149) at org.apache.jasper.el.JasperELResolver.invoke(JasperELResolver.java:147) at org.apache.el.parser.AstValue.getValue(AstValue.java:159) at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:184) at org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:944) at org.apache.jsp.button.orchestral_jsp._jspService(orchestral_jsp.java:131) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:591) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:527) at net.sourceforge.stripes.action.ForwardResolution.execute(ForwardResolution.java:106) at com.orchestral.common.stripes.resolutions.JspViewResolution.execute(JspViewResolution.java:56) at net.sourceforge.stripes.controller.DispatcherHelper$7.intercept(DispatcherHelper.java:497) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:158) at net.sourceforge.stripes.controller.HttpCacheInterceptor.intercept(HttpCacheInterceptor.java:99) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor.intercept(BeforeAfterMethodInterceptor.java:113) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.ExecutionContext.wrap(ExecutionContext.java:74) at net.sourceforge.stripes.controller.DispatcherHelper.executeResolution(DispatcherHelper.java:491) at net.sourceforge.stripes.controller.DispatcherServlet.executeResolution(DispatcherServlet.java:286) at net.sourceforge.stripes.controller.DispatcherServlet.service(DispatcherServlet.java:170) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:591) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:527) at org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:894) at org.apache.jsp.owl_002dimpl.page_jsp._jspService(page_jsp.java:131) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:591) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:527) at org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:894) at org.apache.jsp.owl_002dimpl.owl_002dbody_jsp._jspService(owl_002dbody_jsp.java:214) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:591) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:527) at org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:894) at org.apache.jsp.owl_002dimpl.owl_jsp._jspx_meth_stripes_005flayout_002dcomponent_005f0(owl_jsp.java:257) at org.apache.jsp.owl_002dimpl.owl_jsp._jspx_meth_stripes_005flayout_002drender_005f0(owl_jsp.java:216) at org.apache.jsp.owl_002dimpl.owl_jsp._jspService(owl_jsp.java:144) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:591) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:527) at org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:894) at org.apache.jasper.runtime.PageContextImpl.doInclude(PageContextImpl.java:679) at org.apache.jasper.runtime.PageContextImpl.include(PageContextImpl.java:673) at net.sourceforge.stripes.tag.layout.LayoutContext.doInclude(LayoutContext.java:203) at net.sourceforge.stripes.tag.layout.LayoutComponentRenderer.write(LayoutComponentRenderer.java:137) at net.sourceforge.stripes.tag.layout.LayoutComponentTag.doStartTag(LayoutComponentTag.java:181) at org.apache.jsp.layout.vanilla_jsp._jspx_meth_stripes_005flayout_002dcomponent_005f1(vanilla_jsp.java:419) at org.apache.jsp.layout.vanilla_jsp._jspx_meth_stripes_005flayout_002ddefinition_005f0(vanilla_jsp.java:222) at org.apache.jsp.layout.vanilla_jsp._jspService(vanilla_jsp.java:147) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:591) at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:527) at org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:894) at org.apache.jasper.runtime.PageContextImpl.doInclude(PageContextImpl.java:679) at org.apache.jasper.runtime.PageContextImpl.include(PageContextImpl.java:673) at net.sourceforge.stripes.tag.layout.LayoutContext.doInclude(LayoutContext.java:203) at net.sourceforge.stripes.tag.layout.LayoutRenderTag.doEndTag(LayoutRenderTag.java:175) at org.apache.jsp.owl_002dimpl.owl_jsp._jspx_meth_stripes_005flayout_002drender_005f0(owl_jsp.java:227) at org.apache.jsp.owl_002dimpl.owl_jsp._jspService(owl_jsp.java:144) at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443) at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386) at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.orchestral.core.web.impl.PlatformFilter.doFilter(PlatformFilter.java:102) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728) at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:470) at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:395) at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316) at net.sourceforge.stripes.action.ForwardResolution.execute(ForwardResolution.java:110) at net.sourceforge.stripes.controller.DispatcherHelper$7.intercept(DispatcherHelper.java:497) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:158) at net.sourceforge.stripes.controller.HttpCacheInterceptor.intercept(HttpCacheInterceptor.java:99) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor.intercept(BeforeAfterMethodInterceptor.java:113) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.ExecutionContext.wrap(ExecutionContext.java:74) at net.sourceforge.stripes.controller.DispatcherHelper.executeResolution(DispatcherHelper.java:491) at net.sourceforge.stripes.controller.DispatcherServlet.executeResolution(DispatcherServlet.java:286) at net.sourceforge.stripes.controller.DispatcherServlet.service(DispatcherServlet.java:170) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at net.sourceforge.stripes.controller.StripesFilter.doFilter(StripesFilter.java:260) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.orchestral.common.servlet.filter.NoCacheFilter.doFilter(NoCacheFilter.java:62) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.orchestral.common.servlet.filter.XUACompatibleFilter.doFilter(XUACompatibleFilter.java:58) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.orchestral.common.servlet.filter.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:74) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.orchestral.core.web.impl.PlatformFilter.doFilter(PlatformFilter.java:102) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at com.orchestral.core.web.impl.session.SessionTracker.invoke(SessionTracker.java:313) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650) at com.orchestral.core.web.impl.MonitorValve.invoke(MonitorValve.java:65) at com.orchestral.core.web.impl.ErrorReportValve.invoke(ErrorReportValve.java:62) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) -- Ing