Orion Filter Tutorial, Lesson 5, Compressing the Response

In this part of the tutorial, we will build a simple Filter that compresses the response using GZip.



1 Introduction

    In this part of the tutorial, we will build a simple Filter that compresses the response.

    The Filter with interfere with the response returned and try to compress the returned data using GZip, but only if the client accepts GZip compressed content.

    GZip is a compression tool that uses the same compression algorithm as for instance zip (which is more comonly used on Windows platforms). Details about gzip can be found here.

    Most browsers can decompress GZip compressed content. To make sure that a client can handle GZip compressed content, one should always look for the value "gzip" in the HTTP requet's "Accept-Encoding" header.

    Compressing the response will be carried out by the use of the GZip classes of the java.util.zip library.

    In order to get hold of and manipulate the Response before this is sent on down the line of Filters (or to the client), The GenericResponseWrapper of lesson 3 will be used.

2 Writing the Filter

    Now its time to write the GZIPFilter that will use the GenericResponseWrapper of lesson 3. The Filter will use this Wrapper to get hold of the response data and then use the java.util.zip classes to compress the data before returning the response.


    Figure 1: The GZIPFilter class extends the GenericFilter class.

    After compressing the Response data, the size of the Response has to be reset, and a "Content-Encoding" header should be added with a value of "gzip" in order for the client to recognize the compressed content as just that.

    We will create the GZIPFilter in the following steps:



    2.1 Step 1: Create a file named 'GZIPFilter.java'

      Fire up your editor and start editing a source file with the name 'GZIPFilter.java' in the 'filter' directory set up during lesson 1.

    2.2 Step 2: Add import statements

      Start of with the package name and any imports we need, so that your source looks like the following:


      package com.acme.filter;

      import javax.servlet.FilterChain;
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpServletRequest;
      import java.io.ByteArrayOutputStream;
      import java.io.OutputStream;
      import java.io.IOException;
      import java.util.zip.GZIPOutputStream;
      import java.util.Enumeration;
      Listing 1: Package name and import statements

      This will make sure that we have the neccessary classes available.

    2.3 Step 3: Add class declaration

      Continue with the following:


      public class GZIPFilter extends GenericFilter
      {
      Listing 2: Class declaration

      This will make sure that we extend the GenericFilter that we initially created in lesson 1.

    2.4 Step 4: Add a constructor

      Continue with the following:


      public GZIPFilter() {}
      Listing 3: Constructor

    2.5 Step 5: Override the doFilter() method

      We shall now override the doFilter() method inherited from the GenericFilter class with the following:


      public void doFilter(final ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException
      {
      HttpServletResponse httpResponse = (HttpServletResponse) response;
      HttpServletRequest httpRequest=(HttpServletRequest) request;
      GenericResponseWrapper wrapper = new GenericResponseWrapper(httpResponse);
      chain.doFilter(request, wrapper);
      OutputStream out = response.getOutputStream();
      Listing 4: Override the doFilter() method

      Above, we create a ne GenericResponseWrapper and send this on down the chain instead of the Response our Filter was originally given. This will allow us to freely setup the Response to our liking.

    2.6 Step 6: Compress the Response data

      Continue with the following


      if (!isCached(wrapper) && !isIncluded(request) && accepts(httpRequest,"gzip"))
      {
      ByteArrayOutputStream compressed = new ByteArrayOutputStream();
      GZIPOutputStream gzout = new GZIPOutputStream(compressed);
      gzout.write(wrapper.getData());
      gzout.flush();
      gzout.close();
      httpResponse.setHeader("Content-Encoding", "gzip");
      out.write(compressed.toByteArray());
      response.setContentLength(compressed.size());
      }
      Listing 5: Override the doFilter() method (continued).

      In Listing 5 above, we first make sure that the Response is a non-cached response. We also check that the Request was not ment for inclusion in another resource (Orion allows for Filters to be applied on server-side redirects). Finally, we make sure that the client can handle Gzip compressed content.

      If all these conditions are met, we write the content that the Wrapper holds into a GZipOutputStream, and then grab the compressed data and write it to our Filters Response's OutputStream.

    2.7 Step 7: Passing on unmodified data

      If the conditions described in step 6 above are not met, the data held by the Wrapper should be passed on without modification:


      else
      {
      out.write(wrapper.getData());
      }
      out.flush();
      out.close();
      }
      Listing 5: Override the doFilter() method (continued).

      Above, we write the unmodified data to the OuputStream of the Response.

    2.8 Step 8: Check if included

      As Orion allows for Filters to be invoked on server-side requests, the Filter needs to make sure that this Request is not included in another Request:


      protected boolean isIncluded(ServletRequest request)
      {
      String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
      if (uri == null)
      {
      return false;
      }
      else
      {
      return true;
      }
      }
      Listing 6: Check if included

    2.9 Step 9: Check if cached

      If this Response is cached then we dont want to compress the empty response:


      protected boolean isCached(GenericResponseWrapper wrapper)
      {
      if (wrapper.getData().length > 0)
      {
      return false;
      }
      else
      {
      return true;
      }
      }
      Listing 7: Check if cached

    2.10 Step 10: Check if client accepts GZip

      We should only compress the Response if the client allows for it:


      protected boolean accepts(HttpServletRequest request, String name)
      {
      return headerContains(request,"Accept-Encoding",name);
      }

      protected boolean headerContains(HttpServletRequest request, String header, String value)
      {
      Enumeration accepted=request.getHeaders(header);
      while(accepted.hasMoreElements())
      {
      String headerValue=(String)accepted.nextElement();
      if(headerValue.indexOf(value)!=-1)
      {
      return true;
      }
      }
      return false;
      }
      }
      Listing 8: Check "Accept-Encoding" header for "gzip"

      The GZIPFilter should now look like this.

    2.11 Step 11: Compile the GZIPFilter

      Compile the GZIPFilter.

      The GZIPFilter.class should be located in the 'orion/default-web-app/WEB-INF/classes/com/acme/filter/' directory when done.

3 Configuring your Filter

    It is now time for us to configure our Filter usage.

    To do so we will go through the following steps:



    3.1 Step 1: Configure Filter existence in web.xml

      Start up your editor and open the file 'orion/default-web-app/WEB-INF/web.xml'.

      After the configuration of the 'prePost' Filter, add the following rows:


      <filter>
      <filter-name>gzip</filter-name>
      <filter-class>com.acme.filter.GZIPFilter</filter-class>
      </filter>
      Listing 17: Adding the Filter.

      This will tell the container that our web-application has yet another filter that we will refer to as 'gzip', which represents the class 'com.acme.filter.GZIPFilter'.

    3.2 Step 2: Configure Filter mapping in web.xml

      After the mapping configuration of the 'imageManipulation' Filter, add the following rows:


      <filter-mapping>
      <filter-name>gzip</filter-name>
      <url-pattern>/*.jsp</url-pattern>
      </filter-mapping>
      Listing 18: Mapping the Filter.

      This will tell our container that within this web-application, any requests for resources with an suffix of '.jsp' will first invoke the filter named 'gzip'.

    3.3 Step 3: Save changes to web.xml

      Save your 'web.xml' and make sure that it is located in the 'orion/default-web-app/WEB-INF/' directory.

      Your web.xml should now look something like this.

4 Testing your Filter

    It is now time to test the Filter.

    We will do so in the following steps:



    4.1 Step 1: Make sure Orion is running

      Make sure that your Orion Application Server is up and running.

    4.2 Step 2: Open the URL

      Open up the location 'http://localhost/filter.jsp' in a normal browser. (We created the file 'filter.jsp' in lesson 1).

      Substitute 'localhost' with the name of the server you are working with if needed.

      The result should be the 'filter.jsp' page in its compressed form (it will look just as normal though).

    Congratulations! You have just created and successfully tested your first Response compressing filter!

    Continue with Lesson 5, a Chain of Filters.

Copyright © 2003 IronFlare AB