Details on HttpSession Object

Overview

  • HTTP is a stateless protocol
  • To keep JAVA object available longer than the duration of a HTTP request  each Object is stored within the HTTP session object. This is called Session tracking feature.
  • Typical technical solutions  for Session Tracking are
      • Cookies
      • URL rewriting

    Hidden form fields.

 

Why and how should you protect your HTTP session object

  • HttpSession object is not thread safe
  • Browser Tabbing, Fast Page Reloading  and async. AJAX request may give you concurrent access to the same HttpSession object
  • There is no guarantee that multiple calls to HttpServletRequest.getSession() will return the same HttpSession object .
  • Don’t synchronize on HttpSession object as this object may be recreated by certain containers
  • Synchronize on immutable object returned by :  session.getId().intern()

Reference

Creating a HTTP session using cookies

JSESSIONID cookie is created/sent when session is created. Session is created when your code calls request.getSession() 
or request.getSession(true) for the first time. If you just want get session, but not create it if it doesn't exists, 
use request.getSession(false) -- this will return you a session or null. In this case, new session is not created, 
and JSESSIONID cookie is not sent. (This also means that session isn't necessarily created on first request... you 
and your code is in control when the session is created)

Understanding Browser Session and HTTP Session Object

Using Browser tabbing with firefox  

Start a first Browser session and run their initial  HTTP POST to create the HTTP session by running timeoutTest() :
Session 1:  $ firefox http://localhost:8180/WFJPA2EL-1.0/ 
12:56:42.530 timeoutTest() - NEW Session  - ID zjiDVDLbFkQYhJ4bARWTmP6P - Access Count: 0- i
             JSESSIONID cookie: zjiDVDLbFkQYhJ4bARWTmP6P.wls1 - MaxIncativeInterval:  10 - 
             Last AccessedTime: 12:56:34.636 - Cookie MaxAge: -1

Now start as new Browser Tab using the same URL  and run the HTTP Post request again 
Session 2: $ firefox http://localhost:8180/WFJPA2EL-1.0/
12:56:47.856 timeoutTest() - WELCOME Back Session   - ID zjiDVDLbFkQYhJ4bARWTmP6P - Access Count: 1- 
             JSESSIONID cookie: zjiDVDLbFkQYhJ4bARWTmP6P.wls1 - MaxIncativeInterval:  10 - 
             Last AccessedTime: 12:56:42.547 - Cookie MaxAge: -1

- The second request doesn't create a new HTTP session object
- Instead browser session are uing the HTTP Session object 
- Thats is why you should serialize access to the HTTP session object as multiple threads may use this JAVA object !
 

Using a different Firefox profile or different Host name will create different HTTP Objects:

Session 1: $ firefox http://localhost:8180/WFJPA2EL-1.0/
10:06:59.096 timeoutTest() - NEW Session  - ID O3tl4IqIDCAGWB16O52HO6h6 - Access Count: 0- 
             JSESSIONID cookie: O3tl4IqIDCAGWB16O52HO6h6.wls1 - MaxIncativeInterval:  10 - 
             Last AccessedTime: 10:06:53.374 - Cookie MaxAge: -1

Session 2: $  firefox http://wls1:8180/WFJPA2EL-1.0/
10:07:42.817 timeoutTest() - NEW Session  - ID 6kqRcL-DDpKCywOqrq3wo8yB - Access Count: 0- 
             JSESSIONID cookie: 6kqRcL-DDpKCywOqrq3wo8yB.wls1 - MaxIncativeInterval:  10 - 
             Last AccessedTime: 10:07:37.164 - Cookie MaxAge: -1

- Using a different hostname [ or Firefox Profiles ] will create a new HTTP session object !

HTTP session object details

  • A HTTP Session objects stores data about Cookies and Attributes
  • The JSESSIONID is the cookie for implementing the Session Feature
  • The accessCount attribute tracks how often this session is reused by an HTTP request
10:19:27.673  timeoutTest() - WELCOME Back Session   - ID fwdaf1VInpfsA-20853SiT_F - Access Count: 1- 
              JSESSIONID cookie: fwdaf1VInpfsA-20853SiT_F.wls1 - MaxIncativeInterval:  10 - 
              Last AccessedTime: 10:19:23.205 - Cookie MaxAge: -1
