ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Two Servlet Filters Every Web Application Should Have

by Jayson Falkner
11/19/2003

Almost every single web application you will ever make will seriously benefit from using servlet filters to both cache and compress content. A caching filter optimizes the time it takes to send back a response from your web server, and a compression filter optimizes the size of the content that you send from your web server to a user via the Internet. Since generating content and sending content over the World Wide Web are the bread and butter of web applications, it should be no surprise that simple components that aid in these processes are incredibly useful. This article details the process of building and using a caching filter and a compression filter that are suitable for use with just about any web application. After reading this article, you will understand caching and compressing, have code to do both, and be able to apply caching and compression to any of your future (or existing!) web applications.

Review: Servlet Filters in 10 Sentences

Servlet filters are powerful tools that are available to web application developers using the Servlet 2.3 specification or above. Filters are designed to be able to manipulate a request or response (or both) that is sent to a web application, yet provide this functionality in a method that won't affect servlets and JSPs being used by the web application unless that is the desired effect. A good way to think of servlet filters is as a chain of steps that a request and response must go through before reaching a servlet, JSP, or static resource such as an HTML page in a web application. Figure 1 shows the commonly used illustration of this concept.

Figure 1
Figure 1. The servlet filter concept

The large gray box is a web application that has some endpoints, such as JSP, and some filters applied to intercept all requests and responses. The filters are shown in a stack, three high, that each request and response must pass through before reaching an endpoint. At each filter, custom Java code would have a chance to manipulate the request or response, or anything that has to do with either of those objects.

Related Reading

Java Servlet & JSP Cookbook
By Bruce W. Perry

Understand that a user's request for a web application resource can be forced to go through any number of filters, in a given order, and any of the filters may manipulate the request, including stopping it altogether, and respond in a variety of different ways. This is important to understand because later in this article, two filters will be presented that manipulate the HttpServletRequest and HttpServletResponse objects to provide some very convenient functionality. Don't worry if you don't know anything about coding a filter -- it would certainly help if you understood the code, but in the end of the article, all of the code is provided in a JAR that can easily be used without knowing a thing about how it was made.

Before moving on, if you would like to learn more about the basics of servlet filters, I suggest checking out Servlets and JavaServer Pages; the J2EE Web Tier. It is a Servlet 2.4 and JSP 2.0 book I co-authored with Kevin Jones, and the book provides complete coverage of servlets and servlet filters, including the two filters presented later in this article. It would be nice if you bought the book, but the chapters on servlets and filters will soon be available for free on the book support site -- if they're online already, read away.

Compressing Content Using a Servlet Filter

Compression is the act of removing redundant information, representing what you want in as little possible space. It is incredibly helpful for sending information across the World Wide Web, because the speed at which people get information from a web application is almost always dependent on how much information you are trying to send. The smaller the size of your information, the faster it can all be sent. Therefore, if you compress the content your web application generates, it will get to a user faster and appear to be displayed on the user's screen faster. In practice, simply applying compression to a decent-sized web page often results in saving several seconds of time.

Now, the theory is nice, but the practice is nicer. This theoretical compression isn't something you have to labor over each time you go to code a servlet, JSP, or any other part of a web application. You can obtain very effective compression by having a servlet filter conditionally pipe whatever your web application produces to a GZIP-compressed file. Why GZIP? Because the HTTP protocol, the protocol used to transmit web pages, allows for GZIP compression. Why conditionally? Because not every browser supports GZIP compression, but almost every single modern web browser does. If you blindly send GZIP-compressed content to an old browser, the user might get nothing but gibberish. Since checking for GZIP compression support is nearly trivial, it is no problem to have a filter send GZIP-compressed content to only those users that can handle it.

Source Code

Download jspbook.zip for all of the source code found in this article. Also download jspbook.jar for the ready-to-use JAR with compiled versions of both the cache and compression filter.

