views:

249

answers:

7

Hi,

I was wondering if there was an easy way to serve GZipped content with Java Servlets. I already have the app up and running so the modifications needed should be too heavy.

I have access to the response object just at the end of the doPost/doGet method, so I'm looking for something like

response.setGzip(true);

It doesn't have to be that easy but it would be ideal.

Thanks a lot

+6  A: 

This article has the complete (and brief) source code for a ServletFilter that automatically compresses on the fly.

Jonathan Feinberg
@Jonathan, Thanks a lot!
Pablo Fernandez
Why would you phrase your otherwise helpful information as a supercilious rhetorical question?
Jonathan Feinberg
+2  A: 

Look at GzipOutputStream class. Something like this:

response.setContentType(...)
GzipOutputStream os = new GzipOutputStream(response.getOutputStream);
//write to os
maximdim
Yeah, but then... what do I do with that <code>os</code>?
Pablo Fernandez
See Jonathan Feinberg's solution :)
Carl Smotricz
+1  A: 

Depending on your container, the container will most likely do this for you. It may do it automatically, or you might need to manually configure it to do it for you. The advantage of this method is zero code changes. And, again, depending on container, you can conditionally enable/disable the compression based on where the request comes from or source browser.

For Tomcat, have a look at the compression attribute on the HTTP config pages (v5.5, v6.0).

Paul Wagland
A: 

If you really, really don't want to fiddle with the Java code any more, you could also consider hooking an Apache server in front of your servlet container.

If you have a lot of static content, this could actually improve performance for you, as the Apache will be a bit faster for static pages than any servlet container. So you'd configure it to only delegate servlet requests to your servlet container on localhost.

Apache has handy built-in options for compressing output. I don't remember how to set them, but it's easy and versatile. It negotiates with browsers about what they can handle, and so on. In case of doubt, Apache will generally be more savvy and up-to-date on compression methods than any Java container.

Carl Smotricz
A: 

There are basically 2 ways:

  • Configure it in the appserver. In for example Tomcat you just need to set the compression attribute of the Connector in conf/server.xml to on.
  • Wrap the response.getOutputStream() in a new GzipOutputStream() and write to it instead.

The first way affects the whole webapp, but this really shouldn't hurt, it's almost zero effort and a big favour for performance. And, more importantingly, as opposed to the second way it actually checks the request headers if the client supports Gzip before using it. When you go for the 2nd way headlessly, then about 10% of the world wide web users wouldn't be able to access your webapplication. This is really not an oneliner task.

You can find here an advanced example of a FileServlet which supports under each Gzip and checks that based on the request headers. You may get new insights out of it.

BalusC
A: 

If you are on Tomcat, the connector can do compression for you. This is my configuration,

<Connector port="8000"
 compression="on" 
 compressionMinSize="1024" 
 compressableMimeType="text/html,text/xml" 
 ...
/>

If you run Apache httpd in front of Tomcat, you should use mod_gzip, which does a much better job.

ZZ Coder
A: 

Just wanted to let you know what I ended doing.

I made a wrapper of the request class that looks like this:

public class GzippedResponse extends HttpServletResponseWrapper{

  private PrintWriter pw;

  private GzippedResponse(HttpServletResponse response){
    super(response);
    try{
      pw = new PrintWriter(new GZIPOutputStream(response.getOutputStream()));
    }catch(Exception e){
      throw new ApiInternalException("Failed to create a Gzipped Response", e);
    }
  }

  public static GzippedResponse wrap(HttpServletResponse response){
    return new GzippedResponse(response);
  }

  @Override
  public PrintWriter getWriter() throws IOException {
    return pw;
  }  
}

And then on my BaseAction, which is basically a TemplateMethod for other "Actions" I wrap the response like this:

if(supportsCompression(request)){
  response.setHeader("Content-Encoding", "gzip");
  response = GzippedResponse.wrap(response);
}
action.macroExecute(request,response);

I think its clean enough. If you find something that can be improved, please let me know. Thanks everybody for the answers!

Pablo Fernandez