practical example of listeners

In this post, you will learn:

  • How to define listeners?
  • How to register listeners with the server?
  • How to use both listeners & servlets in a web application?

A scenario for the use of listeners in a web application

Usually each web application has two types of interfaces, one for the users and other for the admin. Users are interested in the information or services provided by the application and the admin is interested in the statistics such as how many users are using the service, which pages are more frequently viewed by the users etc.

Lets assume that we need to create a web application in which we need to present current & total user count to the admin. In order to provide this information to the admin, we need to solve following questions:

1. Where these counters should be stored in the application?

As you know, there are three scopes: request, session and application to store information as attributes. This information should be maintained as long as the application is running hence it should be stored in the application scope i.e. in the ServletContext object.

2. When these counters should be stored in the ServletContext?

These counters need to be stored only once before any user sends a request hence they should be stored at the time of deployment. For this contextInitialized() method of ServletContextListener can be used because it is invoked by the server only once just after the application is deployed.

3. When these counters should be incremented and decremented?

When a user logs in, both the counters should be incremented and when he logs out, current counter should be decremented. Total user counter only increases but the current user counter increases when users come and decreases when they go.

4. How these counters should be incremented and decremented?

There are two ways of incrementing and decrementing these counters in the application. First, there should be entry and exit points of users in the application i.e. there should be servlets for login and logout of users. We can increment the counters from the login servlet and decrement the current counter from the logout servlet. It is the wrong approach because:

a) – Performing more than one tasks in a component decreases its cohesiveness which results in maintenance problem. If in future these counters are to be disabled then the servlets need to be modified.

b) – Accurate information can’t be provided to the admin because if a user logs in but doesn’t logs out i.e. closes his browser after obtaining the required information then current counter will not decrease.

Second, we can use the sessionCreated() method of HttpSessionListener for incrementing the counters and sessionDestroyed() method for decrementing the current counter. This approach is the correct one because it doesn’t have any of the problems of the first approach. Counters can be disabled in the future, by removing the listeners configuration from the web.xml. If a user doesn’t logs out then his session will time out which will also result in the invocation of sessionDestroyed() method.

From these questions and answers, we have come to know that we need to implement ServletContextListener for setting the counters in the application scope at the time of deployment and HttpSessionListener for incrementing and decrementing the counters when session are created and destroyed or timed out.

Following diagram describes the page flow the application:

listener_app_page_flow

Description of the diagram:

1.0 – Web application having servlets, HTML pages and ServletContextListener & HttpSessionListner is deployed on the server.

1.1 – ServletContext object is created for the application by the server.

1.2 – ServletContextListener cum HttpSessionListener object is created by the server and its contextInitialized() method is invoked.

1.3 – total & current counters are set in ServletContext by the contextInitialized() method of ServletContextListener.

2.0 – A request is submitted by a user for the entryServlet.

2.1 – HttpSession object is created for the user by the entryServlet which triggers the HttpSessionEvent and sessionCreated() method of the HttpSessionListener is invoked by the server.

2.2 – Both the counters are incremented by the sessionCreated() method of the HttpSessionListener.

2.3 – Response is sent to the user by the entryServlet.

3.0 – Request is submitted by the user for the exitServlet.

3.1 – HttpSession object of the user is destroyed by the exitServlet which triggers the HttpSessionEvent and sessionDestroyed() method of the HttpSessionListener is invoked by the server.

3.2 – Current counter is decremented by the sessionDestroyed() method of the HttpSessionListener.

3.3 – Response is sent to the user by the exitServlet.

4.0 – A request is submitted by the admin for the adminServlet.

4.1 – Both the counters are fetched from the ServletContext by the adminServlet.

4.2 – User counters are presented to the admin as response by the adminServlet.

Source code of the practical example of listeners:

First, the index.html :

<form method="post" action="entryServlet">
Name <input type="text" name="name"> <br/>
<input type="submit" value="submit">
</form>

Second, the CounterListener :

package com.techmentro.learningpad;

import javax.servlet.*;
import javax.servlet.http.*;


public class CounterListener implements HttpSessionListener, 
ServletContextListener {

	//A data member of type ServletContext is added so that ServletContext can be made available in all the methods
	ServletContext ctx;

	@Override
	public void contextInitialized(
			ServletContextEvent e) {
		//Reference of ServletContext is obtained from the ServletContextEvent object and stored in the data member of the listener
		//In servlets, ServletContext is obtained from the ServletConfig which can't be used in the listener because a listener doesn't 
		//have ServletConfig object.
		ctx=e.getServletContext();
		//current & total counters are set in the ServletContext.
		ctx.setAttribute("total",0);
		ctx.setAttribute("current", 0);

	}
	@Override
	public void sessionCreated(HttpSessionEvent e) {
		//Both the counters are fetched from the ServletContext, incremented and reset.
		Integer t=(Integer)ctx.getAttribute("total");
		t++;
		ctx.setAttribute("total",t);
		Integer c=(Integer)ctx.getAttribute("current");
		c++;
		ctx.setAttribute("current",c);
        System.out.println("Session created, "
        		+ "counters incremented.");
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent e) {
		//Curent counter is fetched from the ServletContext, decremented and reset.
		Integer c=(Integer)ctx.getAttribute("current");
		c--;
		ctx.setAttribute("current",c);
		System.out.println("Session destroyed, "
				+ "current counter decremented.");
	}
	
	@Override
	public void contextDestroyed(
			ServletContextEvent e) {}
}

