4 Replies Latest reply on May 11, 2010 3:42 PM by Chris Simons

    EHCache GZip filter: Incompatible with Seam?!

    Francisco Jose Peredo Noguez Master

      I am trying to use net.sf.ehcache.constructs.web.filter.GzipFilter to reduce bandwith use, but the only page I am able to see is the login page (I am testing this with a Seam-gen app) after I click login (and write a valid login and password) I get:


       javax.servlet.ServletException: net.sf.ehcache.constructs.web.ResponseHeadersNotModifiableException: Failure when attempting to set Content-Encoding: gzip
           net.sf.ehcache.constructs.web.filter.Filter.logThrowable(Filter.java:147)
           net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:102)
      
      root cause
      
      net.sf.ehcache.constructs.web.ResponseHeadersNotModifiableException: Failure when attempting to set Content-Encoding: gzip
           net.sf.ehcache.constructs.web.ResponseUtil.addGzipHeader(ResponseUtil.java:126)
           net.sf.ehcache.constructs.web.filter.GzipFilter.doFilter(GzipFilter.java:89)
           net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:89)
      
      



      Fragment of my web.xml:


      
       <filter>
        <filter-name>gzipFilter</filter-name>
        <filter-class>net.sf.ehcache.constructs.web.filter.GzipFilter</filter-class>
       </filter>
       <filter>
        <filter-name>Seam Filter</filter-name>
        <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
       </filter>
       <filter-mapping>
        <filter-name>gzipFilter</filter-name>
        <url-pattern>*.seam</url-pattern>
       </filter-mapping>
       <filter-mapping>
        <filter-name>Seam Filter</filter-name>
        <url-pattern>/*</url-pattern>
       </filter-mapping>
      
      



      Any hints? Has anyone been able to use EHCache Gzip filter with Seam / Richfaces?

        • 1. Re: EHCache GZip filter: Incompatible with Seam?!
          Ingo Jobling Master

          Hi, are you running Jboss/Tomcat? 



          C:\jboss-4.2.3.GA\server\default\deploy\jboss-web.deployer\server.xml
          
            <Connector ...
           
                         compression="on" 
          
                         compressionMinSize="2048" 
          
                         compressableMimeType="text/html,text/xml" 
          
            />




          More about this: enable-gzip-compression-in-tomcat

          • 2. Re: EHCache GZip filter: Incompatible with Seam?!
            Francisco Jose Peredo Noguez Master

            Thanks, that of course works.... but not for me, since the Tomcat is shared, by many applications, and convincing the admin to make such a change to global configuration is just impossible (I have been trying to convince him to do it for a few months now...)

            • 3. Re: EHCache GZip filter: Incompatible with Seam?!
              Francisco Jose Peredo Noguez Master

              I had to patch the code in GzipFilter (for EHCache-1.2.3, the one included in hibernate-distribution-3.3.1.GA, I tried upgrading to 1.5.x the latest stable the release, but it causes an infinite loop failure when seam is starting)


              I added this lines in the doFilter method to basically swallow the problem. I think the problem is that Seam redirects and EHCache filter are not compatible:


               // Write the zipped body
                          try{
                               ResponseUtil.addGzipHeader(response);
                               response.setContentLength(compressedBytes.length);
                          }catch(ResponseHeadersNotModifiableException ex){
                               return;
                          }
              




              Here is all the (patched) class code (just in case anyone faces the same problem):


              
              /**
               *  Copyright 2003-2006 Greg Luck
               *
               *  Licensed under the Apache License, Version 2.0 (the "License");
               *  you may not use this file except in compliance with the License.
               *  You may obtain a copy of the License at
               *
               *      http://www.apache.org/licenses/LICENSE-2.0
               *
               *  Unless required by applicable law or agreed to in writing, software
               *  distributed under the License is distributed on an "AS IS" BASIS,
               *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
               *  See the License for the specific language governing permissions and
               *  limitations under the License.
               */
              
              package mx.gob.tabasco.sbmi.filters;
              
              import net.sf.ehcache.constructs.web.GenericResponseWrapper;
              import net.sf.ehcache.constructs.web.ResponseHeadersNotModifiableException;
              import net.sf.ehcache.constructs.web.ResponseUtil;
              import net.sf.ehcache.constructs.web.filter.Filter;
              
              import org.apache.commons.logging.Log;
              import org.apache.commons.logging.LogFactory;
              
              import javax.servlet.FilterChain;
              import javax.servlet.http.HttpServletRequest;
              import javax.servlet.http.HttpServletResponse;
              import java.io.ByteArrayOutputStream;
              import java.util.zip.GZIPOutputStream;
              
              /**
               * Provides GZIP compression of responses.
               * <p/>
               * See the filter-mappings.xml entry for the gzip filter for the URL patterns
               * which will be gzipped. At present this includes .jsp, .js and .css.
               *
               * @author <a href="mailto:gluck@thoughtworks.com">Greg Luck</a>
               * @author <a href="mailto:amurdoch@thoughtworks.com">Adam Murdoch</a>
               * @version $Id: GzipFilter.java 80 2006-05-14 03:16:22Z gregluck $
               */
              public class GzipFilter extends Filter {
              
                  private static final Log LOG = LogFactory.getLog(GzipFilter.class.getName());
              
                  /**
                   * Performs initialisation.
                   */
                  protected void doInit() throws Exception {
                  }
              
                  /**
                   * A template method that performs any Filter specific destruction tasks.
                   * Called from {@link #destroy()}
                   */
                  protected void doDestroy() {
                      //noop
                  }
              
                  /**
                   * Performs the filtering for a request.
                   */
                  protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
                                          final FilterChain chain) throws Exception {
                      if (!isIncluded(request) && acceptsEncoding(request, "gzip")) {
                          // Client accepts zipped content
                          if (LOG.isDebugEnabled()) {
                              LOG.debug(request.getRequestURL() + ". Writing with gzip compression");
                          }
              
                          // Create a gzip stream
                          final ByteArrayOutputStream compressed = new ByteArrayOutputStream();
                          final GZIPOutputStream gzout = new GZIPOutputStream(compressed);
              
                          // Handle the request
                          final GenericResponseWrapper wrapper = new GenericResponseWrapper(response, gzout);
                          chain.doFilter(request, wrapper);
                          wrapper.flush();
              
                          gzout.close();
                          
                          //return on error or redirect code, because response is already committed
                          int statusCode = wrapper.getStatus();
                          if (statusCode != HttpServletResponse.SC_OK) {
                              return;
                          }
              
                          //Saneness checks
                          byte[] compressedBytes = compressed.toByteArray();
                          boolean shouldGzippedBodyBeZero = ResponseUtil.shouldGzippedBodyBeZero(compressedBytes, request);
                          boolean shouldBodyBeZero = ResponseUtil.shouldBodyBeZero(request, wrapper.getStatus());
                          if (shouldGzippedBodyBeZero || shouldBodyBeZero) {
                              compressedBytes = new byte[0];
                          }
              
                          // Write the zipped body
                          try{
                               ResponseUtil.addGzipHeader(response);
                               response.setContentLength(compressedBytes.length);
                          }catch(ResponseHeadersNotModifiableException ex){
                               return;
                          }
              
              
                          response.getOutputStream().write(compressedBytes);
                      } else {
                          // Client does not accept zipped content - don't bother zipping
                          if (LOG.isDebugEnabled()) {
                              LOG.debug(request.getRequestURL()
                                      + ". Writing without gzip compression because the request does not accept gzip.");
                          }
                          chain.doFilter(request, response);
                      }
                  }
              
              
              
                  /**
                   * Checks if the request uri is an include.
                   * These cannot be gzipped.
                   */
                  private boolean isIncluded(final HttpServletRequest request) {
                      final String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
                      final boolean includeRequest = !(uri == null);
              
                      if (includeRequest && LOG.isDebugEnabled()) {
                          LOG.debug(request.getRequestURL() + " resulted in an include request. This is unusable, because" +
                                  "the response will be assembled into the overrall response. Not gzipping.");
                      }
                      return includeRequest;
                  }
              
                  /**
                   * Determine whether the user agent accepts GZIP encoding. This feature is part of HTTP1.1.
                   * If a browser accepts GZIP encoding it will advertise this by including in its HTTP header:
                   * <p/>
                   * <code>
                   * Accept-Encoding: gzip
                   * </code>
                   * <p/>
                   * Requests which do not accept GZIP encoding fall into the following categories:
                   * <ul>
                   * <li>Old browsers, notably IE 5 on Macintosh.
                   * <li>Internet Explorer through a proxy. By default HTTP1.1 is enabled but disabled when going
                   * through a proxy. 90% of non gzip requests seen on the Internet are caused by this.
                   * </ul>
                   * As of September 2004, about 34% of Internet requests do not accept GZIP encoding.
                   *
                   * @param request
                   * @return true, if the User Agent request accepts GZIP encoding
                   */
                  protected boolean acceptsGzipEncoding(HttpServletRequest request) {
                      return acceptsEncoding(request, "gzip");
                  }
              
              
              }
              
              
              

              • 4. Re: EHCache GZip filter: Incompatible with Seam?!
                Chris Simons Expert

                Excellent post; I'm noticing the same issue, even with the latest version of EhCache (2.0.1).  However, I notice that the exception is encountered only once, and from that point forward not again.  So I'm curious if you think it's worth patching?