Thursday, December 11, 2008

Servlet Event Listeners

Overview
First introduced as part of version 2.3 of the Servlet specification, application event listeners allow you to receive notifications of certain web application events. The primary goal of providing application events is to allow you to better manage resources at either the context or HTTP session level. For example, you can be notified when a web application has been created and is ready to handle requests and create a pool of objects to be used by servlets. Conversely, when the web application is terminated, you can gracefully destroy the object pool.

Four different types of listeners are defined by the specification:
ServletContextListener Allows notification of context lifecycle events (create and destroy)
ServletContextAttributesListener Allows notification of context attribute changes (add, modify, and remove)
HttpSessionListener Allows notification of HTTP session lifecycle events (create and destroy)
HttpSessionAttributesListeners Allows notification of HTTP session attribute changes (add, modify, and remove)

Each of these listener interfaces can be implemented by a listener object. The object is registered with the web application via entries in the deployment descriptor (web.xml). Next, we’ll take a look at simple examples of each type of listener as well as the corresponding configuration in web.xml.

ServletContextListener
Implementing the javax.servlet.ServletContextListener interface allows you to receive context lifecycle events. Note that there is a one-to-one relationship between a context and a web application, so it is safe to say that context lifecycle events can be viewed as web application lifecycle events as well.
The ServletContextListener is, in my opinion, the most powerful of the event listeners in that it allows you to manage resources that will be used by your web application, such as a JDBC connection pool. When you receive notification that the context has been created, you can create and initialize a pool of JDBC connections, and when the context is destroyed, you can properly tear it down.
Prior to the introduction of the ServletContextListener, many servlet developers would use a preloaded servlet to manage resources. The servlet would be preloaded when the web application started and its init() method would be called, which is where any resource initialization code would reside. When the web application was destroyed, the servlet destroy() method would be invoked and any resources would be destroyed as well. This is still a perfectly acceptable way to handle resources, but using the ServletContextListener is a much cleaner (and more elegant) way to handle context lifecycle events.
Let’s take a look at a simple implementation that simply outputs messages to the servlet log file when context events take place:

package com.omh.listeners;

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

/**
* Simple example of implementing the application lifecycle
* event listener ServletContextListener
*/
public class MyContextListener
implements ServletContextListener
{
public void contextInitialized(ServletContextEvent event)
{
// Grab ahold of the ServletContext
ServletContext context = event.getServletContext();

context.log(context.getServletContextName() + " initialized");
}

public void contextDestroyed(ServletContextEvent event)
{
// Grab ahold of the ServletContext
ServletContext context = event.getServletContext();

context.log(context.getServletContextName() + " destroyed");
}
}
When a web application is started, a ServletContext is created as well. Once the ServletContext has been initialized, the servlet container checks its internal list of listeners. If any listeners implement javax.servlet.ServletContextListener, the contextInitialized() method will be invoked on that listener. A similar process is followed when the web application is destroyed. The following shows how our event listener is configured within web.xml:




com.omh.listeners.MyContextListener



Listeners are simply specified by adding a element with a corresponding entry that specifies the full package name of the class that implements one of the application event listeners. You can specify any number of listeners, and a single class can implement any number of the event listener interfaces. The order in which you specify event listeners in web.xml is important, because the servlet container will invoke the listeners in that same order when events occur. Note that when a context is destroyed, the listeners are invoked in reverse order. Remember that, in order to be fully portable, the elements in web.xml must appear in the same order as they appear in the XML grammar.

Using Tomcat 4.0, the servlet log file will contain the following messages after starting and then stopping the server:
Java Servlets Developer's Guide Listener Examples initialized

Java Servlets Developer's Guide Listener Examples destroyed
Note that you must specify a element within web.xml in order to get the web application name from getServletContextName(). If you don’t specify a , you will get the message “null initialized” instead.

ServletContextAttributeListener

Implementing the javax.servlet.ServletContextAttributeListener interface allows you to receive notification when attributes are added, modified, or removed from the ServletContext for a web application. Perhaps you have a very complex web application that stores state information in the ServletContext and you require notification of certain changes in state. Maybe you allow a servlet to cache information in the context, and have a background thread automatically destroy the cache after a certain amount of time (the thread can be started when the attribute is added). Whatever the reason, implementing the ServletContextAttributeListener gives you fine-grain control over these events.
Again, let’s take a look at an example that simply logs the context attribute events as they happen:
package com.omh.listeners;

import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContext;

/**
* Simple example of implementing the application lifecycle
* event listener ServletContextAttributeListener
*/
public class MyContextAttributeListener
implements ServletContextAttributeListener
{
public void attributeAdded(ServletContextAttributeEvent event)
{
// Grab ahold of the ServletContext
ServletContext context = event.getServletContext();

String attributeName = event.getName();
Object attributeValue = event.getValue();

context.log("Add context attribute " +
attributeName + "=" + attributeValue);
}

public void attributeRemoved(ServletContextAttributeEvent event)
{
// Grab ahold of the ServletContext
ServletContext context = event.getServletContext();

String attributeName = event.getName();
Object attributeValue = event.getValue();

context.log("Remove context attribute " +
attributeName + "=" + attributeValue);
}

public void attributeReplaced(ServletContextAttributeEvent event)
{
// Grab ahold of the ServletContext
ServletContext context = event.getServletContext();

String attributeName = event.getName();
Object oldAttributeValue = event.getValue();
Object attributeValue =
context.getAttribute(attributeName);

context.log("Replace context attribute " +
attributeName + "=" + attributeValue +
" old value=" + oldAttributeValue);
}
}
To see the listener in action, you first need to configure it within web.xml



com.omh.listeners.MyContextListener




and then create a simple servlet that uses context attributes:
package com.omh;

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

/**
* Simple servlet for testing a ServletContextAttributeListener
*/
public class TestContextAttributes
extends HttpServlet
{
public void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
resp.setContentType("text/html");

// Get an output stream that takes into account
// the proper character encoding
PrintWriter out = resp.getWriter();

// Get the context
ServletContext context = getServletContext();

// Set, modify, and delete an attribute
String name = "com.omh.name";

out.println("Setting " + name + " to Bob
");
context.setAttribute(name, "Bob");
out.println("Setting " + name + " to Larry
");
context.setAttribute(name, "Larry");
out.println("Removing " + name);
context.removeAttribute(name);
}
}
Note that the context attribute name I’m using includes my class package name. This is a good idea for two reasons. First, using the package name will greatly reduce the chance that another application will use the same name. Second, it helps you debug problems, because the name will contain the package name of the code that put it there.

Requesting this servlet will cause something like the following to be logged to the servlet log file:
Add context attribute com.omh.name=Bob
Replace context attribute com.omh.name=Larry old value=Bob
Remove context attribute com.omh.name=Larry

No comments:

Post a Comment