Third, the EntryServlet:

package com.techmentro.learningpad;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;


public class EntryServlet extends HttpServlet 
{
	public void doPost(HttpServletRequest request, 
			HttpServletResponse response)
			throws ServletException, IOException {
		String user=request.getParameter("name");
		response.setContentType("text/html");
		PrintWriter out=response.getWriter();
		out.println("Welcome, "+user);
		//HttpSessionEvent will be triggered.
		HttpSession session=request.getSession();
		session.setAttribute("user", user);
out.println("<br> exit");
		out.close();
	}
}

Fourth, the ExitServlet:

package com.techmentro.learningpad;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;


public class ExitServlet extends HttpServlet 
{
	public void doGet(HttpServletRequest request, 
			HttpServletResponse response)
			throws ServletException, IOException {
		HttpSession session=request.getSession();
		String user=(String)session.getAttribute("user");
		//HttpSessionEvent will be triggered.
		session.invalidate();
		response.setContentType("text/html");
		PrintWriter out=response.getWriter();
	out.println(user+", you have successfully exited.");
out.println("<br> try again");
	out.close();
	}
}

Fifth, the AdminServlet:

package com.techmentro.learningpad;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class AdminServlet extends HttpServlet 
{
	public void doGet(HttpServletRequest request, 
			HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter out=response.getWriter();
		out.println("Welcome, Admin");
		out.println("<br> Counters are:");
		//Reference of the ServletContext is otained form the ServletConfig
		ServletContext ctx=	getServletConfig().getServletContext();
		//Both the counters are fetched.
		Integer c=(Integer)ctx.getAttribute("current");
		Integer t=(Integer)ctx.getAttribute("total");
		out.println("<br> Total Users: "+t);
		out.println("<br> Current Users: "+c);
out.println("<br> refresh");
		out.close();
	}
}

Sixth, the web.xml. It has <listener> element for registering the listener to the server and <session-config> element to set session timeout.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
 
 
 <!--To register a listener to the server, following element need to be added to the web.xml  -->
 <listener>
 <listener-class>com.techmentro.learningpad.CounterListener</listener-class>
 </listener>
 
 <servlet>
 <servlet-name>s1</servlet-name>
 <servlet-class>com.techmentro.learningpad.EntryServlet</servlet-class>
 </servlet>
 <servlet-mapping>
 <servlet-name>s1</servlet-name>
 <url-pattern>/entryServlet</url-pattern>
 </servlet-mapping>
 <servlet>
 <servlet-name>s2</servlet-name>
 <servlet-class>com.techmentro.learningpad.ExitServlet</servlet-class>
 </servlet>
 <servlet-mapping>
 <servlet-name>s2</servlet-name>
 <url-pattern>/exitServlet</url-pattern>
 </servlet-mapping>
 <servlet>
 <servlet-name>s3</servlet-name>
 <servlet-class>com.techmentro.learningpad.AdminServlet</servlet-class>
 </servlet>
 <servlet-mapping>
 <servlet-name>s3</servlet-name>
 <url-pattern>/adminServlet</url-pattern>
 </servlet-mapping>
 
 <!-- Session timeout is set to 2 minutes i.e. if no request is received from a user for 2 minutes, his session will be destroyed.-->
 <session-config>
 <session-timeout>2</session-timeout>
 </session-config>
</web-app>

To test this application use following steps:

1. After deploying the application on a web server, send a request for the adminServlet using following url: http://localhost:8080/counterApp/adminServlet. If port no of your server is different, then use it in place of 8080. You will receive the response having current & total user count 0.

2. From two different browsers, request the home page by using the following url: http://localhost:8080/counterApp. Using the input form of the home page, send requests to the entryServlet from both the browsers.

3. Resend the request to the adminServlet using the refresh hyper link in its response page. This time value of both current and total users will be 2.

4. Exit the first user using the exit hyper link and recheck the counters. This time total will be 2 and current will be 1. Repeat the same step for the second user.

5. Send another request for the entry servlet and close the browser without exiting. Check the counters, total should be 3 and current should be 1. Recheck the counters after 2 minutes, total will be 3 and current will be 0 because of session time out.


If you like the post, then share it...Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

Leave a Reply

Your email address will not be published. Required fields are marked *