|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 5
To really understand filters, you have to see them in action. The first filter we'll look at is simple but powerful; it records
the duration of all requests. It's modeled loosely after the nondescriptively named ExampleFilter from the Tomcat 4.0 distribution. Here is the code:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TimerFilter implements Filter {
private FilterConfig config = null;
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
public void destroy() {
config = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
long before = System.currentTimeMillis();
chain.doFilter(request, response);
long after = System.currentTimeMillis();
String name = "";
if (request instanceof HttpServletRequest) {
name = ((HttpServletRequest)request).getRequestURI();
}
config.getServletContext().log(name + ": " + (after - before) + "ms");
}
}When the server calls init(), the filter saves a reference to the config in its config variable, which is later used in the doFilter() method to retrieve the ServletContext. When the server calls doFilter(), the filter times how long the request handling takes and logs the time once processing has completed. This filter nicely
demonstrates before- and after-request processing. Notice that the parameters to the doFilter()method are not HTTP-aware objects, so to call the HTTP-specific getRequestURI() method requires a cast of the request to an HttpServletRequest type.
To use this filter, you must declare it in the web.xmldeployment descriptor using the <filter>tag, as shown below:
<
filter>
<filter-name>timerFilter</filter-name>
<filter-class>TimerFilter</filter-class>
</filter>This notifies the server that a filter named timerFilteris implemented in the TimerFilterclass. You can apply a registered filter to certain URL patterns or servlet names using the <filter-mapping>tag:
<
filter-mapping>
<filter-name>timerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>This configures the filter to operate on all requests to the server (static or dynamic), just what we want for our timing filter. If you connect to a simple page, the log output might look like this:
2001-05-25 00:14:11 /timer/index.html: 10ms
In Tomcat 4.0 beta 5, you'll find the log file under server_root/logs/.
Download the WAR file for this filter at http://www.javaworld.com/jw-06-2001/Filters/timer.war.
Our next filter is a clickstream filter written by the folks at OpenSymphony. This filter tracks user requests (a.k.a. clicks) and request sequences (a.k.a. clickstreams) to show a site administrator who's visiting her site and what pages each visitor has accessed so far. It's an open source library, under the LGPL license.
Inside the clickstream library you'll find a ClickstreamFilterclass that captures request information, a Clickstream class that operates like a struct to hold data, and a ClickstreamLogger class that captures session and context events to glue everything together. There's also a BotChecker class that determines if a client is a robot (using simple logic, like "Did they request robots.txt?"). To view the data,
the library provides a clickstreams.jsp visitor summary page and a supporting viewstream.jsp visitor detail page.
We'll look first at the ClickstreamFilter class. All these examples are slightly modified from the original, for formatting and to fix portability issues, which I'll
discuss later.
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
public class ClickstreamFilter implements Filter {
protected FilterConfig filterConfig;
private final static String FILTER_APPLIED = "_clickstream_filter_applied";
public void init(FilterConfig config) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Ensure that filter is only applied once per request.
if (request.getAttribute(FILTER_APPLIED) == null) {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
HttpSession session = ((HttpServletRequest)request).getSession();
Clickstream stream = (Clickstream)session.getAttribute("clickstream");
stream.addRequest(((HttpServletRequest)request));
}
// pass the request on
chain.doFilter(request, response);
}
public void destroy() { }
}The doFilter() method gets the user session, obtains the Clickstream from the session, and adds the current request data to the Clickstream. It uses a special FILTER_APPLIED marker attribute to note if the filter was already applied for this request (as might happen during request dispatching)
and to ignore any follow-on filtering action. You might wonder how the filter knows that the clickstreamattribute will be present in the session. That's because the ClickstreamLogger places it there when the session is created. Here is the ClickstreamLoggercode:
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ClickstreamLogger implements ServletContextListener,
HttpSessionListener {
Map clickstreams = new HashMap();
public ClickstreamLogger() { }
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().setAttribute("clickstreams", clickstreams);
}
public void contextDestroyed(ServletContextEvent sce) {
sce.getServletContext().setAttribute("clickstreams", null);
}
public void sessionCreated(HttpSessionEvent hse) {
HttpSession session = hse.getSession();
Clickstream clickstream = new Clickstream();
session.setAttribute("clickstream", clickstream);
clickstreams.put(session.getId(), clickstream);
}
public void sessionDestroyed(HttpSessionEvent hse) {
HttpSession session = hse.getSession();
Clickstream stream = (Clickstream)session.getAttribute("clickstream");
clickstreams.remove(session.getId());
}
}The logger receives application events and uses them to bind everything together. On context creation, the logger places a
shared map of streams into the context. This allows the clickstreams.jsppage to know what streams are currently active. On context destruction, the logger removes the map. When a new visitor creates
a new session, the logger places a new Clickstream instance into the session and adds the Clickstreamto the central map of streams. On session destruction, the logger removes the stream from the central map.