ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Servlet App Event Listeners

by Stephanie Fesler
04/12/2001

Related Reading

Java Servlet Programming, 2nd EditionJava Servlet Programming, 2nd Edition
By Jason Hunter with William Crawford
Table of Contents
Index
Sample Chapter
Full Description

Sun Microsystems released the proposed final draft of the Servlet 2.3 specification on October 20, 2000. Although the Servlet 2.3 specification is not in its final version yet, we do not have to wait any longer to play with the exciting new features defined by it. Apache's Tomcat has released a build, version 4.0 beta 1 that fully supports the Servlet 2.3 proposed final draft specification.

My last article was an introduction to Servlet concepts (for those new to this area) and then it was an overview of two new additions to the specification, the application lifecycle events and Servlet filters. This article is going to go into detail about writing the application lifecycle events.

Working Environment

All the code that will be shown in this article has been tested on Apache's Tomcat version 4.0 beta 1 on a Windows platform. This version of Tomcat has full support of the new Servlet 2.3 specification and the JSP 1.2 specification. If you need help installing and setting up Tomcat refer to James Goodwill's article Installing and Configuring Tomcat for an easy to follow guide on installing and configuring Apache's Tomcat version 4.0 beta 1.

All the code is part of a Web Application called demo.

Overview of Application Lifecycle Events

The Servlet 2.3 proposed final draft specification has defined application lifecycle events to provide Web Application developers more interaction with the ServletContext object and HttpSession objects. Web Application developers write event listeners so they can now be notified when lifecycle events happen (such as creation or destruction) or when attributes are modified in the ServletContext object or HttpSession objects.

Event listeners are Java classes that follow the JavaBeans design and are provided by the Web Application developer in the Web archive (.war) file. There are two types of event listeners, and both types apply to the ServletContext object and HttpSession objects. The two types are lifecycle events and changes to attributes events. Table 1 shows the types of events, a brief description, and the listener interface to implement. This table is taken from the Servlet 2.3 proposed final draft specification.

Table 1: Supported Event Types
Event Type Description Listener Interface
Servlet Context Events
Lifecycle The Servlet context has just been created and is available to service its first request, or the Servlet context is about to be shutdown. javax.Servlet.ServletContextListener
Changes to Attributes Attributes on the Servlet context has been added, removed, or replaced. javax.Servlet.ServletContextAttributesListener
Http Session Events
Lifecycle An HttpSession has just been created, or has been invalidated or timed out. javax.Servlet.http.HttpSessionListener
Changes to Attributes Attributes have been added, removed or replaced in an HttpSession object. javax.Servlet.http.HttpSessionAttributesListener

An interesting point is that there can be multiple listener classes listening to each event type and the Web application developer has the flexibility to define the order the event listener objects are invoked.

The container manages the lifecycle of event listeners. It is the containers responsibility to instantiate each of the listener classes in a Web Application before the execution of the first request into the Web Application. Also, each of the listener classes must be referenced until the Web Application services the last request.

For more details on the container's responsibilities, refer to my last article.

Writing a Simple Servlet Context Application Lifecycle Event Listener

When a web application is deployed a servlet context object, ServletContext, is created and associated with the web application. There is a one-to-one relationship between a servlet context object and the web application. All resources within the web application, such as servlets and JSPs, can retrieve any information stored in the servlet context.

As a web application programmer, you may want to initialize objects and place them in the servlet context when it is created and destroy the objects when the servlet context is destroyed. For example, you may decide to create a connection to a database when the servlet context is created and close the connection when the servlet context is destroyed.

To write an application lifecycle event listener that executes when the servlet context is created and destroyed, write a Java class that implements the javax.Servlet.ServletContextListener class. This class has two methods with the following signatures (taken from the JavaDocs):

void contextDestroyed (ServletContextEvent sce)
   Notification that the Servlet context is about to be shut down.

void contextInitialized (ServletContextEvent sce)
   Notification that the Web Application is ready to process requests.

