Guide: How to use Pack200 for your Java Web Start applications on Apache Web Server
Introduction
Lots of documentation exists about how to use Java 5’s new Pack200 JAR compression, with things like how to set up a Tomcat servlet to send its .pack.gz files with the right HTTP headers, and so on.
However, it is not clear how to use it in a more practical setting: an Apache web server. This weblog entry serves as a guide for how to get Pack200 working on Apache httpd. I don’t know if my method is the best way to do it, but it works. This method has been tested on Apache 1.3.31 and Apache 2.0.53.
UPDATE: I’ve updated the .htaccess file so it works on Apache 2.0 as well.
This guide assumes you have JAR files for your project’s classes (with extension .jar), and that you have Pack200-gzip-compressed versions (with extension .jar.pack.gz). If you don’t know how to compress your JAR files with Pack200, you can read more about Pack200 at Sun’s Java 5.0 documentation site. There is also a Pack200 Ant task, which makes it easier to integrate Pack200 compression into your build process.
Why you should use Pack200
Pack200 provides an insane amount of compression for your application’s JAR files. In my application’s case, Pack200+gzip compresses my 440KB jars to 89KB. This is an 80% improvement, saving my users 350KB worth of download time and bandwidth.
Your JNLP file
First, let’s talk about your JNLP file. The <resources> element should just reference your jars like you would normally:
<resources> <jar file="app.jar" /> </resources>
As you can see, your JNLP file doesn’t need to worry about Pack200; it will all be handled on the server side.
Apache server configuration
The Apache server’s configuration files are where all of the messy logic lies. In the folder where the JAR files will go, we must create an Apache .htaccess file, with these contents:
# Return the right mime type for JARs AddType application/x-java-archive .jar # Enable type maps AddHandler application/x-type-map .var Options +MultiViews # Tweak MultiViews - this line is for # APACHE 2.0 ONLY! MultiViewsMatch Any <Files *.pack.gz> # Enable the Content-Encoding header for .jar.pack.gz files AddEncoding pack200-gzip .jar # Stop mod_gzip from messing with the Content-Encoding # response for these files RemoveEncoding .gz </Files>
Again, this file should be called “.htaccess” - don’t forget the dot at the front. The file should be placed in the folder which corresponds to the URL for the JAR files referenced in your JNLP file.
Alternatively, these lines could be placed in a <Directory> element inside your main httpd.conf file. However, you may not have access to this file on your web host, so .htaccess will suffice.
Type maps
Next, we must make a “type map file” for each jar file. (For more information about type maps, see Apache content negotiation documentation. This guide should be all you need to know for this purpose, however.)
For each JAR file, create a text file with the same name as the JAR file, but with “.var” at the end. For example, for “file.jar”, the type map filename would be “file.jar.var”. Inside the text file, write this:
URI: packed/file.jar.pack.gz Content-Type: x-java-archive Content-Encoding: pack200-gzip URI: unpacked/file.jar Content-Type: x-java-archive
(Replace “file.jar” in this example with the name of the JAR file this type map is for.) Repeat this process for each JAR file in your application, so you have a bunch of files all ending with “.jar.var”.
Folder structure
Next, we will set up the subfolders that contain the JARs and packed JARs. This part is simple: make two subfolders, one called “packed” and one called “unpacked”. Place all of your original JAR files (with extension .jar) in the “unpacked” folder, and all of your packed JARs (with extension .jar.pack.gz) in the “packed” folder.
Conclusion
Your file structure should now look something like this:
- myapp/
- packed/
- core.jar.pack.gz
- ui.jar.pack.gz
- unpacked/
- core.jar
- ui.jar
- .htaccess
- core.jar.var
- myapp.jnlp
- ui.jar.var
- packed/
Now your server should be set up for serving your application’s JAR files to both Pack200-enabled Java 5.0 users, and to users of older versions of Java Web Start in regular JAR form. You can even visit yourserver.com/jarfolder/file.jar and see that the JAR file downloads just like it would without all this type map and Pack200 nonsense. However, when Java Web Start requests the JARs, it will receive a Pack200-compressed version.
This solution may not work for you for some reasons, but the most probable reason is that your web host has prevented you from using .htaccess files, or prevented you from using certain options (like type maps). If you can’t get it working, you may want to contact your web administrator.
If this solution doesn’t work for you, please let me know, either by email at keith@kano.net or by posting a comment below.
December 9th, 2004 at 9:29 am
Excellent, I never got around to experimenting with Apache.
-Kumar Srinivasan
December 29th, 2004 at 2:49 pm
Thanks for this post. I wanted to try PAck200 on my app but I don’t have Tomcat set up. I look forward to trying this.
Question- if one doesn’t care if people not using JDK 5.0 can access the app, is it okay to just provide the packed jars and forget about the fancy stuff? If you list the packed jars in the jnlp file and provide them on the server, will everything be handled automatically on the client?
December 29th, 2004 at 4:52 pm
I think it’s best to provide both packed and unpacked jars, so that if people need to download the jars manually, or some other (non-Sun) JNLP-based application that doesn’t support pack200 needs to download them, it will work.
March 12th, 2005 at 6:52 pm
Hi,
I needed to add another line to .var file:
URI: file.jar
URI: packed/file.jar.pack.gz
Content-Type: x-java-archive
Content-Encoding: pack200-gzip
URI: unpacked/file.jar
Content-Type: x-java-archive
as otherwise it was trying to access it like .var file.
May 27th, 2005 at 5:36 pm
Thanks for the information. This is very helpful.
One question, though, does this require the JnlpDownloadServlet to be deployed, or is it not required?
Thanks.
May 28th, 2005 at 9:43 am
No, the point of this is to avoid the servlet. However, I think the servlet supports JAR versioning, whereas this apache approach does not.
September 8th, 2005 at 8:52 am
Hi Keith,
Is it a requirement that the packed and unpacked JARs be in separate directories, or is that just something you did to “tidy up”?
Thanks,
Graham.
January 11th, 2006 at 2:34 am
Graham, I did that to keep things clean.
January 17th, 2006 at 1:32 pm
Thanks, I think it worked great..
Great work.
But how can we be sure that it is calling indeed the compressed jar ?
January 17th, 2006 at 1:36 pm
jcorreia, I used a tool like ethereal to packet log, to be sure.
January 20th, 2006 at 12:04 pm
Hi,
Is this valid for applet“s too ?
Thanks
January 20th, 2006 at 12:09 pm
John, I haven’t tried it with applets, but I think it would work just the same.
February 22nd, 2006 at 11:45 am
The configuration above seems to only work for me if I actually call the file.jar.var name specifically then it will negotiate which file to grab depending on what content-encoding I specify.
For anyone wondering on a linux command line you can do this…
curl -O http://webserver/file.jar.var –header “content-type: application/x-java-archive” –header “accept-encoding: pack200-gzip,gzip”
If you leave out the second header you will get the unpacked version.
Any ideas, anyone?
April 12th, 2006 at 4:51 am
How can i achieve the same with the Jboss app server which user tomcat as web server. I am desoerately looking for replies