Thursday, 2 August 2012

Session life cycle : Understand session and session time out in a java web application

Click here for low cost java hosting servers and deploy your war files.


What is a session in java web application :-
 The session is unique id used to track the user. In a java web application, the session is a JSESSIONID cookie created by the application container. When an http request is made to the java web application, it will check whether there is a valid JSESSIONID cookie available in the request header. If the request header doesn't contain a valid JSESSIONID cookie then the application container will create a new JSESSIONID cookie and will send it with response header. If the request contains a valid JSESSIONID cookie then it will be used to track the request.  The response header will contain JSESSIONID cookie only if the application container (server) creates a new JSESSIONID.

Bit  specifically :
When the first http request is made from a web browser to a java web application url say http://www.javaweb.com/test-service, the request may not have a cookie containing JSESSIONID in its header so the java web application container (eg: jetty, tomcat, etc..) will create a new JSESSIONID cookie and will send it back with the response header. Then the browser will save this JSESSIONID cookie and when the next request is made to the same domain (but may be a different url say http://www.javaweb/test-service2) the JSESSIONID cookie will be sent with its request header.

What is session time out : 
The session can be automatically destroyed after a particular interval of time of the inactivity. And this life time for JSESSIONID can be specified WEB-INF/web.xml file as

<session-config>
<session-timeout>30</session-timeout>
</session-config>

Here the 30 denotes 30 minutes for the life time for the JSESSIONID i.e. when the time reaches 30 minutes after the last request is made to the same domain with a specific JSESSIONID (say JSESSIONID=0A1CE552BADB1634CEBE9A61E0C887AF) cookie, then that JSESSIONID will be destroyed in the server. And, even if a request is made with the same JSESSIONID=0A1CE552BADB1634CEBE9A61E0C887AF cookie the container will create a new JSESSIONID and will send it with response header as a cookie.
If the time is set to 0 or less than 0, then the session will not be expired.

What is issue of setting session time out 0 (zero) (i.e. not to time out the session) :
Now we know when server creates new session. Suppose if someone is making many requests to the server without a JSESSIONID cookie then a new session will be created for each request in the server. But the server will not get any chance to destroy these sessions because the session timeout is set to zero.

One of the practical scenarios is when the web application communicates with a mobile application via web service. For instance, suppose the mobile application has a login page, a user is trying to login with an incorrect username or password. In this case, the user will not get logged in but a new session will be created in the server as the request didn't contain a valid JSESSIONID cookie. A server restart cannot save this issue as the server is saving these sessions in to a permanent storage area (based on the server configuration) so the server will never get a chance to destroy/invalidate these sessions.
Now, just think about this case with millions/billions of users trying to login few times with invalid credentials, in a clustered java web application this case is normal (clustered application means : running the same java web application on several parallel servers to handle heavy traffic AKA load balancing, so obviously the end-users may be millions/billions). Specifying the session time out is safe in this case because the server will get a chance to destroy the unused sessions.

How to test creating and destroying sessions with a sample application :-
    For that first we need to understand about web listeners. As we know, there are six types of web listeners available in java web application in the form of interfaces. They are javax.servlet.ServletContextListener, javax.servlet.ServletContextAttributeListener, javax.servlet.http.HttpSessionListener, javax.servlet.http.HttpSessionAttributeListener, javax.servlet.ServletRequestListener and javax.servlet.ServletRequestAttributeListener.

Implement any or all of these interfaces in a class and refer that class in the WEB-INF/web.xml file. For instance, if class com.group.arti.web.core.weblisteners.AppSessionListener implements javax.servlet.http.HttpSessionListener then the following code must be added as the first element in the WEB-INF/web.xml file.

<listener>
        <description>To listener the session life cycle</description>
        <listener-class>com.group.arti.web.core.weblisteners.AppSessionListener</listener-class>
</listener>

What a web listener does is it listens to the web events. For instance, the implementation class of javax.servlet.http.HttpSessionListener will listen to the creation and destruction of HttpSession.
The implemented methods sessionCreated and sessionDestroyed in com.group.arti.web.core.weblisteners.AppSessionListener will be invoked when the session is created and destroyed resp.

Now create a java web application with the sample files and also keep a jsp file testService.jsp under web directory.

Sample web.xml file :-

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

       <listener>
               <description>To listener the session life cycle</description>
               <listener-class>com.group.arti.web.core.weblisteners.AppSessionListener</listener-class>
       </listener>

 <!-- <filter>
<filter-name>appSecurityFilterChain</filter-name>
<filter-class>com.group.arti.web.core.filter.security.AppSecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>appSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> -->

<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>

Sample web listener implementation class :

package com.group.arti.web.core.weblisteners;

import java.util.logging.Logger;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class AppSessionListener implements HttpSessionListener {

private static final Logger LOGGER = Logger.getLogger(AppSessionListener.class.getName());

@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
LOGGER.info("sessionCreated " + httpSessionEvent.getSession().getId());
}

@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
LOGGER.info("sessionDestroyed " + httpSessionEvent.getSession().getId());
}

}

Now start the web application and run the below code to make several requests to the web application. If we check the server console we can see 10 sessions creation has been logged.

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;

public class Test {
public static void main(String[] args) {

try {

// increase numberOfRequests and try
int numberOfRequests = 10;

for (int i = 0; i < numberOfRequests; i++) {

                                //use your web application url
URL url = new URL("http://localhost:8080/check-web-behaviour/testService.jsp");

URLConnection con = url.openConnection();

HttpURLConnection httpUrlConnection = (HttpURLConnection) con;
httpUrlConnection.setReadTimeout(10000);
httpUrlConnection.setConnectTimeout(15000);
httpUrlConnection.setRequestMethod("GET");
httpUrlConnection.setDoInput(true);
httpUrlConnection.setDoOutput(true);

// httpUrlConnection.setRequestProperty("Cookie",
// "JSESSIONID=9550495DDC72AAC410B07B6C16908CB5");

httpUrlConnection.connect();

httpUrlConnection.getInputStream();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}
}