Home Page Javascript Download Core Servlets & Java Server Java Cryptography Java in 60 Min A Day Javascript Tutorial About Us Contact Us
Site Search
Free Javascript Lessons
Newest Java Articles
Checkbox Changer
Check Entry
Check All
Block Key Press
Basic Validation
Auto Email Link
JavaScript Forms: Agree Before Entry
Auto Tab
JavaScript Calendars: Time Entered - Day Highlighted
JavaScript Calendars: Days Left
Related SE Keywords
Java virtual machine
Java Applet
Javascript Lesson
Javascript Tutorial
Java Programming
Java Games
Java Xml
Java String
Cryptography Extensions
Free Web Master Tools
 
JavascriptDownload.net > Free Javascript Lessons > Core Servlets & JavaServer Pages > 5- Servlet and JSP Filters / A Compression Filter

5.11. Example: A Compression Filter

Most recent browsers can handle gzipped content, automatically uncompressing documents that have gzip as the value of the Content-Encoding response header and then treating the result as though it were the original document. Sending such compressed content can be a real time saver because the time required to compress the document on the server and then uncompress it on the client is typically dwarfed by the savings in download time, especially when dial-up connections are used. For example, Listing 5.22 shows a servlet that has very long, repetitive, plain-text output: a ripe candidate for compression. If gzip could be applied, it could compress the output by a factor of over 300!

However, although most browsers support this type of encoding, a few do not. Sending compressed content to browsers that don't support gzip encoding produces a totally garbled result. Browsers that support content encoding include most versions of Netscape for UNIX, most versions of Internet Explorer for Windows, Netscape 4.7 and later for Windows, Firefox, and Opera 5.12 and above. Therefore, this compression cannot be done blindly—it is only valid for clients that use the Accept-Encoding request header to specify that they support gzip.

We demonstrate very similar functionality in Core Servlets and JavasServer Pages, Volume 1: Core Technologies, compressing the output right in the servlet. However, because we would like to be able to apply this behavior to multiple resources, a filter is a much more appropriate place for it. The compression filter can use the StringWrapper of Section 5.9 (Modifying the Response) to compress content when the browser supports such a capability. Accomplishing this task requires the following:

  1. A class that implements the Filter interface. This class is called CompressionFilter and is shown in Listing 5.21. The init method stores the FilterConfig object in a field in case subclasses need access to the servlet context or filter name. The body of the destroy method is left empty.

  2. A wrapped response object. The doFilter method checks if the client indicates that it supports compression (i.e., has gzip as one of the values of its Accept-Encoding header). If it doesn't, we invoke the doFilter method of the FilterChain object with the original response and request objects. If the client supports gzip compression, the doFilter method wraps the ServletResponse object in a StringWrapper and passes that wrapper to the doFilter method of the FilterChain object. After this call completes, all other filters and the final resource have executed and the output is inside the wrapper. So the original doFilter extracts a String that represents all of the resource's output. We then wrap a ByteArray OutputStream in a GZIPOutputStream. We wrap the resultant GZIPOutputStream in an OutputStreamWriter, enabling us to pass a String through the compression mechanism. Using the toString method of the StringWrapper, we copy the original buffered output into the OutputStreamWriter. Finally, doFilter sends the compressed output to the client by writing the entire underlying byte array of our stream chain (e.g., ByteArrayOutputStream, GZIPOutputStream, and OutputStreamWriter) to the OutputStream that is associated with the original response object.

  3. Registration with long servlet. First, the filter element of web.xml (Listing 5.23) associates the name CompressionFilter with the class coreservlets.filters.CompressionFilter. Next, the filter-mapping element uses a servlet-name of LongServlet so that the filter fires each time that long servlet (Listing 5.22) is requested. The servlet and servlet-mapping elements assign the name LongServlet to the servlet and specify the URL that corresponds to the servlet.

  4. Disablement of the invoker servlet. This operation is shown in Section 5.2 (Example: A Reporting Filter) and is not repeated here.

When the filter is attached, the body of the servlet is reduced 300 times and the time to access the servlet on a 28.8K modem is reduced by more than a factor of 10 (more than 50 seconds uncompressed; less than 5 seconds compressed), a huge savings. Figure 5-9 shows the page that the compression filter was used on. However, two small warnings are in order here.

First, there is a saying in the software industry that there are three kinds of lies: lies, darn lies, and benchmarks. The point of this maxim is that people always rig benchmarks to cast their point in the most favorable light possible. We did the same thing by using a servlet with long simple output and using a slow modem connection. So, we're not promising that you will always get a tenfold performance gain, but it is a simple matter to attach or detach the compression filter. That's the beauty of filters. Try it yourself and see how much it buys you in typical usage conditions.

