Sunday, January 11, 2009

New version of MarkUtils-Web: ZipServlet and CompressFilter

If you're not already familiar with my ZipServlet and CompressFilter, see my previous posting where I introduced these Java web utilities.

As with the previous release, the update is available on ziesemer.java.net. The release folder is directly available here. "com.ziesemer.utils.web-2009.01.11.zip" contains the source code, a compiled .jar, and generated JavaDocs.

New in this release are a number of fixes and enhancements. To report any new bugs or enhancement requests, please use the issue tracker on ziesemer.dev.java.net.

ZipServlet combo mode

ZipServlet now provides a feature almost identical to YUI's Combo Handling. This allows for multiple files to be requested and sent joined together in one response, which can reduce the number of HTTP requests and improve performance, as detailed in this post on yuiblog.com.

This feature works particularly well for JavaScript and CSS files. However, only files of one type should be requested together, otherwise the returned MIME Content-Type HTTP header wouldn't make any sense. Unlike the current implementation provided by yahooapis.com, ZipServlet enforces this by returning a HTTP 400 error (Bad Request) if multiple files are requested that have different content types.

The combo mode is enabled by default in ZipServlet, but can be disabled by using the "comboEnabled" servlet parameter. The path on which combination requests are answered can also be configured through the "comboPath" servlet parameter. comboPath defaults to "combo", as used by Yahoo! / YUI. Additional details are listed in the JavaDocs for ZipServlet, included in the download.

A notable difference between ZipServlet and the Yahoo! / YUI implementation is that YUI files require the version to be prefixed to each requested file, e.g. "/combo?2.6.0/build/yuiloader/yuiloader-min.js&2.6.0/build/dom/dom-min.js&2.6.0/build/event/event-min.js". This presumably allows for multiple files to be requested across different versions. This doesn't make a lot of sense, and results in a longer URL as the version is prefixed before each requested file. As ZipServlet is designed to have each instance associated with one .zip file, as each version of a resource (YUI, etc.) belongs in its own file, and due to implementation details, it made sense to include the "combination" functionality directly into ZipServlet rather than as an additional filter, etc. This restricts combination requests to a given ZipServlet instance, while improving code reuse, performance, the URL length, and other aspects. The equivalent URL to the above when requested from ZipServlet, when using the example configuration in the previous post, including the "yui/" zipPrefix, would be "/combo?/build/yuiloader/yuiloader-min.js&/build/dom/dom-min.js&/build/event/event-min.js". Additionally, the leading slash before each file is optional in the ZipServlet implementation.

Unit Tests

New in this release is a complete suite of JUnit tests. I previously had not included any tests partially due to finding a unit testing solution for Java EE servlets and filters that met my requirements. I had looked at HttpUnit ServletUnit, but found a number of shortcomings. While I had previously been testing under Apache Tomcat, this required manual starting and stopping of the server, and registration of the projects. There are methods to automate this, but none that didn't seem overly complex and without their own shortcomings.

I finally decided upon using Jetty. Jetty is both free and open-source, and is 100% Java which makes it very portable. It is very representative of a production servlet engine as it is one, used by many projects including the JBoss and Apache Geronimo application servers. Jetty fully supports embedded within a Java application, which makes it very suitable for unit testing. It can be configured either declaratively or by using standard web.xml files, with details and several examples available at http://docs.codehaus.org/display/JETTY/Embedding+Jetty. As an added bonus, Jetty is readily available as an Apache Maven artifact, including the latest release and beta versions. This means that if opening the project with Maven support, such as with m2eclipse, Jetty and any other dependencies will automatically be downloaded and included on the testing classpath.

For this project, I wrote a ServletTester class that is reused by all the test classes. Each instance starts a Jetty instance on a dynamic port on the loopback address (127.0.0.1), rather than requiring that a specific be used or configured. This class then provides convenience methods for obtaining the server, connector, context, and base URL.

There are 2 types of tests that I provided for each component (ZipServlet and CompressFilter) - standard tests and configuration tests. The standard tests initializes and holds on to a Jetty instance within a ServletTester as a static field, which is reused by all the test methods. This is mainly for performance reasons, so that a new server isn't required for every test. The configuration tests deal specifically with testing the configuration options available for each component. While these configuration tests still reuse the same server instance, the server's context is restarted with a new ServletHandler for every test.

For the details of this methodology, please feel free to download the code and see for yourself. These tests also demonstrate the features and usage of the components.

2 comments:

Anonymous said...

For testing from client-side, HtmlUnit is far more superior than HttpUnit.

Mark A. Ziesemer said...

Anonymous - if you're going to make a claim like that, at least have the courage to put your name behind it.

While you may be correct, both HtmlUnit and HttpUnit have their advantages and disadvantages. Both are free and open source. The only reason I was looking at either framework for this purpose was the ServletUnit support provided by HttpUnit. In this case, HtmlUnit doesn't provide an equivalent feature.

While both frameworks are designed for testing web pages, I needed to test at a higher level (HTTP vs. HTML), testing HTTP response headers and the like. As far as I see, HtmlUnit doesn't provide anything for this, and I quickly decided against the ServletUnit support provided by HttpUnit for this project.