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 -
- Automatic CSS DataURI image inlining for supported browsers
- Automatic CSS MHTML image inlining for supported IE browsers
- LessCSS compilation for .less files
- Automatic YUI compression of .css & .js resources
- Optional: DataURI inlining of <img> tags in HTML responses
So as you can see, the primary purpose of this filter is web app resource optimization.
DataURIs and MHTML?
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:
- 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.
- 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 -
- Supported features will be detected by using visural-common’s built in WebClient detector (based on User-Agent)
- Resource URLs ending in .less will be compiled through the LessCSS compiler
- Resources ending in .less or .css will have DataURIs or MHTML applied, but only if the client supports them, otherwise the original image references will be left untouched.
By default, only images < 50kb in size will be inlined, any larger images will be left as url(..) references. This is configurable. - Resources ending in .less or .css will be compressed through the Yahoo YUI CSS Compressor
- Resources ending in .js will be compressed through the Yahoo YUI Javascript Compressor
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 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
