Java classes forming an application, Applet, or weblet can be bundled up into something that looks very much like a ZIP file. JavaBeans are also packaged in JAR files. In
JDK 1.1+ compression is turned on by default and in earlier versions it was always off. It is faster to download a group of classes as a single JAR file since HTTP has high
overhead to set up a connection for each file transferred. The tools Jar.exe and jarsigner.exe come bundled
with the JDK.
Jar vs Zip
A JAR file differs from a standard ZIP in that:
- It includes a manifest member: meta-inf/Manifest.mf, that describes the JAR’s contents and provides checksums. The
manifest is just a zip member you can read in the ordinary way, or access with
Sun’s Javadoc on
java.
util.jar.Manifest : available:
- Jar files always use / as the separator between directory names, never \, however WinZip will display them as \. You can check what they really
are / with a hex viewer such as SlickEdit. Directory names must precisely match package names, case sensitive.
- In JDK 1.0.2 the files inside a jar were not compressed. In JDK 1.1+ they usually are. Jars don’t support all the PKZIP compression formats. In
JDK 1.5+ there is also the pack200.exe supercompressor.
- The directory includes long filenames, not just 8+3.
- Jars may optionally be signed. For Netscape-style signing you will see two extra files:
- zigbert.rsa which contains your public jar-signing key and a stripped down version of your jar-signing certificate. It
contains your public key, your company name, your certificate expiry date, your signing authority’s public key, your signing authority’s name, and your signing
authority’s expiry date. Your private key is not present. Your certificate is digitally signed with the signing authority’s private key. By that I mean, the
checksum of your certificate is encrypted with the signing authority’s private key. It can be decrypted with the signing authority’s public key to verify the
signature. The browser’s public key is pre-installed in the browser for verification.
- zigbert.sf which looks much like a manifest file. It contains the digital signatures of each member of the jar file. These are encrypted with your
certificate’s private key. They can be decrypted for verification with your certificate’s public key. Netscape computes them a slightly different way from Sun.
Sun’s jarsigner and Netscape 4.79’s signtool 1.1, 1.2 and 1.3 produce different information and layout
in the JAR archives. For details of how this information is used to verify a signed jar, see certificate.
If the jar is digitally signed there is the possibility of Applets escaping the restrictions of the sandbox. You don’t need to sign your jar files if your Applets are
willing to live with the usual security restrictions. Since Netscape, Microsoft and Sun use incompatible signing techniques, you may have to sign your jar file multiple times
or have separate jars for each of the bewildering array of signing and security schemes. To make matters worse, Internet Explorer ignores signing in jar files. It only pays
attention to it in cab files. When the elephants fight, it is the grass that suffers. See signed Applets for more information. Today, the
solution is to ignore Microsoft-style signing and the Microsoft Java implementation. When you install Sun Java in IE, it will use Sun-style signing.
- For Sun signed jars there are two extra files phony.dsa (a binary representation of your public key and of your signing certificate), and phony.sf
which looks like a duplicate of the manifest except the hashes values are different. Name phony matches your cert alias, so you might like to pick one that looks better
to the public. Why are both the manifest hashes and the phony.sf hashes needed? I suspect the manifest hashes are simple hashes and that the phony.sf
hashes are actually encryptions of those hashes.
- Starting with JDK 1.2, a jar may be executable by designating one class in it as the official class to start when the jar as a whole is executed. The key is an attribute line
in the manifest file of the form:
Main-Class: com.mindprod.mypackage.MyClass
See Main-Class for details. If you then associate *.jar files with java.exe you can just write myJar.jar
on the command line and it will automatically start the program, normally using javaw.exe rather than java.exe (i.e. no
console!).
- JDK 1.2+ has many new classes for handling jars. java.net.JarURLConnection lets you access a remote jar just as if it were a remote sequential file
by using a new URL syntax: jar:http://www.foo.com/bar/baz.jar!/com/foo/Quux.class. The !/ separates the member name from the filename. java.util.jar.JarFile
lets you randomly process local jar files. java.net.URLClassLoader lets you load classes from arbitrary jar sources, not just the archive jars.
- There are additional *.DSA, *.RSA and *.SF members to handle jar signing.
Tips on Using Jar Files
- The directory path names stored inside the Jar should exactly match the package names, no extra qualification. There must be no drive letters. jar.exe
won’t warn you if you screw up! They must match in case precisely as well. Check with WinZip that you have not inadvertently
included C:\ in your directory/package names. Whatever you put on the command line goes in raw as the path. You must thus juggle your current
directory before using jar.exe to make things work out.
- You can put a data file inside the Jar file (a resource) and access it with code like this:
InputStream fis = MyApp.class.getResourceAsStream( "InWords.properties" );
where InWords.properties is the name of the file inside the jar file you want to read. Class.getResource makes these
changes to the resource name: if the resource name starts with "/", it is unchanged; otherwise, the package name is prepended to the resource name after converting "."
to "/". This allows you to use either dots or slashes to separate the components of the name.
Class.getResource is broken, in that Netscape 4.0.x does not support it. Now that Netscape 8.0 is available, perhaps we can ignore that restriction.
To get information out of jar files other than the main jar, you would need to use ZipFile and ZipEntry.getInputStream.
To see how to fish *.gifs and jpgs out of jar files, see Image.
- Names of resources and their paths are case sensitive in a jar. You may have been lulled into a false sense of security because, under Windows,
resources outside the jar are case-insensitive. When you pack your application into a jar, it stops working.
- See the java.util.zip.ZipOutputStream class to write jar files. Normally you manipulate them with jar.exe or WinZip.
- Microsoft Internet Explorer with Microsoft Java (now increasingly rare) does not support digitally signed jar files. It uses Microsoft-proprietary single-platform cab files
instead. However, recent versions can deal with unsigned jar files. With the Sun Java Plug-in, IE can also deal with Sun-style signed jars.
- If you use the -jar option, java.exe ignores the classpath. It will only look in that jar.
- Beginning with JDK 1.2.2 it is no longer possible to use a Jar file in which some classes of a package are signed and others are unsigned or signed by a different signer. See signed
Applets.
Building Jar Files
You can create or read jar files with the java.util.zip classes, or with Sun’s Jar.exe. utility.Sun’s JDK Tool Guide to
Jar : available:
You can download the JDK documentation pack. Visual Cafe has an excellent jar builder. You can also use WinZip
version 6.3 or later to view or delete files. Unfortunately WinZip there are several problems in using WinZip
to create jar files or to add files to them. I have written the WinZip people several times asking for some simple extensions to make WinZip
suitable for dealing with jar files, but each time they have refused.
If you are using packages, for jar.exe you must be in the root directory when using jar.exe to ensure your
package names get properly included because the names you give jar.exe on the command line are the names it will blindly use for your packages
inside the created jar file. You don’t want any names like C:\com\mindprod\mypackage.MyClass.class or \com\mindprod\mypackage.MyClass.class
or MyClass.class (unless you have no packages). You want names like com\mindprod\mypackage\MyClass.class which translates
to com.mindprod.mypackage.MyClass.class on the command line. They have to make sense both as filenames and as fully qualified package/class names.
If you are not using packages, you must be in the directory where the class files are when you build your jar. That way the filename MyClass.class
will be the same as the classname MyClass.class.
Here is some typical code to build a jar file.
See Main-Class for details on how to create the main.mft file. See jar.exe for another explanation
of how to use it. You create a main.mft file to help generate an /META-INF/MANIFEST.MF member. You don’t create the /META-INF/MANIFEST.MF
directly.
Starting in JDK 1.5, you can super-compress jar files using pack200.exe. This will optionally remove debug information,
and compress the jar as a whole.
Executing Jar Files
One of the most useful associations to set up is to make *.jar
files executable with java.exe (see below for details how). Then
you can just type the name of the jar on the command line to start it executing. For JDK 1.4+, after you have built the
Main-Class entry, and set up an association of the *.jar
extension to C:\Program Files\java\jre6\bin\java.exe, all
you need to do to run the jar is:
converter.jar
or
java.exe -jar converter.jar
Watch out. Java install sometimes sets the association back to javaw.exe,
no console!
For JDK 1.1, you need a little more, something like this:
To run the jar in an Applet, see Applet for some sample HTML or see
on doing it.
Making Jar Files Double Clickable
In W98/Me/NT/W2K, you can set up the extension *.jar
so that when you double click on a jar file in the disk explorer it will start executing the main-class
of the jar. In XP/W2K3/Vista, you need to buy an associations editor utility such as Creative
Element Power Tools.
- Click Start ⇒ Disk Explorer ⇒ View ⇒ Options ⇒ File Types ⇒ Executable Jar
file ⇒ edit.
- If there is no Executable Jar file entry click new to create one and
fill in the Description of type to jar - Java executable.
- Change the name of the entry from Executable Jar file to something that will sort where you
expect it like jar - Java executable.
- Change the content type to the proper MIME type application/java-archive.
See MIME for details.
- Set default extension for Content type to .jar.
- Click open ⇒ edit .
- If there is no open, Click new and set the action to open.
- Set the application to perform to: "C:\Program Files\java\jre6\bin\javaw.exe"
-jar "%1". You can browse or adjust as necessary to account for where you put your javaw.exe.
javaw.exe will hide the console output. To see the console output, associate java.exe
instead. I always associate with java.exe myself. Without the console output, if anything goes
wrong, you have no clue what the problem is.
- Uncheck DDE.
- Click OK.
- Click new and set the action to execute.
- Set the application to perform to: "C:\Program Files\java\jre6\bin\javaw.exe" -jar "%1".
You can browse or adjust as necessary to account for where you put your javaw.exe
- Uncheck DDE.
- Click OK.
- Click new and set the action to edit.
- Set the application to perform to: "C:\Program Files\WinZip\winzip32.exe" "%1".
You can browse or adjust as necessary to account for where you put your WinZip program.
- Uncheck DDE.
- Click OK.
- Click execute ⇒ set default.
- Click always show extension.
- click change icon. Choose a nice icon. Perhaps some friendly artist could provide me with an jar.ico
file I could share with everyone.
- click close, close.
- Write a letter to Microsoft asking them to automate this bubblegum. Tell them to end the warring between apps. Allow
more than one app to register per function. The ones you use most often should bubble to the top. Tell them that whether
apps are properly installed or uninstalled, this information should be self-healing.
The Manifest
The manifest is a member file inside a JAR file describing the contents of the JAR archive. It always has the name /META-INF/MANIFEST.MF.
For a Netscape 4.79 digitally signed Jar file, it looks like this: An unsigned version quite simple in that it contains just three lines:
Manifest-Version: 1.0
Created-By: 1.4.0 (Sun Microsystems Inc.)
Main-Class: com.mindprod.bio.Biorhythms
A Sun signed version is more complex in that it also contains a hash for each member put here by jarsigner.
Manifest-Version: 1.0
Created-By: 1.4.0 (Sun Microsystems Inc.)
Main-Class: com.mindprod.bio.Biorhythms
Name: com/mindprod/bio/Biorhythms.class
SHA1-Digest: ueEw1mJ4aOXT9vmosR0nM/eUt6Y=
Name: com/mindprod/bio/SelectableDate.class
SHA1-Digest: 8HylCo90The8D1mBuYEEOFQDUsY=
A Netscape signed version is more complex still in that in has two different hashes for each member put there by signtool.
Manifest-Version: 1.0
Created-By: Signtool (signtool 1.3)
Comments: PLEASE DO NOT EDIT THIS FILE. YOU WILL BREAK IT.
Main-Class: com.mindprod.Wassup.Wassup
Name: cmp/Wassup/Wassup$1.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: VqONNAXcXR+ZRhTUeRQFjw==
SHA1-Digest: ZeKujl0SBvAlLpjN8NQIzh43cJs=
Name: cmp/Wassup/Wassup$2.class
Digest-Algorithms: MD5 SHA1
MD5-Digest: qPjkzkE3GQvNfI9NvFrj3A==
SHA1-Digest: tj7Ftc7psa6Rbq0NI7q6il2hwGs=
The specification document is very loose about the format of a manifest file. Here are the rules I have gleaned from experimentation:
- It is a simple text file embedded in the jar as member name meta-inf/Manifest.mf
- Only forward slashes are used, no \, not even for Windows.
- The jar.exe program automatically generates the Manifest-Version, Digest-Algorithms, SHA-Digest
and MD5-Digest lines for you inserting them into the MANIFEST.MF file you manually compose. It will optionally create the Main-Class
line.
It might be wise to review the /META-INF/MANIFEST.MF member generated by examining the JAR file with WinZip.
Unfortunately WinZip there are several problems in using WinZip to create jar files or to add files to them.
Manifest Gotchas
- There is no way to put command line arguments destined for java.exe inside the jar in the manifest. However, if you use JWS,
you can put them in the JNLP file which lives outside the jar. The reason for this is, by the time the java.exe JVM gets
around to looking inside jars, it has already cast in stone everything it learned from the command line.
- Main-Class gives the fully qualified name of the class you want executed if the jar is executed without specifying a class. You must specify it
exactly like this
Main-Class: com.mindprod.canadiantax.CanadianTaxCalculator
Note, there is no space before the colon and exactly one afterwards. There must be a line feed at the end of the line. There is no .class on the end.
- Be very careful with blank lines. There must be a blank line ahead of each Name: group, and no blank lines within that group. There is no error message if you make a mistake,
just some of your lines may be ignored. Name: statements don’t necessarily start a fresh group.
-
Manifest files have about strangest continuation convention ever invented. No line may be longer than 72 bytes (not characters), in its UTF8-encoded form. If a value would
make the initial line longer than this, it should be continued on extra lines (each starting with a single SPACE). Don’t split a name over two lines. e.g.
Main-Class: com.mindprod.MyProgram
Class-Path: activation.jar axis.jar
commons-discovery.jar
commons-logging.jar
jaxrpc.jar
log4j-1.2.8.jar
mail.jar saaj.jar junit.jar
I suggest putting long names on a line by themselves, with a single space on the start of the line, and using short names to stay away from the 72 limit. Be very careful with
spaces. Don’t add extra ones or remove any. jar.exe is highly senstive to having its spaces and blank lines just right. It does not give helpful
error messages.
Main-Class
Starting with JDK 1.2, a jar may made be executable by designating one class in it as the official class to start when the jar as a whole is executed, e.g. double clicked or
executed on the command line without a class mentioned:
java.exe -jar biorhythms.jar
The key is an attribute line in the meta-inf/manifest.inf manifest member of the form:
Main-Class: com.mindprod.bio.Biorhythms
Of course, the class selected must have a public static main method. You can use the jar -m option to add that text to your
manifest from a main.mft file containing text like this:
Main-Class: com.mindprod.Bio.Biorhythms
Watch out for extraneous lead/trailing spaces or extraneous trailing .class. You must have a trailing linefeed or the main.mft
file.
You can also specify the classpath, but you cannot specify command line parameters or system properties in the manifest.
Netscape 4.79’s signtool.exe won’t just sign an existing jar file. You have to take your existing jar apart into separate files, sign
it, and let it recreate the jar file. Beware: the signtool -m option uses an incompatible format for the main.mft files
that differs jar.exe’s. A signtool-style main.mft file needs a lead + sign on
its entries. e.g.
+ Main-Class: com.mindprod.Wassup.Wassup
A jar.exe-style main.mft does not.
Manifest Class-Path:
In JDK 1.3+ there is an analogous manifest entry to let you control the classpath. It is used to specify optional jars that will be downloaded only if needed. Normally you
would also place a Jar-index of these files in your main jar to help the loader decide which ones need to be loaded.
If you have multiple secondary jars, you must specify them in the manifest Class-Path entry of the master jar. It won’t do you any good to
specify them in the SET CLASSPATH environment parameter or on the java.exe -classpath
parameter.
Class-Path: myplace/myjar.jar . myplace/other.jar jardir/
Note how the elements are separated by space, not semicolon or colon as on the command line. The elements are relative URLs, but I have not done experiments or
found any documentation that describes what they are relative too. I presume the main jar. It could be the code base of the root jar file. It could be the CWD.
If you figure it out, please let me know.
According to Sun’s JWS FAQ, in JDK 1.5, Java Web Start still does not support Class-Path. There was a report that up until JDK 1.5, java.exe too
ignored this entry, however there is another report it has always worked fine. Class-Path is tricky to use, possibly leading to the erroneous report.
Note the list is space-separated, not semicolon-separated as in the SET CLASSPATH=C:\;. Note also that only relative directories and jar names are
permitted using / not \. They are relative to the containing jar. You cannot use C:\. Note
also that directory names need to terminate (but not begin) with a /. This is because Class-Path has to be platform-independent.
Note that jars always ignore the SET classpath and even the command line classpath. These other two classpaths are in no way
merged with the manifest Class-Path.
If you use -jar on the java.exe command line, java.exe will
quietly ignore the set environment classpath and any -classpath or -cp command line options. What are you to do if
you have additional jars to include?
- Copy them to the ext directory
- Mention them in the Class-Path: manifest entry. If you mention them in the manifest, you must specify the jars in relative URL form e.g. myplace/myjar.jar
and you must separate them by spaces, not the usual Windows semicolons or Linux colons. You may only use relative URLs, so drive letters are not permitted. The jar Class-Path
will also be used for Applets.
- In JDK version 1.4.2+ Mention these jars in a manifest extension list, and they will be downloaded if needed. e.g.
Extension-List: activation mail
activation-Extension-Name: javax.activation
activation-Implementation-URL: http://abc.com/activation.jar
mail-Extension-Name: javax.mail
mail-Implementation-URL: http://abc.com/mail.jar
- When Java goes searching for classes, it recursively searches the tree of jars mentioned in the various Class-Path entries in the already included
jars. So you could create a dummy jar whose sole purpose was to provide a list of other jars to search.
Writing To Jars
To write or update the Jar file, normally you use the jar.exe utility. Since jars are just zip files with extra members, you can also use ZIP
utilities such as PKZIP and WinZip.
You can also read and write the jar files from Java with the ZipEntry, ZipException, ZipFile, ZipInputStream
and ZipOutputStream classes. While a jar is in use, some OS’s may lock it, so don’t count on being able to update jars on the fly when
you are executing classes from them.
Gotchas
It is easy to forget to include everything you need to make a jar totally self contained. Sometimes jar will work on your machine because all the classes are otherwise
available. However, they won’t work on your client’s machine. This is such a hassle I have written an entire separate essay on dependencies,
how to make sure you include everything you need in your jar — your classes, all the classes they use, and all the classes they in turn use.
You have a similar problem with packing zip files of classes and source.
When you recompile, naturally this does not magically update all the jars in the world. Similarly when you build a jar, jar.exe will happily include
out of date class files that really should have been recompiled. Make sure everything, including classes from other packages have been freshly compiled before building your
jar. Here is my plan for preparing a distribution jar:
- Delete all class files in all vaguely related packages to ensure all are recompiled with official -targets and that all class files match the current
source.
- Recompile the class you are planning to distribute. That will force a recompile of classes it uses in other packages. By looking all over for class files you will see what the
dependencies are.
- Recompile and rejar the other packages to incorporate any of the distributed classes.
Sealing
A package within a jar file can be optionally sealed, which means that all classes defined in that package must be archived in the same jar file. You might want to seal a
package to prevent tampering, or to prevent accidental use of classes outside the original set.
To seal a package, you need to add a Name header for the package to your manifest, followed by a Sealed header, like this:
Name: com/mindprod/myPackage/
Sealed: true
Nesting
The member files in a zip file can be accessed individually, just like the files in a jar file (a species of zip file). However, when one zip is contained within another zip,
you can only access the contained zip file itself, not its individual members. You would need to expand it to disk somewhere before accessing its members.
There are three approaches to the problem:
- Put all members in the same jar/zip.
- Use several individual jar files, and arrange to have them on the path.
- Use a JWS installer class to unpack a nested jar into indvidual jars.
Learning More
Sun’s JDK Technote Guide on
Jars : available:
Sun’s JDK Tool Guide to
jar.exe : available:
Sun’s JDK Technote Guide on
Manifests : available:
Sun’s Javadoc on the
JarFile class : available:
Sun’s Javadoc on the
JarEntry class : available:
Sun’s Javadoc on the
JarInputStream class : available:
Sun’s Javadoc on the
JarOutputStream class : available:
Sun’s JDK Tool Guide to
pack200.exe : available:
Sun’s Javadoc on the
Pack200 class : available: