I am looking for a GZIP servlet filter to be used in a high volume web-app. I doesn't want to use the container specific options.
Requirement
Thank you.
This filter will compress the content of replies using GZIP. Compression is done on the fly. This assumes that you're really on a slow link, where you have lots of CPU, but not much bandwidth.
GZip is a form of server-side data compression that's helpful in reducing page loading time. In other words, it takes a set of data and makes it smaller for more streamlined, efficient delivery to a user's computer. Gzip compression reduces the size of your HTML, stylesheets, and JavaScript files.
However, in practice, GZIP performs best on text-based content, often achieving compression rates of as high as 70-90% for larger files, whereas running GZIP on assets that are already compressed via alternative algorithms (for example, most image formats) yields little to no improvement.
The Java Servlet specification version 2.3 introduces a new component type, called a filter. A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses.
The GZIP filter that I use to compress resources in my webapps:
public class CompressionFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String acceptEncoding = httpRequest.getHeader(HttpHeaders.ACCEPT_ENCODING);
if (acceptEncoding != null) {
if (acceptEncoding.indexOf("gzip") >= 0) {
GZIPHttpServletResponseWrapper gzipResponse = new GZIPHttpServletResponseWrapper(httpResponse);
chain.doFilter(request, gzipResponse);
gzipResponse.finish();
return;
}
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
public class GZIPHttpServletResponseWrapper extends HttpServletResponseWrapper {
private ServletResponseGZIPOutputStream gzipStream;
private ServletOutputStream outputStream;
private PrintWriter printWriter;
public GZIPHttpServletResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
response.addHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
}
public void finish() throws IOException {
if (printWriter != null) {
printWriter.close();
}
if (outputStream != null) {
outputStream.close();
}
if (gzipStream != null) {
gzipStream.close();
}
}
@Override
public void flushBuffer() throws IOException {
if (printWriter != null) {
printWriter.flush();
}
if (outputStream != null) {
outputStream.flush();
}
super.flushBuffer();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (printWriter != null) {
throw new IllegalStateException("printWriter already defined");
}
if (outputStream == null) {
initGzip();
outputStream = gzipStream;
}
return outputStream;
}
@Override
public PrintWriter getWriter() throws IOException {
if (outputStream != null) {
throw new IllegalStateException("printWriter already defined");
}
if (printWriter == null) {
initGzip();
printWriter = new PrintWriter(new OutputStreamWriter(gzipStream, getResponse().getCharacterEncoding()));
}
return printWriter;
}
@Override
public void setContentLength(int len) {
}
private void initGzip() throws IOException {
gzipStream = new ServletResponseGZIPOutputStream(getResponse().getOutputStream());
}
}
public class ServletResponseGZIPOutputStream extends ServletOutputStream {
GZIPOutputStream gzipStream;
final AtomicBoolean open = new AtomicBoolean(true);
OutputStream output;
public ServletResponseGZIPOutputStream(OutputStream output) throws IOException {
this.output = output;
gzipStream = new GZIPOutputStream(output);
}
@Override
public void close() throws IOException {
if (open.compareAndSet(true, false)) {
gzipStream.close();
}
}
@Override
public void flush() throws IOException {
gzipStream.flush();
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (!open.get()) {
throw new IOException("Stream closed!");
}
gzipStream.write(b, off, len);
}
@Override
public void write(int b) throws IOException {
if (!open.get()) {
throw new IOException("Stream closed!");
}
gzipStream.write(b);
}
}
You also need to define the mapping in your web.xml:
<filter>
<filter-name>CompressionFilter</filter-name>
<filter-class>com.my.company.CompressionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CompressionFilter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CompressionFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CompressionFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CompressionFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With