Let's write a simple application lifecycle event listener that writes a message to the console of the server when the web application is ready to accept requests and when it is going to be removed. Listing 1 shows the code for a simple application event listener class.


Listing 1: An application lifecycle event listener that outputs messages to the console when the web application is ready to service requests and when it is going to be removed. (MyContextListener.java)

package com.listeners;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributesListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public final class MyContextListener
    implements ServletContextListener {

  private ServletContext context = null;

  public MyContextListener() {}

  //This method is invoked when the Web Application
  //has been removed and is no longer able to accept
  //requests

  public void contextDestroyed(ServletContextEvent event)
  {

    //Output a simple message to the server's console
    System.out.println("The Simple Web App. Has Been Removed");
    this.context = null;

  }


  //This method is invoked when the Web Application
  //is ready to service requests

  public void contextInitialized(ServletContextEvent event)
  {
    this.context = event.getServletContext();

    //Output a simple message to the server's console
    System.out.println("The Simple Web App. Is Ready");

  }
}


Deploying a Simple Servlet Context Application Lifecycle Event Listener

Application lifecycle event listeners are deployed within the web archive (.war) file and are defined in the deployment descriptor, web.xml, for the web application. The lifecycle event listeners are defined using <listener> tags. The order you define the listener classes does matter;, they will be executed in the order you specify. Listing 2 shows the web.xml file that defines the application lifecycle event listener that was shown in Listing 1.


Listing 2: The web.xml file that defines the application lifecycle event listener listed above (MyContextListener.java)

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

   <!-- Define application events listeners -->
  <listener>
    <listener-class>
     com.listeners.MyContextListener
    </listener-class>
  </listener>

  <!-- Define servlets that are included in the example application -->
  <servlet>
    <servlet-name>
     simple
    </servlet-name>
    <servlet-class>
     com.servlets.SimpleServlet
    </servlet-class>
  </servlet>

  <servlet>
   <servlet-name>
   bank
   </servlet-name>
   <servlet-class>
   com.servlets.BankBalance
   </servlet-class>
  </servlet>

  <!-- Define servlet mappings to urls -->
  <servlet-mapping>
    <servlet-name>
     simple
    </servlet-name>
    <url-pattern>
     /simple
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
   <servlet-name>
   bank
   </servlet-name>
   <url-pattern>
   /bank
   </url-pattern>
  </servlet-mapping>
</web-app>


There are a few items to notice. The listener tag comes before the servlet definitions in the web.xml file. Also, the <listener> tag has a child element, <listener-class>, that defines the application lifecycle event listener.

Figure 1 shows the console for Tomcat when it starts up and the above web application is deployed.

Screen shot.
Figure 1. Tomcat's console when it starts up.

Figure 2 is a snapshot of the directory structure to give you a good idea of how the demo web application is set up.

Screen shot.
Figure 2. A snapshot of Tomcat's working directory.

Listening For Changes To The Servlet Context

The Servlet 2.3 specification allows for more interaction between the web application programmer and ServletContext. The programmer can now write an application lifecycle event listener that executes when the attributes of the ServletContext object have been modified. If you want to execute some code when the attributes of the ServletContext object has been modified, write a Java class that implements the javax.Servlet.ServletContextAttributesListener interface. This interface defines three methods with the following signatures (this is taken from the JavaDocs):

void attributeAdded (ServletContextAttributeEvent scab)
  Notification that a new attribute was added to the servlet context.

void attributeRemoved (ServletContextAttributeEvent scab)
  Notification that an existing attribute has been removed from the servlet context.

void attributeReplaced (ServletContextAttributeEvent scab)
  Notification that an attribute on the servlet context has been replaced.

Listing 3 is an example of writing an application event listener for getting notifications of changes to the ServletContext object.


Listing 3: An application event listener that writes messages to the console when attributes of the ServletContext object have been modified. (ServletContextAttribListener.java)

package com.listeners;


import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributesListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;


public class ServletContextAttribListener
    implements ServletContextAttributesListener {


  //This method is invoked when an attribute
  //is added to the ServletContext object
  public void attributeAdded (ServletContextAttributeEvent scab)
  {
    System.out.println("An attribute was added to the " +
      "ServletContext object");
  }

  //This method is invoked when an attribute
  //is removed from the ServletContext object
  public void attributeRemoved (ServletContextAttributeEvent scab)
  {
    System.out.println("An attribute was removed from " +
      "the ServletContext object");
  }

  //This method is invoked when an attribute
  //is replaced in the ServletContext object
  public void attributeReplaced (ServletContextAttributeEvent scab)
  {
    System.out.println("An attribute was replaced in the " +
      "ServletContext object");
  }

}


This code is well and good, but what about testing it? Listing 4 is for an HTML document that allows the user to add name-value pairs as an object to the ServletContext object. You can also remove items and replace items in the ServletContext object. Listing 5 is the code for the Servlet that parses the request from the HTML document and actually does the manipulation of the ServletContext object.


Listing 4: An HTML document that allows users to play with the ServletContext object. (servletcontextattrib.html)

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<title>Working With Attributes Of the ServletContext Object</title>
</head>

<body>
<center>
<font size="6" color="navy">
  Playing With The ServletContext Object
</font>
</center>

<form name="MyForm" action="./servletcontextattrib" method="POST">
<table width="75%" bgcolor="silver" align="center">

  <tr><td rowspan="3">Select an Action</td>
    <td><input type="radio" name="action" value="add">
      Add Item To ServletContext
    </td>
  </tr>
  <tr>
    <td><input type="radio" name="action" value="remove">
      Remove Item From ServletContext
    </td>
  </tr>
  <tr>
    <td><input type="radio" name="action" value="replace">
      Replace Item In ServletContext
    </td>
  </tr>

  <tr><td>Servlet Context Attribute Name</td>
    <td><input type="text" name="name" value=""></td>
  </tr>
  <tr><td>Servlet Context Attribute Value</td>
    <td><input type="text" name="value" value=""></td>
  </tr>

  <tr><td colspan="2" align="center">
    <input type="submit" name="btnSubmit" value="Submit">
    </td>
  </tr>
</table>
</form>

</body>
</html>


h


Listing 5: The code for the servlet that takes the HTML form information and does the actual manipulation of the ServletContext object. (ServletContextAttrib.java)

package com.servlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class ServletContextAttrib extends HttpServlet
{
  public void service(HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException
  {
    response.setContentType("text/html");
    ServletOutputStream out = response.getOutputStream();

    out.print("<?xml version='1.0' encoding='UTF-8'?>");

    out.print("<!DOCTYPE html");
    out.print("PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'");
    out.print("'DTD/xhtml1-strict.dtd'>");

    out.print("<html>");
    out.print("<head>");
    out.print("<title>ServletContext Attributes</title>");
    out.print("</head>");
    out.print("<body>");

    ServletContext context = getServletContext();

    String action = request.getParameter("action");

    String name = request.getParameter("name");
    String value = request.getParameter("value");

    if (action == null) {}

    else {
      if (action.equals("add"))
      {
        String test = (String) context.getAttribute(name);
        if (test == null)
        {
          context.setAttribute(name, value);
          out.print("Added Item To ServletContext object");
        } else {
          context.setAttribute(name, value);
          out.print("Replaced Item in ServletContext");
        }
      }

      else if (action.equals("remove"))
      {
        String test = (String) context.getAttribute(name);
        if (test == null) {
          out.print("Item does not exist");
        } else {
          context.removeAttribute(name);
          out.print("Removed Item From ServletContext");
        }
      }

      else
      {
        String test = (String) context.getAttribute(name);
        if (test == null)
        {
          context.setAttribute(name, value);
          out.print("Added Item To ServletContext object");
        } else {
          context.setAttribute(name, value);
          out.print("Replaced Item in ServletContext");
        }
      }
    }

    out.print("<center> <br /> <br />");
    out.print("<a href='./servletcontextattrib.html'>");
    out.print("Back To Home Page");
    out.print("</a>");
    out.print("</center>");

    out.print("</body>");
    out.print("</html>");

  }
}


Deploying All Of The Above Listeners and Servlets


Listing 6: The deployment descriptor, web.xml, which contains the definitions for the listener classes and Servlet defined above.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

   <!-- Define application events listeners -->
  <listener>
    <listener-class>
      com.listeners.MyContextListener
    </listener-class>
  </listener>

  <listener>
    <listener-class>
      com.listeners.ServletContextAttribListener
    </listener-class>
  </listener>


  <!-- Define servlets that are included in the example application -->
  <servlet>
    <servlet-name>
      simple
    </servlet-name>
    <servlet-class>
      com.servlets.SimpleServlet
    </servlet-class>
  </servlet>

  <servlet>
    <servlet-name>
      bank
    </servlet-name>
    <servlet-class>
      com.servlets.BankBalance
    </servlet-class>
  </servlet>

  <servlet>
    <servlet-name>
      context attribs
    </servlet-name>
    <servlet-class>
      com.servlets.ServletContextAttrib
    </servlet-class>
  </servlet>

  <!-- Define servlet mappings to urls -->
<servlet-mapping>
    <servlet-name>
      simple
    </servlet-name>
    <url-pattern>
      /simple
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>
      bank
    </servlet-name>
    <url-pattern>
      /bank
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>
      context attribs
    </servlet-name>
    <url-pattern>
      /servletcontextattrib
    </url-pattern>
  </servlet-mapping>

</web-app>


Working With Session Information

The application event listeners can also make notifications when an HttpSession object is created, destroyed, or has its attributes modified. This is another way the Servlet 2.3 specification is empowering the web application programmer.

It is possible to write one application event listener that handles all notifications for changes to the HttpSession objects. This could be done with ServletContext notifications, but the earlier examples kept them separate to show that method.

To receive lifecycle notifications for HttpSession objects write a Java class that implements the javax.Servlet.http.HttpSessionListener interface. This interface defines two methods and their signatures are (taken from the JavaDocs):

void sessionCreated (HttpSessionEvent se)
  Notification that a session was created.

void sessionDestroyed (HttpSessionEvent se)
  Notification that a session was invalidated.

To receive notifications that the attributes of an HttpSession object have been modified implement the javax.servlet.http.HttpSessionAttributesListener interface. This interface defines three methods and their signatures are (taken from the JavaDocs):

void attributeAdded (HttpSessionBindingEvent se)
  Notification that an attribute has been added to a session.

void attributeRemoved (HttpSessionBindingEvent se)
  Notification that an attribute has been removed from a session.

void attributeReplaced (HttpSessionBindingEvent se)
  Notification that an attribute has been replaced in a session.

Listing 7 is an example of an application event listener that receives all notifications of HttpSession objects; whether the HttpSession object was created or destroyed and if an attribute was added or removed.


Listing 7: An application event listener that handles all notifications of HttpSession objects. (MySessionListener.java)

package com.listeners;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionAttributesListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;



public final class MySessionListener
  implements HttpSessionAttributesListener, HttpSessionListener {


  public void sessionCreated(HttpSessionEvent event) {

    System.out.println("HttpSession object has been created");

  }


  public void sessionDestroyed(HttpSessionEvent event) {

    System.out.println("HttpSession object has been removed");

  }

  public void attributeAdded(HttpSessionBindingEvent event) {

    System.out.println("An attribute has been added " +
      "to an HttpSession object");

  }



  public void attributeRemoved(HttpSessionBindingEvent event) {

    System.out.println("An attribute has been removed " +
      "to an HttpSession object");

  }


  public void attributeReplaced(HttpSessionBindingEvent event) {

    System.out.println("An attribute has been replaced " +
      "to an HttpSession object");

  }
}


It's good to see the code for the application event listener, but let's test it. Listing 8 is the code for a Servlet that maintains a "bank balance" for an individual in an HttpSession object. You can withdraw and deposit money into the account, in increments of $50.


Listing 8: A Servlet that works with an HttpSession object. (BankBalance.java)

package com.servlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class BankBalance extends HttpServlet
{
  public void service(HttpServletRequest request,
            HttpServletResponse response)
            throws IOException, ServletException
  {
    response.setContentType("text/html");
    ServletOutputStream out = response.getOutputStream();

    out.print("<?xml version='1.0' encoding='UTF-8'?>");

    out.print("<!DOCTYPE html");
    out.print("PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'");
    out.print("'DTD/xhtml1-strict.dtd'>");

    out.print("<html>");
    out.print("<head>");
    out.print("<title>Maintaining Session State</title>");
    out.print("</head>");
    out.print("<body>");

    out.print("<center>");

    HttpSession session = request.getSession();

    String action = request.getParameter("action");

    Double balance = (Double) session.getAttribute("Balance");
    if (balance == null)
    {
      balance = new Double(500);
      session.setAttribute("Balance", balance);
      out.print(balance.doubleValue());
      out.print("<br />");

    } else {
      double bal = balance.doubleValue();

      if (action.equals("withdraw"))
      {
        bal -= 50;
      } else {
        bal += 50;
      }

      balance = new Double(bal);
      session.setAttribute("Balance", balance);

      out.print("Your new balance is: ");
      out.print(bal);
      out.print("<br />");

    }


    out.print("<a href='./bank?action=deposit'>");
    out.print("Deposit $50");
    out.print("</a>");

    out.print("<br />");

    out.print("<a href='./bank?action=withdraw'>");
    out.print("Withdraw $50");
    out.print("</a>");

    out.print("</center>");

    out.print("</body>");
    out.print("</html>");

  }
}

The web.xml deployment descriptor that defines this Servlet and the application event listener is shown in Listing 9.


Listing 9: The web.xml deployment descriptor showing the BankBalance Servlet definition and the MySessionListener application event listener definition.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">

<web-app>

   <!-- Define application events listeners -->
  <listener>
    <listener-class>
      com.listeners.MyContextListener
    </listener-class>
  </listener>

  <listener>
    <listener-class>
      com.listeners.ServletContextAttribListener
    </listener-class>
  </listener>

  <listener>
    <listener-class>
      com.listeners.MySessionListener
    </listener-class>
  </listener>


  <!-- Define servlets that are included in the example application -->
  <servlet>
    <servlet-name>
      simple
    </servlet-name>
    <servlet-class>
      com.servlets.SimpleServlet
    </servlet-class>
  </servlet>

  <servlet>
    <servlet-name>
      bank
    </servlet-name>
    <servlet-class>
      com.servlets.BankBalance
    </servlet-class>
  </servlet>

  <servlet>
    <servlet-name>
      context attribs
    </servlet-name>
    <servlet-class>
      com.servlets.ServletContextAttrib
    </servlet-class>
  </servlet>

  <!-- Define servlet mappings to urls -->
<servlet-mapping>
    <servlet-name>
      simple
    </servlet-name>
    <url-pattern>
      /simple
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>
      bank
    </servlet-name>
    <url-pattern>
      /bank
    </url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>
      context attribs
    </servlet-name>
    <url-pattern>
      /servletcontextattrib
    </url-pattern>
  </servlet-mapping>

</web-app>

Summary

This article shows you how to write application event listeners to handle notifications about changes to the ServletContext object and HttpSession objects. Application event listeners give web application programmers greater control over the ServletContext and HttpSession objects. Although the examples in this article were simplistic, they lay the groundwork for examining these event listeners. Now that you can write listener classes, adding more advanced programming within the notification methods is easy.

Stephanie Fesler is a BEA Systems expert on implementing various Java 2EE API.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.