Combining Ant, Jar, Signatures and Pack200

Signing and Compressing your code for applets

Note: a more up-to-date version of some of the information below is in this article.

It's been a while since we've done an article, so we thought we'd fill the gap with a general purpose guide to compressing your Java applications for use with Applets. The techniques below can be used with any Java application, not just one using BFO classes.

Pack 200 Compression

Pack 200 is a compression algorithm specifically designed to compress Java .class files, giving much better compression than the regular "zip" compression used with Jar files. It works with Java 1.5 or later.

The compressor works by modifying and rearranging the class files in the Jar, which leads to problems when the Jar is signed, as the process invalidates the digital signature.

There are more detailed articles on this, and we're not going to cover that ground here. What we are going to do is give you a step-by-step guide to a) creating signed, pack200 compressed Jars with ant, and b) detail how to serve those Jars to an applet.

Using Pack200 with Ant

The first thing you need to do is add the pack200 task to your ant installation - download the Zip file, extract the Pack200.jar file and then add the line

<taskdef name="pack200" classname="com.sun.tools.apache.ant.pack200.Pack200Task" classpath="path/to/Pack200Task.jar"/>
to your build.xml ant file.

Then you need a task that will take a Jar and create a digitally signed, pack200 compressed version of that Jar. Here's the one we use:

<!--
  Sign the ${jar} Jarfile and create the file ${jar}.pack.gz
-->
<target name="pack">
 <echo file="pack200.conf" append="false">
com.sun.java.util.jar.pack.verbose=0
com.sun.java.util.jar.pack.package.majver=150
com.sun.java.util.jar.pack.package.minver=7
pack.effort=9
pack.segment.limit=-1
 </echo>
 <pack200 stripdebug="true" configfile="pack200.conf" src="${jar}" destfile="${jar}.tmp.jar" repack="true"/>
 <move file="${jar}.tmp.jar" tofile="${jar}"/>
 <signjar jar="${jar}" alias="${alias}" storepass="${password}" keystore="${keystore}"/>
 <pack200 configfile="pack200.conf" gzipoutput="true" src="${jar}" destfile="${jar}.pack.gz"/>
 <delete quiet="true" file="pack200.conf"/>
</target>

Yes, those temporary files are ugly but we couldn't find a better way to this, and the approach above will work.

The Jar must be repacked before signing, and the final pack after signing must not modify the signed classes in any way (ie by stripping debug information). The parameters passed into the pack200 task are undocumented, but will ensure the compressed Jar file will work with Java 1.5, even if the JVM used to build the Jar is 1.6.

Feel free to download the above XML snippet to integrate into your build script.

Serving Pack200 files as applets

For clients running Java 1.6u10 or later, no work is required - when an applet tag references jarfile, the browser will first look for the file jarfile.pack.gz and use that if it's available. So all you need to do us put your pack200 compressed files in the same directory as the Jar files for your applet.

For clients running earlier versions of Java, the server will need to intercept requests for the Jar files used by the applet and serve the pack200 compressed version instead if it's appropriate. Sun provide a Servlet to do this called the JnlpDownloadServlet - the quick guide to getting it running with a standard Servlet engine like Tomcat is:

  1. Compile JnlpDownloadServlet.java and add it to your WEB-INF/classes file. You can get it from the sample/jnlp folder of the Sun JDK package, or you can find a copy on the web.
  2. Modify your web.xml file so that *.jar files are served with this Servlet. You need to add the following lines:
    <servlet>
      <servlet-name>JnlpDownloadServlet</servlet-name>
      <servlet-class>jnlp.sample.servlet.JnlpDownloadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>JnlpDownloadServlet</servlet-name>
      <url-pattern>*.jar</url-pattern>
    </servlet-mapping>
    

Conclusion

If you're deploying to an Applet, we believe Pack 200 compression is the single most important thing you can do to optimize your applet. With our PDF Library Jar we're able to reduce the download to 30% of the original size.

It would be nice if Sun Oracle did a better job of packaging and promoting this, or if the Ant project provided a better integrated "pack200" task out of the box. Until then we hope this guide will prove useful.