10:19:27.674  attr  = accessCount      value = 1
10:19:27.675  Cookie   name  = JSESSIONID   value = fwdaf1VInpfsA-20853SiT_F.wls1    Cookie MaxAge = -1
Details:
 - Cookie MaxAge = -1       -> No Cookie Timeout
 - MaxIncativeInterval:  10 -> HTTP session timeout [ 10 seconds ] 
 - accsessCount             -> HTTP object stored within HTTP session object 

Java Code to display HTTP session details and set and read attributes

public void trackSession(HttpSession session, String methodName)
      {
        final Object lock = session.getId().intern();  
        synchronized(lock) 
          {
            String heading = null;
            accessCount =    (Integer)session.getAttribute("accessCount");
            if (accessCount == null) 
              {
                accessCount = new Integer(0);
                heading =  methodName + " - NEW Session";
              } 
            else 
              {
                heading =  methodName + " - WELCOME Back Session ";
                accessCount = new Integer(accessCount.intValue() + 1);
              }
            String jSessionId = "JSESSIONID not found";
            int jSessionMaxAge = 0;
            HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); 

                // There no API to return JESSIONID cookie - we need to loop throught the Cookie Arrary 
            Cookie[] cookies = req.getCookies();
            for(Cookie cookie : cookies)
              {
                if("JSESSIONID".equals(cookie.getName()))
                  {    
                    jSessionId = cookie.getValue();
                    jSessionMaxAge = cookie.getMaxAge();
                               }
               }
                       
            session.setAttribute("accessCount", accessCount);
            setSessionInfo(heading +  "  - ID "  + session.getId() + " - Access Count: " + accessCount.intValue() 
                 + "- JSESSIONID cookie: " + jSessionId + " - MaxIncativeInterval:  " + session.getMaxInactiveInterval()
                 + " - Last AccessedTime: " + Tools.getTime2(session.getLastAccessedTime()) + " - Cookie MaxAge: " + jSessionMaxAge);
            
                // display Session details : Cookies and 
            if (displaySessionDetails )
              {   
                Enumeration es = session.getAttributeNames();
                while (es.hasMoreElements())
                  {
                    String attr = (String)es.nextElement();
                    Object value = session.getValue(attr);
                    setSessionInfo("      attr  = "+ attr +"      value = "+ value);
                  } 

                for (Cookie cookie : cookies) 
                  {
                    String cookieName = cookie.getName();
                    String cookieValue = cookie.getValue();
                    setSessionInfo("    Cookie   name  = "+  cookieName+"   value = "+ cookieValue + "    Cookie MaxAge = " +  cookie.getMaxAge());
                  }    
              }
          }                                  
      }

How to deal with HTTP Session Timeout – a JSF sample

  • For security and memory management, sessions need to be invalidated at a certain time
There are two related methods in HttpSession.
- HttpSession.invalidate() 
  By invoking invalidate(), the session will be invalidated immediately. 
  This is useful for the case such as logout.
- HttpSession.setMaxInactiveInterval(int interval)
 The method setMaxInactiveInterval(int interval) allows us to configure the time (in seconds) 
 between client requests before the servlet container will invalidate the session.
 That is, an idle session will be invalidated after the specified time.

See https://weblogs.java.net/blog/swchan2/archive/2013/08/29/when-httpsession-invalidated

HTTP session timemout can be configured via web.xml 

web.xml sample 
 <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>

setMaxInactiveInterval() sample
    HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
    if ( session == null)
      {
        throw new IllegalArgumentException(methodName+ ": Could not get HTTP session : ");    
      }
    session.setMaxInactiveInterval(5);

Note setMaxInactiveInterval configures the time (in seconds) between client requests 
and before the servlet container will invalidate the session.

After your initial HTTP GET request wait 5 seconds and send a HTTP POST request. 
This HTTP POST request will fail with:   
  javax.faces.application.ViewExpiredException: viewId:/index.xhtml - 
    View /index.xhtml could not be restored.
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:210)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:121)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)

Fix : You may Add an error page to your web.xml file
    <error-page>
        <exception-type>javax.faces.application.ViewExpiredException</exception-type>
        <location>/faces/index.xhtml</location>
    </error-page> 

Further details on Thread Safety

Never assign any request or session scoped data as an instance variable of a servlet or filter. 
It will be shared among all other requests in other sessions. 
That's threadunsafe! The below example illustrates that:

public class ExampleServlet extends HttpServlet 
  {
    private Object thisIsNOTThreadSafe;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
      {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
     } 
 }

Reference