4 Replies Latest reply on May 11, 2010 3:42 PM by chris.simons

    EHCache GZip filter: Incompatible with Seam?!

      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?!
          joblini

          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?!

            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?!

              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?!

                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?