001    package org.rakeshv.filters;
002    
003    import java.io.IOException;
004    import java.util.Enumeration;
005    import javax.servlet.Filter;
006    import javax.servlet.FilterChain;
007    import javax.servlet.FilterConfig;
008    import javax.servlet.ServletException;
009    import javax.servlet.ServletRequest;
010    import javax.servlet.ServletResponse;
011    import javax.servlet.http.HttpServletRequest;
012    import javax.servlet.http.HttpServletResponse;
013    
014    /**
015     * A <code>Filter</code> class that is used to compress the response
016     * to a client request.  The filter is activated only if the client
017     * specified the <code>Accept-Encoding</code>, or the
018     * <code>TE</code> HTTP headers.
019     *
020     * <p>The following shows ways in which you can enable the filters
021     * in your web application configuration file (WEB-INF/web.xml):</p>
022     *
023     * <pre>
024     *  &lt;filter&gt;
025     *    &lt;filter-name&gt;compressionFilter&lt;/filter-name&gt;
026     *    &lt;filter-class&gt;org.rakeshv.filters.CompressionFilter&lt;/filter-class&gt;
027     *    &lt;description&gt;A servlet filter that supports Gzip, Deflate and ZIP compressed responses.&lt;/description&gt;
028     *  &lt;/filter&gt;
029     *
030     *  &lt;filter-mapping&gt;
031     *    &lt;filter-name&gt;compressionFilter&lt;/filter-name&gt;
032     *    &lt;servlet-name&gt;myServlet&lt;/servlet-name&gt;
033     *  &lt;/filter-mapping&gt;
034     *
035     *  &lt;filter-mapping&gt;
036     *    &lt;filter-name&gt;compressionFilter&lt;/filter-name&gt;
037     *    &lt;url-pattern&gt;/docs/*&lt;/url-pattern&gt;
038     *  &lt;/filter-mapping&gt;
039     * </pre>
040     *
041     * <p>&copy; Copyright 2003, Rakesh Vidyadharan</p>
042     * @author Rakesh Vidyadharan 2<sup><small>nd</small></sup> September 2003
043     *
044     * @version $Id: CompressionFilter.java,v 1.7 2005/07/19 17:36:37 rakesh Exp $
045     */
046    public class CompressionFilter extends FilterAdapter 
047    {
048      /**
049       * A constant that is used to denote that <code>deflate</code>
050       * compression is to be used.
051       */
052      public static final int DEFLATE_COMPRESSION = 1;
053    
054      /**
055       * A constant that is used to denote that <code>zip</code>
056       * compression is to be used.
057       */
058      public static final int ZIP_COMPRESSION = 2;
059    
060      /**
061       * A constant that is used to denote that <code>gzip</code> 
062       * compression is to be used.
063       */
064      public static final int GZIP_COMPRESSION = 3;
065    
066      /**
067       * A reference to the Servlet container's Filter configuration.
068       */
069      private FilterConfig filterConfig = null;
070    
071      /**
072       * Check the <code>Accept-Encoding</code> and 
073       * <code>accept-encoding</code> HTTP headers, and if either of them
074       * specified the accepted compression methods (those supported by
075       * the java.util.zip classes), then the response is compressed using 
076       * the desired compression algorithm.  If multiple algorithms were
077       * specified, then one of the specified algorithms will be used.
078       * The exact algorithm that will be used is in the order specified:
079       * </p>
080       * <ol>
081       *  <li>GZIP
082       *  <li>ZIP
083       *  <li>Deflate
084       * </ol>
085       *
086       * <p>If the special <code>*</code> value is used to specify the
087       * compression to use, <code>Deflate</code> compression will be used.
088       * </p>
089       *
090       * <p><b>Note:</b> This method does an implicit call to {@link 
091       * CompressionResponseWrapper#finish()} if the response if being
092       * compressed.  Other servlets down the chain do not need to 
093       * worry about finishing the compression stream.</p>
094       *
095       * @throws IOException - If exceptions are encountered while
096       *   applying the filter.
097       * @throws ServletException - If exceptions are encountered while
098       *   interacting with the request or response objects.
099       */
100      public void doFilter( ServletRequest request, 
101          ServletResponse response, FilterChain chain ) 
102        throws IOException, ServletException 
103      {
104        int compressionType = -1;
105    
106        Enumeration e = ( (HttpServletRequest) request ).getHeaderNames();
107        while ( e.hasMoreElements() ) 
108        {
109          String header = (String) e.nextElement();
110          if ( header.equalsIgnoreCase( "Accept-Encoding" ) ||
111              header.equalsIgnoreCase( "TE" ) )
112          {
113            String encoding = 
114              ( (HttpServletRequest ) request ).getHeader( header );
115            if ( encoding.indexOf( "gzip" ) != -1 ) 
116            {
117              compressionType = CompressionFilter.GZIP_COMPRESSION;
118            }
119            else if ( encoding.indexOf( "zip" ) != -1 )
120            {
121              compressionType = CompressionFilter.ZIP_COMPRESSION;
122            }
123            else if ( encoding.indexOf( "deflate" ) != -1 || encoding.equals( "*" ) )
124            {
125              compressionType = CompressionFilter.DEFLATE_COMPRESSION;
126            }
127          }
128        }
129    
130        if ( compressionType == -1 )
131        {
132          chain.doFilter( request, response );
133        }
134        else 
135        {
136          CompressionResponseWrapper wrappedResponse = 
137            new CompressionResponseWrapper( (HttpServletResponse) response );
138          wrappedResponse.setCompressionType( compressionType );
139          chain.doFilter( request, wrappedResponse );
140    
141          try
142          {
143            wrappedResponse.finish();
144          }
145          catch ( IOException ioex )
146          {
147            // Do nothing
148          }
149        }
150      }
151    }