Java Enterpise Platform - Request Observer Listener

Motivation

Whereas observing ServletResponses is non-trivial, Sun makes it much easier to observe ServletRequests by including the ServletRequestListener interface in the J2EE servlet specification. Observing ServletRequests is not only easy, it's also very useful when you want to record or log the request that was made. Recording and logging ServletRequests with a listener is preferable to doing that same work in the requested resource (servlet, JSP, etc.) since the former strategy achieves a separation of concerns that is difficult to achieve with the latter strategy.

This tutorial first describes a reusable, abstract ServletRequestListener that can be extended to observe ServletRequests in myriad meaningful ways. After introducing the abstract observer listener, we'll see that the listener can easily be extended to record ServletRequests to the application container's log file.

The Code

The ServletRequestListener API defines two methods, of which we need only extend the first.


package com.bigohsoftware.common.listener.request;

import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;

public abstract class RequestObserverListener implements ServletRequestListener
{

	protected abstract void doOnServletRequestInitialized(ObservedServletRequest observedServletRequest);

	public void requestInitialized(ServletRequestEvent event)
	{
		ServletRequest request = event.getServletRequest();

		if (request instanceof HttpServletRequest)
		{
			doOnServletRequestInitialized(new ObservedHttpServletRequest((HttpServletRequest) request));
		}
		else
		{
			doOnServletRequestInitialized(new ObservedServletRequest(request));
		}

	}

	public void requestDestroyed(ServletRequestEvent event)
	{
		// do nothing
	}

}
				

You'll notice that the listener class cataloged above makes use of two helper classes that adapt the original ServletRequest into an object that is more easily observed, recorded and logged.


package com.bigohsoftware.common.listener.request;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;

import javax.servlet.ServletRequest;

public class ObservedServletRequest
{

	protected final String requestorIP;
	protected final String requestorHostName;
	protected final int requestedPort;
	protected final String requestedProtocol;
	protected final Date requestReceivedAt;

	public ObservedServletRequest(ServletRequest request)
	{

		requestorIP = request.getRemoteAddr();

		String hostName = request.getRemoteHost();
		try
		{
			if (hostName.equals(request.getRemoteAddr()))
			{
				InetAddress addr = InetAddress.getByName(request.getRemoteAddr());
				hostName = addr.getHostName();
			}
			if (InetAddress.getLocalHost().getHostAddress().equals(request.getRemoteAddr()))
			{
				hostName = "Local Host";
			}
		}
		catch (UnknownHostException e)
		{
			e.printStackTrace();
		}
		requestorHostName = hostName;

		requestedPort = request.getServerPort();

		requestedProtocol = request.getProtocol();
		
		requestReceivedAt = new Date();
	}

	public String toString()
	{
		StringBuffer sb = new StringBuffer();

		sb.append("Received a " + requestedProtocol + " request");
		sb.append(" from " + requestorHostName + " (" + requestorIP + ")");

		return sb.toString();
	}

}
				

package com.bigohsoftware.common.listener.request;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class ObservedHttpServletRequest extends ObservedServletRequest
{

	protected final String httpMethod;
	protected final String resourceRequested;
	protected final String queryString;
	protected final String contextName;
	protected final String containerUserName;
	protected final String jSessionId;

	ObservedHttpServletRequest(HttpServletRequest request)
	{
		super(request);

		httpMethod = request.getMethod();

		resourceRequested = request.getRequestURL().toString();

		queryString = request.getQueryString();

		contextName = request.getContextPath();

		containerUserName = request.getRemoteUser();

		HttpSession session = request.getSession(false);
		jSessionId = (session == null) ? null : session.getId();
	}

	public String toString()
	{
		StringBuffer sb = new StringBuffer();

		sb.append(super.toString());
		sb.append(" for resource " + resourceRequested + " (" + httpMethod + ")");
		sb.append(" --  jSessionId: " + ((jSessionId == null || jSessionId.equals("")) ? "undefined" : jSessionId));
		sb.append(" --  userName: " + ((containerUserName == null || containerUserName.equals("")) ? "unknown" : containerUserName));

		return sb.toString();
	}

}
				
An Example: Request Logger Listener

The abstract listener class described above can be extended to provide many different types of behavior when a ServletRequest is observed. For example, we might define a concrete listener that persists request details to a database or we might define a concrete listener that logs the details of the request to the application container's log file. The code below implements the latter example.


package com.bigohsoftware.common.listener.request;

public class RequestLoggerListener extends RequestObserverListener
{

	@Override
	protected void doOnServletRequestInitialized(ObservedServletRequest observedServletRequest)
	{
		System.out.println(observedServletRequest);
	}

}
				

By applying the above logging listener to a web application, you'll see that new ServletRequests are logged in this format:
Received a HTTP/1.1 request from localhost (127.0.0.1) for resource http://127.0.0.1:8080/CommonWeb/ (GET) -- jSessionId: B46435C89F9FABCE571AF701E0BC6820 -- userName: unknown