Search code examples
javajakarta-eelistenerstack-overflowhttpsession

Why calling invalidate() in HttpSessionListener doesn't give StackOverflowError?


I have a jsp file and a HttpSessionListener to monitor HttpSession destroying activities.

index.jsp

<%
    HttpSession s = request.getSession();
    System.out.println("SID1 : " + s.getId());
    s.setAttribute("Key", "Value");
    s.invalidate();
%>

SessionListener

@WebListener
public class SessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent se) {
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession s = se.getSession();
        System.out.println("SID2 : " + s.getId());
        System.out.println(s.getAttribute("Key"));
        s.invalidate();
        System.out.println("Session Destroyed");
    }

}

Now according to the above situation, sending a HTTP request to index.jsp should create a HttpSession and call it's invalidate() method, meanwhile the HttpSessionListener should catch the same HttpSession and call the invalidate() once again, and this process should repeat over and over again.

Which should eventually result in throwing a java.lang.StackOverflowError. But I have got the the following output without any errors.

SID1 : A2751AE9E782A17380415B0078C9ED90
SID2 : A2751AE9E782A17380415B0078C9ED90
Value
Session Destroyed

I have tested it with both GlassFish and Tomcat servers, the result stays the same. Can someone explain what's going on?


Solution

  • Apparently this is because session is already invalidated when you invoke invalidate method for the second time from public void sessionDestroyed(HttpSessionEvent se) {...}.

    Session.beginInvalidate() returns false value in this case and this block is not invoked:

    boolean result = beginInvalidate();
    
    try {
        //if the session was not already invalid, or in process of being invalidated, do invalidate
        if (result) {
             //tell id mgr to remove session from all contexts
             _handler.getSessionIdManager().invalidateAll(_sessionData.getId());
        }
    }
    

    In particular,_handler.getSessionIdManager().invalidateAll invokes SessionHandler.invalidate, which invokes SessionHandler.removeSession which invokes _sessionListeners.get(i).sessionDestroyed(event);.

    So in case if the session is already invalidated this scenario doesn't work.