I'm saying this GZIP compression stuff is good. But how good? GZIP compression will usually get you around a 6:1 compression ratio; it depends on how much content you are sending and what the content is. In practice, this means you will send content to a user up to six times faster if you simply use GZIP compression whenever you can. The only trick is that you need to be able to convert normal content in to GZIP-compressed content. Thankfully, the standard Java API provides code for doing exactly this: the java.util.zip package. The task is as easy as sending output sent in a web application's response conditionally through the java.util.zip.GZIPOutputStream class. Here is some code for doing exactly that.

As with most every filter, three classes are needed to do the job. A customized implementation of the javax.servlet.Filter interface, a customized implementation of the javax.servlet.ServletOutputStream class, and a customized implementation of the javax.servlet.http.HttpServletResponse class. Full source code for these three classes is provided at the end of the article; for now I will focus only on the relevant code. First, a check needs to be made if a user has support for GZIP-compressed content. This check is best done in the implementation of the Filter class.

...
public class GZIPFilter implements Filter {

  // custom implementation of the doFilter method
  public void doFilter(ServletRequest req,
                       ServletResponse res,
                       FilterChain chain)
      throws IOException, ServletException {
      
    // make sure we are dealing with HTTP
    if (req instanceof HttpServletRequest) {
      HttpServletRequest request =
          (HttpServletRequest) req;
      HttpServletResponse response =
          (HttpServletResponse) res;
      // check for the HTTP header that
      // signifies GZIP support
      String ae = request.getHeader("accept-encoding");
      if (ae != null && ae.indexOf("gzip") != -1) {
        System.out.println("GZIP supported, compressing.");
        GZIPResponseWrapper wrappedResponse =
          new GZIPResponseWrapper(response);
        chain.doFilter(req, wrappedResponse);
        wrappedResponse.finishResponse();
        return;
      }
      chain.doFilter(req, res);
    }
  }

Information about GZIP support is conveyed using the accept-encoding HTTP header. This header can be accessed using the HttpServletRequest object's getHeader() method. The conditional part of the code need be nothing more than an if statement that either sends the response as is or sends the response off to be GZIP compressed.

The next important part of the GZIP filter code is compressing normal content with GZIP compression. This code occurs after the above filter has found that the user does know how to handle GZIP-compressed content, and the code is best placed in a customized version of the ServletOutputStream class. Normally, the ServletOutputStream class handles sending text or non-text content to a user while ensuing appropriate character encoding is used. However, we want to have the ServletOutputStream class send content though a GZIPOutputStream before sending it to a client. This can be accomplished by overriding the write() methods of the ServletOutputStream class to GZIP content before sending it off in an HTTP response.

...
  public GZIPResponseStream(HttpServletResponse response)
      throws IOException {
    super();
    closed = false;
    this.response = response;
    this.output = response.getOutputStream();
    baos = new ByteArrayOutputStream();
    gzipstream = new GZIPOutputStream(baos);
  }
...
  public void write(int b) throws IOException {
    if (closed) {
      throw new IOException(
          "Cannot write to a closed output stream");
    }
    gzipstream.write((byte)b);
  }

  public void write(byte b[]) throws IOException {
    write(b, 0, b.length);
  }

  public void write(byte b[], int off, int len)
      throws IOException {
    System.out.println("writing...");
    if (closed) {
      throw new IOException(
          "Cannot write to a closed output stream");
    }
    gzipstream.write(b, off, len);
  }
...

There are also a few loose ends to tie up, such as setting the content's encoding to be the MIME type for GZIP, and ensuring the subclass of ServletOutputStream has implementations of the flush() and close() methods that work with the changes to the write() methods. However, all of these changes are minor, and you may see code that does them by looking at the source code provided at the end of this article. The most important point to understand is that the filter has altered the response to ensure that all content is GZIP compressed.

Test out the above code by grabbing a copy of jspbook.jar, which includes compiled classes of the GZIP filter, and putting the JAR in the WEB-INF/lib directory of your favorite web application. Next, deploy the com.jspbook.GZIPFilter class to intercept all requests to resources ending with ".jsp" or anything that produces HTML. Reload the web application for the changes to take effect. The GZIP filter should now be automatically compressing all responses, if the user's browser supports GZIP compression.

Pages: 1, 2, 3

Next Pagearrow