1

I observe a very strange behavior of method reference. We have a web app and chain of servlet hierarchy: Servlet A extends Servlet B extends HttpServlet (let's overlooked why do we need this).

The web app is hosting under Tomcat/Java 8 (web.xml version="3.1" metadata-complete="true"). At my local laptop (Tomcat 8.0.35 or 8.5.15, Java 8 Update 131, OSX) I observe stack overflow for such servlet code:

public class A extends B {
    @Override
    protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
        ServletCommon.servletServiceWrapper(pRequest, pResponse, super::service);
    }
}

The stack overflow (see below) looks very suspicious - it doesn't contain class B in the hierarchy at all! Class A calls HttpServlet directly (Why!? And how this even possible!!!????).

At the same time code with lambda works fine:

public class A extends B {
    @Override
    protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {
        ServletCommon.servletServiceWrapper(pRequest, pResponse, (t, u) -> super.service(t, u));
    }
}

The wrapper also contains very simple code:

public class ServletCommon {
    @FunctionalInterface
    public interface MyBiConsumer<T, U>{
        void accept(T t, U u) throws ServletException, IOException ;
    }
public static void servletServiceWrapper(HttpServletRequest request, HttpServletResponse response,
        MyBiConsumer<HttpServletRequest, HttpServletResponse> pDelegate) throws ServletException, IOException {
        pDelegate.accept(request, response);
    }
}

Stack trace

java.lang.StackOverflowError
    ServletA.lambda$service$0(ServletA.java:19)
    ServletCommon.servletServiceWrapper(ServletCommon.java:31)
    ServletA.service(ServletA.java:19)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    ServletA.lambda$service$0(ServletA.java:19)
    ServletCommon.servletServiceWrapper(ServletCommon.java:31)
    ServletA.service(ServletA.java:19)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    ...

Any ideas?

FoxyBOA
  • 5,788
  • 8
  • 48
  • 82

1 Answers1

1

Pretty much, yeah.

You're managing to call the wrong service() method (the public one with ServletRequest, not the protected one with HttpServletRequest), resulting in an infinite loop. However your example code isn't clear enough to indicate why that's happening.

Community
  • 1
  • 1
Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • You're right, but why "super::service" point out to HttpServlet.service(ServletRequest req, ServletResponse res), but "(t, u) -> super.service(t, u)" point out to "B.service(HttpServletRequest pRequest, HttpServletResponse pResponse)". I observer that the behavior is differ from runtime env. (i.e. Eclipse, Tomcat, Jetty etc.) – FoxyBOA May 17 '17 at 11:31
  • 1
    Could you try working out the simplest possible test case and see if the behaviour is really different between environments? That could warrant a bug report, if the method resolution works in a non-deterministic fashion. – Kayaman May 17 '17 at 11:39
  • 2
    Sounds like a compiler bug. I couldn’t find any `javac` version having that bug, so perhaps, there’s an `ecj` version having such a bug… – Holger May 17 '17 at 17:08
  • As I mentioned in the post, the bug is under Tomcat (compiled by javac) and I didn't observe it under Eclipse + Jetty. – FoxyBOA May 18 '17 at 12:20