Monday, April 21, 2008

MarkUtils-Web: ZipServlet and CompressFilter

"MarkUtils-Web" is an initial collection of web-related Java utilities that I wrote and am releasing on ziesemer.java.net for public use (GPL).

The source code, a compiled .jar, and generated JavaDocs are all included in "com.ziesemer.utils.web-*.zip". (Download)

At the moment, there are 2 components:

ZipServlet

My idea for ZipServlet came while working with YUI (and previously Ext JS) in a J2EE application. Typically, when including Java components, one or few JAR libraries are imported into the application. No real equivalent really exists for web content.

Full support of most JavaScript libraries require a sizable collection of files - not only JavaScript, but CSS, images, and other resources as well. For example, for full support of Ext JS 2.0.2, including the several required .js scripts along with .css and image resources, this amounts to 1,818 files across 174 folders, totaling 21.9 MB. While Ext JS provides some other options, including custom builds, each poses dependency and other challenges. Even including only the core 2 .js files, 1 ext-all.css, and possibly 196 image files still seems a bit excessive.

Including any large number of files in a project can quickly lead to difficulties, particularly with source control. While utilizing the extracted layout would easily allow developers to patch and modify the included project, such practice is typically not a good idea and would likely lead to additional upgrade complexities as previous changes would have to be found and migrated. Just the upgrade itself would require the replacement of potentially hundreds of files!

My more ideal solution was to simply include YUI's distribution .zip into my project's WebContent/WEB-INF folder. My ZipServlet then serves any YUI-related files requested by the client directly out of the .zip file. While this leads to additional uncompression that can require additional CPU time, I've not encountered any performance issues. Client-side caching is usually "on" by default. Additional server-side caching could additionally be used. Another option would be to re-package the .zip file without compression. (The size of the .zip file would be larger, but it would still be a single .zip file.)

Shown below are the relevant sections out of my web.xml file, allowing yui_2.6.0.zip to appear just like a folder stored on my web server. For example, with the configuration listed below, "<contextRoot>/build/yahoo-dom-event/yahoo-dom-event.js" will return oneof YUI's primary JavaScript files:

<servlet>
 <servlet-name>yui_2.6.0</servlet-name>
 <servlet-class>com.ziesemer.utils.web.ZipServlet</servlet-class>
 <init-param>
  <param-name>file</param-name>
  <param-value>/WEB-INF/yui_2.6.0.zip</param-value>
 </init-param>
 <init-param>
  <param-name>zipPrefix</param-name>
  <param-value>yui/</param-value>
 </init-param>
</servlet>

<servlet-mapping>
 <servlet-name>yui_2.6.0</servlet-name>
 <url-pattern>/yui_2.6.0/*</url-pattern>
</servlet-mapping>

See the included JavaDocs for further details on these options.

ZipServlet follows the standards described in RFC 2616 as applicable. In particular, this implementation supports last modified checking and partial content requests.

Similar to YUI, some other libraries I've included this way include Firebug Lite and The Dojo Toolkit.

CompressFilter

The idea behind CompressFilter is explained fairly well in "Two Servlet Filters Every Web Application Should Have" (Jayson Falkner, 2003-11-19, ONJava.com). The sample code included on ONJava.com is a bit lacking and requires some improvement, but to be fair, it is over 4 years old.

Notable features of my CompressFilter include:

  • Following the standards described in RFC 2616 as applicable, particularly sections 3 and 14.
  • Support for both gzip and deflate, while respecting the priorities both requested by the client and configured on the server.

Configuring a CompressFilter into an application requires only a <filter/> and one or more <filter-mapping/> elements in web.xml. It is recommended to include <dispatcher>REQUEST</dispatcher> into <filter-mapping/> to prevent output from being compressed multiple times (e.g. from <jsp:include/>), as defined in the Java Servlet 2.4 Specification (see SRV.6.2.5).

See the included JavaDocs for further details. Also see the update information.

2 comments:

Anonymous said...

Very nice. This seems like a great solution to a couple of problems I've been trying to solve lately. Great timing!

Thanks!

Unknown said...

Thanks a lot! I decided I'd rather serve some of my website mirrors (which are for my own use only for when I don't have wifi) out of zipfiles. My other option is to gzip everything which seems lacking. This way I have the entire website in a zip archive. I need a better mirroring tool than wget, and also a mirroring tool that can manage my zipfiles for me. To be continued!