On the fly JavaScript minification using Dojo ShrinkSafe

The Dojo ShrinkSafe is a tool that you can use to minify JavaScript, it can reduce size of JavaScript by close to 1/3 rd or more. You can call ShrinkSafe through commandline, or through Ant as part of your web applications build process. I wanted to see if i can call ShrinkSafe Java API to minify JavaScript.

I built ShrinkSafeFilter which will be part of my .war file and then i applied it to JavaScript URLs and what the ShrinkSafeFilter will do is it will collect the JavaScript file response into String minify it using ShrinkSafe API and return the minifed version to the browser, you can download the sample code from here

My Web application has index.html that includes three JavaScript files which are inside the /WebContent/js folder, this is the screen shot of how the test.js file looks like in the firebug




These are the steps that i had to follow to create ShrinkSafe Filter

  • First i had to create a ShrinkSafeFilter like this


    package com.webspherenotes.performance.minfy;

    import java.io.IOException;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.dojotoolkit.shrinksafe.Compressor;

    public class ShrinkSafeFilter implements Filter{


    public void destroy() {
    System.out.println("Entering ShrinkSafeFilter.destroy()");
    System.out.println("Exiting ShrinkSafeFilter.destroy()");
    }

    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain filterChain) throws IOException, ServletException {
    try {
    System.out.println("Entering ShrinkSafeFilter.doFilter()");

    System.out.println("Received request for " + ((HttpServletRequest)request).getPathInfo());
    SSHttpServletResponseWrapper responseWrapper = new SSHttpServletResponseWrapper((HttpServletResponse)response);
    filterChain.doFilter(request, responseWrapper);
    responseWrapper.finishResponse(response.getWriter());
    System.out.println("Exiting ShrinkSafeFilter.doFilter()");
    } catch (Throwable e) {
    e.printStackTrace(System.out);
    }
    }

    public void init(FilterConfig arg0) throws ServletException {
    System.out.println("Entering ShrinkSafeFilter.init()");
    System.out.println("Exiting ShrinkSafeFilter.init()");
    }

    }

    In the doFilter() method of ShrinkSafeFilter i am wrapping the HttpServletResponse object into the SSHttpServletResponseWrapper method, and then forwarding control to application server for letting it written the static JavaScript file

  • This is how my SSHttpServletResponseWrapper.java looks like
    package com.webspherenotes.performance.minfy;

    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;

    import org.dojotoolkit.shrinksafe.Compressor;

    public class SSHttpServletResponseWrapper extends HttpServletResponseWrapper{

    StringOutputStream stringOutputStream;

    public SSHttpServletResponseWrapper(HttpServletResponse response) {
    super(response);
    }


    public ServletOutputStream getOutputStream() throws IOException {
    stringOutputStream = new StringOutputStream();
    return stringOutputStream;
    }

    public void finishResponse(PrintWriter writer){
    String inputJavaScript = stringOutputStream.getString();
    long beginTime = System.currentTimeMillis();
    String outputJavaScript = Compressor.compressScript(inputJavaScript, 0, 1, "all");
    System.out.println("Time took to compress " +( System.currentTimeMillis() -beginTime));
    writer.println(outputJavaScript);

    }

    }

    In the getOutputStream() method i am returning a custom ServletOutputStream object that will be used for collecting the actual response into String. Then inside the finishResponse() method i am taking the actual JavaScript output returned by application server into String and calling the Compressor.compressScript(inputJavaScript, 0, 1, "all") method of ShrinkSafe to minify it.

  • THis is how my StringOutputStream class looks like

    package com.webspherenotes.performance.minfy;

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;

    import javax.servlet.ServletOutputStream;

    class StringOutputStream extends ServletOutputStream {

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    public void write(byte[] b, int offset, int length) throws IOException {
    byteArrayOutputStream.write(b, offset, length);
    }

    public void write(byte[] b) throws IOException {
    byteArrayOutputStream.write(b);
    }

    public void write(int b) throws IOException {
    byteArrayOutputStream.write(b);
    }

    public String getString() {

    return new String(byteArrayOutputStream.toByteArray());
    }

    public void close() throws IOException {
    byteArrayOutputStream.close();
    }

    public void flush() throws IOException {
    byteArrayOutputStream.flush();
    }

    }

    This is simple ServletOutputStream class that collects the response in string



This is how the firebug output looks like after i applied the ShrinkSafeFilter to it, did you notice the shinksafe has removed comments from the test.js and also the indentation.

1 comment:

buyi wen said...

i find a free online service to compress js and minify css, so it will reduce the size of web page.