[ Team LiB ] Previous Section Next Section

An Example Filter—Session Logging

Another useful application of filtering is the logging of incoming requests for later playback. You may occasionally want to take a snapshot of what users are doing on your system so you can simulate actual user activity for load testing and capacity planning.

The recording process is very simple. For each different session, you generate a unique filename and store that filename in the session. (If a session doesn't contain a filename, you know that you are starting a new recording.)

For each incoming request, write out the request parameters to the session recording, along with the name of the JSP or servlet. When the session is invalidated, either due to a timeout or being explicitly invalidated, the recording is closed out.

Listing 7.5 shows the session recording filter.

Listing 7.5 Source Code for SessionRecorder.java
package examples.filter;

import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Enumeration;
import java.io.PrintWriter;
import java.io.FileWriter;
import java.io.BufferedWriter;

public class SessionRecorder implements Filter
{
    public static final String RECORDING_FILE = "RECORDING_FILE";

    public static HashMap times = new HashMap();
    public static int recordingId;
    public static String recordingPrefix;
    public FilterConfig config;

    public SessionRecorder()
    {
    }

    public void init(FilterConfig filterConfig)
    {
        // Grab the destination of the recording files from
        // the filter configuration. The prefix should contain
        // a directory name and the beginning of a filename.
        // The rest of the filename will be a sequence number
        // and ".xml".
        recordingPrefix = filterConfig.getInitParameter("prefix");
        config = filterConfig;
    }

    public void destroy()
    {
    }

    public void doFilter(ServletRequest request,
            ServletResponse response, FilterChain chain)
        throws java.io.IOException, ServletException
    {
        // Ignore non-http requests.
        if (!(request instanceof HttpServletRequest))
        {
            chain.doFilter(request,response);
            return;
        }

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        request.getSession();

        // Execute the JSP/servlet or chained filter.
        chain.doFilter(request, response);

        // Write the request out to the recording file.
        recordRequest((HttpServletRequest) request,
                (HttpServletResponse) response);
    }

    public synchronized static int getNextRecordingId()
    {
        return recordingId++;
    }

    public String generateRecordingFilename()
    {
        return recordingPrefix+getNextRecordingId()+".xml";
    }

    public PrintWriter openRecordingFile(String filename)
    {
        try
        {
            PrintWriter out = new PrintWriter(new BufferedWriter(
                        new FileWriter(filename, true)));
            return out;
        }
        catch (Exception exc)
        {
            config.getServletContext()
                        .log("Error opening recording file: ", exc);
            return null;
        }
    }

    public void recordRequest(HttpServletRequest request,
            HttpServletResponse response)
    {
        HttpSession session = request.getSession();

        // Get the recording file name.
        RecordingFile recordingFile =
            (RecordingFile) session.getAttribute(RECORDING_FILE);

        // If there is no recording file, create a new one.
        if (recordingFile == null)
        {
            recordingFile = new RecordingFile(generateRecordingFilename());
            session.setAttribute(RECORDING_FILE, recordingFile);
            initializeRecordingFile(recordingFile.filename);
        }

        // Write the request parameters and URI to the file.
        try
        {
            PrintWriter out = openRecordingFile(recordingFile.filename);
            if (out == null) return;

            out.println("<request>");
            out.print("<uri>");
            out.print(request.getRequestURI());
            out.println("</uri>");
            Enumeration e = request.getParameterNames();
            while (e.hasMoreElements())
            {
                String paramName = (String) e.nextElement();
                String[] values = request.getParameterValues(paramName);
                for (int i=0; i < values.length; i++)
                {
                    out.print("<param><name>");
                    out.print(paramName);
                    out.print("</name><value>");
                    out.print(values[i]);
                    out.println("</value></param>");
                }
            }
            out.println("</request>");
            out.close();
        }
        catch (Exception exc)
        {
            config.getServletContext()
                        .log("Error appending to recording file: ", exc);
        }
    }

    public void initializeRecordingFile(String filename)
    {
        try
        {
            PrintWriter out = openRecordingFile(filename);
            if (out == null) return;
            out.println("<?xml version=\"1.0\"?>");
            out.println("<session>");
            out.close();
        }
        catch (Exception exc)
        {
            config.getServletContext()
                        .log("Error initializing recording file: ", exc);
        }
    }

    public void finishRecordingFile(String filename)
    {
        try
        {
            PrintWriter out = openRecordingFile(filename);
            if (out == null) return;
            out.println("</session>");
            out.close();
        }
        catch (Exception exc)
        {
            config.getServletContext()
                        .log("Error finishing recording file: ", exc);
        }
    }

    class RecordingFile implements HttpSessionBindingListener
    {
        public String filename;

        public RecordingFile(String aFilename)
        {
            filename = aFilename;
        }

        public void valueBound(HttpSessionBindingEvent event)
        {
        }

        public void valueUnbound(HttpSessionBindingEvent event)
        {
            // When the session terminates, this method is invoked.
            // Close out the recording file by writing the closing tag.
            finishRecordingFile(filename);
        }
    }
}

The deployment descriptor for this application is shown in Listing 7.6.

Listing 7.6 Source Code for web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
    <display-name>Show Times</display-name>
    <description>An application to demonstrate the use of a filter
    </description>

    <filter>
       <filter-name>Session Recorder</filter-name>
       <filter-class>examples.filter.SessionRecorder</filter-class>
       <init-param>
           <param-name>prefix</param-name>
           <param-value>/tmp/sessions</param-value>
       </init-param>
    </filter>

    <filter-mapping>
       <filter-name>Session Recorder</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

Remember to change the <init-param> of the session recorder filter to a directory suitable to your configuration.

Listing 7.7 shows the recording of a request to a simple JSP.

Listing 7.7 Example Recorded Session
<?xml version="1.0"?>
<session>
<request>
<uri>/sessionrecorder/postit.jsp</uri>
<param><name>age</name><value>10</value></param>
<param><name>age</name><value>7</value></param>
<param><name>name</name><value>Samantha</value></param>
<param><name>name</name><value>Kaitlynn</value></param>
</request>
</session>

Information like this can be very useful in debugging applications or for security purposes. In addition, with a complementary application that enables you to "play-back" the session, you can perform automated regression or load testing.

    [ Team LiB ] Previous Section Next Section