Monday, April 25, 2011

Creating HTML5 Cache manifests for GWT Apps

I've been working recently on making Springpad work offline using some of HTML5's new features and want to share a solution to a problem I ran into generating the cache manifest file. The cache manifest is a text file containing the version number of your manifest file and the paths of the files on your site that you wish to cache. For more specifics on how to set up an offline application with this file check out Mark Pilgrim's excellent Dive Into HTML5.

That's pretty simple. Generally, when you make a web application you know all of the resource names and locations and can add those to the manifest. Unfortunately, when you are using GWT, the situation is slightly more complicated. GWT compiles javascript files from your Java source code, one per browser platform you are targeting. It names these files by generating a checksum based on the contents of the file. Then it creates a simple javascript file that loads the correct javascript file based on the browser's useragent. So this means that everytime you make a change to your application and recompile, you will need to specify a different javascript file in your cache manifest.

You could do this by hand, but that's tedious and likely to be forgotten at some point. Alternatively, you could do some regex'ing of the bootstrapping file and figure out from that what javascript file to put into the manifest. This is better than doing it by hand, but seems hacky and painful since that javascript will be obfuscated in production.

The solution that we settled on was to implement a custom GWT linker to generate the manifest file. GWT allows you to create your own linkers to either override their linker or to supplement it. Here's the code for the linker which will find the compiled name for the javascript file that we want in our cache manifest and then output a cache manifest will that file in it. For right now, all we care about is the Safari user agent, so that's all that I'm handling. Other browsers also handle offline, so in that case you would want to modify this create a different manifest file for each user agent and then have your server serve the appropriate one based on the user agent in the GET request. I've added comments inline to explain how our version works. Hope this helps.