Second, although the specification does not officially mandate that you set response headers before calling the doFilter method of the FilterChain, some servers require you to do so. This is to prevent you from attempting to set a response header after a resource has sent content to the client. So, for portability, be sure to set response headers before calling chain.doFilter.

Core Warning

 

If your filter sets response headers, be sure it does so before calling the doFilter method of the FilterChain object.


Listing 5.21. CompressionFilter.java

package coreservlets.filters;

import java.io.*;
import java.util.zip.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Filter that compresses output with gzip
 *  (assuming that browser supports gzip).
 */
public class CompressionFilter implements Filter {
  private FilterConfig config;

  /** If browser does not support gzip, invoke resource
   *  normally. If browser <I>does</I> support gzip,
   *  set the Content-Encoding response header and
   *  invoke resource with a wrapped response that
   *  collects all the output. Extract the output
   *  and write it into a gzipped byte array. Finally,
   *  write that array to the client's output stream.
   */
  public void doFilter(ServletRequest request,
                       ServletResponse response,
                       FilterChain chain)
      throws ServletException, IOException {
    HttpServletRequest req = (HttpServletRequest)request;
    HttpServletResponse res = (HttpServletResponse)response;
    if (!isGzipSupported(req)) {
      // Invoke resource normally.
      chain.doFilter(req,res);
    } else {
      // Tell browser we are sending it gzipped data.
      res.setHeader("Content-Encoding", "gzip");

      // Invoke resource, accumulating output in the wrapper.
      StringWrapper responseWrapper =
        new StringWrapper(res);
      chain.doFilter(req,responseWrapper);

      // Make a writer that compresses data and puts
      // it into a byte array.
      ByteArrayOutputStream byteStream =
        new ByteArrayOutputStream();
      GZIPOutputStream zipOut =
        new GZIPOutputStream(byteStream);
      OutputStreamWriter tempOut =
        new OutputStreamWriter(zipOut);

      // Compress original output and put it into byte array.
      tempOut.write(responseWrapper.toString());

      // Gzip streams must be explicitly closed.
      tempOut.close();

      // Send compressed result to client.
      OutputStream realOut = res.getOutputStream();
      byteStream.writeTo(realOut);
    }
  }

  /** Store the FilterConfig object in case subclasses
   *  want it.
   */
  public void init(FilterConfig config)
      throws ServletException {
    this.config = config;
  }

  protected FilterConfig getFilterConfig() {
    return(config);
  }

  public void destroy() {}

  private boolean isGzipSupported(HttpServletRequest req) {
    String browserEncodings =
      req.getHeader("Accept-Encoding");
    return((browserEncodings != null) &&
           (browserEncodings.indexOf("gzip") != -1));
  }
}

					  

Listing 5.22. LongServlet.java

package coreservlets;

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

/** Servlet with <B>long</B> output. Used to test
 *  the effect of the compression filter of Chapter 9.
 */
public class LongServlet extends HttpServlet {
  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
      throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    String docType =
      "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
      "Transitional//EN\">\n";
    String title = "Long Page";
    out.println
      (docType +
      "<HTML>\n" +
      "<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" +
      "<BODY BGCOLOR=\"#FDF5E6\">\n" +
      "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n");
    String line = "Blah, blah, blah, blah, blah. " +
                  "Yadda, yadda, yadda, yadda.";
    for(int i=0; i<10000; i++) {
      out.println(line);
    }
    out.println("</BODY></HTML>");
  }
}

					  

Listing 5.23. web.xml (Excerpt for compression filter)

<?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">
<!-- Register the name "CompressionFilter" for
      coreservlets.filters.CompressionFilter. -->
  <filter>
    <filter-name>CompressionFilter</filter-name>
    <filter-class>
      coreservlets.filters.CompressionFilter
    </filter-class>
  </filter>
  <!-- Apply CompressionFilter to the servlet named
      "LongServlet". -->
  <filter-mapping>
    <filter-name>CompressionFilter</filter-name>
    <servlet-name>LongServlet</servlet-name>
  </filter-mapping>
  <!-- Give a name to the servlet that generates long
      (but very exciting!) output. -->
  <servlet>
    <servlet-name>LongServlet</servlet-name>
    <servlet-class>coreservlets.LongServlet</servlet-class>
  </servlet>
  <!-- Make /LongServlet invoke the servlet
       named LongServlet (i.e., coreservlets.LongServlet). -->
  <servlet-mapping>
    <servlet-name>LongServlet</servlet-name>
    <url-pattern>/LongServlet</url-pattern>
  </servlet-mapping>
  <!-- Disable the invoker servlet. -->
  <servlet>
    <servlet-name>NoInvoker</servlet-name>
    <servlet-class>coreservlets.NoInvokerServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>NoInvoker</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
  </servlet-mapping>
</web-app>

					  

Add Comment
 
homepage   |  about us  |  contact us
Powered By teknonova.com