Blog About Contact

ResourceTransformFilter - DataUris, LessCSS, Javascript and CSS Compression Made Easy!

Published Tue, 27 Jul 2010 • 13 comments

ResourceTransformFilter is a swiss-army-knife for HTML/CSS/Javascript transformation in Java web applications.

It's a new addition to the visural-common Apache 2.0 licensed project.

Apply this filter in your Java webapp to get -

So as you can see, the primary purpose of this filter is web app resource optimization.

DataURIs and MHTML?

by http://www.flickr.com/photos/andresrueda/One of the most effective ways of improving website performance is to reduce the number of HTTP requests required for the page to fully load.

Typically you'll have a bunch of small icons used for buttons, panels, links etc. in your CSS, as well as small background graphics here and there. These all add us to a significant number of HTTP requests, each one for only a couple of kilobytes of data.

"CSS Sprites" have often been used as a way of overcoming this issue, however two newer techniques (data URIs and MHTML) are starting to become a more common way to address the problem.

The idea of data URIs and MHTML are the same - inline the image data into the actual CSS (or HTML) as base64-encoded text.

Data URIs are the preferred way of doing this, however they are not supported in older browsers, specifically IE6 and IE7. Also, IE7 on Windows Vista has problems, and I've talked about this in more detail recently. MHTML can be used reliably in IE6/7 on Windows XP, and so the filter will apply it for those clients.

Applying the Filter

I'll cut right to the chase - here's how you can apply this to your Java web-application in two simple steps:

  1. Add visural-common to your projects set of libraries, or adding it as a dependency (Maven). You will also need to add the YUI Jar to your project to use CSS / JS compression.
  2. Add com.visural.common.web.transform.ResourceTransformFilter to your your web.xml as a standard servlet filter, matched to the URLs of your resource file(s)

For example you might add the following XML your web.xml:

    <filter>
        <filter-name>ResourceTransformFilter</filter-name>
        <filter-class>com.visural.common.web.transform.ResourceTransformFilter</filter-class>
    </filter>
...
    <filter-mapping>
        <filter-name>ResourceTransformFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

And you're done.

Default Configuation

The default configuration works as follows -

Transformed responses will be automatically cached, so any additional overhead to generate these responses will be a one-time only cost. Note that any response headers you set will also be cached, and the filter does not affect the detection and dynamic behaviour based on the client browser/OS.

Custom Configuation

You can customise the behaviour of the filter by extending the filter's class com.visural.common.web.transform.ResourceTransformFilter and overriding the protected "over-ride points" to change the classes behaviour.

Use the source as the starting point.

Optional .html DataURI Conversion

The default configuration does not do auto-conversion of HTML <img> tags to DataURI's.

The reason for this is, that it is not always possible to reliably detect HTML responses, without a performance cost, and additionally, the CPU, memory and bandwidth overhead to doing this can be undersirable.

Unlike .CSS files, HTML responses are typically dynamic and can't be cached. Additionally, images in the response may be from external servers, and may be larger than the types of image typically referenced from CSS.

For this reason, you will need to evaluate your circumstances to determine if enabling HTML inlining is a good idea in your application.

If you do want to enable it, you could do so like in the following example -

import com.visural.common.web.client.WebClient;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;

public class MyResourceTransformFilter extends com.visural.common.web.transform.ResourceTransformFilter {
    @Override
    protected List<Transform> getTransforms(HttpServletRequest req, WebClient client) {
        String url = req.getRequestURI();
        String last = url.substring(url.lastIndexOf('/'));
        if (last.contains(".")) {
            return super.getTransforms(req, client);
        } else if (client.supportsDataUris()) {
            return Arrays.asList(Transform.HTML_DATAURI);
        } else {
            return null;
        }
    }
}

There are several other override points, included in the filter, so consult the source or drop me an email if you'd like to customise further.

Summary

So with this one simple addition we get framework-agnostic image inlining, compilation and compression which responds intelligently to the user's choice of browser.

This has been live on onmydoorstep.com.au for a week or two is working out well thus far.

Great, I don't use servlet filters!

There is also a command-line version of the DataURI and MHTML converters which you can apply to non-JVM based projects as part of a build-script or manual process.

If you've downloaded the commandline version you can run it by:

  java -cp visural-common.jar com.visural.common.web.css.CSSDataUri [input-file.css] [output-file.css]

This will convert the [input-file.css] and inline the image data into the output. Note that the relative URLs are assumed to be resolvable relative the location of the CSS on disk. Mostly this will work just fine, and it will even attempt to resolve http:// urls.

The one thing that won't work with the command line version is root-relative urls, e.g. url("/folder/myimage.png"), since there's no way to know what the root is, in the context of the disk. I could fix this is a future version by adding another command line option, so if you need it, let me know.

Similarly, you can run the MHTML converter from the command-line as so:

  java -cp visural-common.jar com.visural.common.web.css.CSSMHTML [input-file.css] [output-file.css] [http://serverurl/for/css]

For this one, you need to supply the additional server URL for the CSS file. This is because MHTML file references need the full URL to the defining CSS file (relative URLs don't work).

This is one of the reasons that I chose to implement the solution as a filter.

Anyhow, given the simplicity of this filter, there's pretty much no reason not to use this in your Java web app and improve those YSlow / PageSpeed results and rating without very little effort :)


About the Author

Richard Nichols is an Australian software engineer with a passion for making things.

Follow him on twitter or subscribe by RSS or email.

You might also enjoy reading -


Discuss / Comment

There are 13 comments.

Add a comment

  • {{e.error}}

Thanks for your comment!/

Required.
Valid email address required.
Required.
Posting message, please wait...