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 Java version 1.1 or later 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 class : 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 Java version 1.1 or later they usually are. Jars don’t support all the PKZIP
compression formats. In Java version 1.5 or later 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 Java version 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!).
- Java version 1.2 or later 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:
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+ 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
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 Java version 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 Java version 1.4 or later ,
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 Java version 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/W7-32/W7-64, 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.
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.
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 Java version 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
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 Java version 1.3 or later 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 might be absolute or relative URLs, but I have not done experiments or found any documentation that
describes what they are relative to. 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 Java version 1.5, Java Web Start still does not
support Class-Path. There was a report that up until Java version 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
JarFile class : available:
Sun’s Javadoc on
JarEntry class : available:
Sun’s Javadoc on
JarInputStream class : available:
Sun’s Javadoc on
JarOutputStream class : available:
Sun’s JDK Tool Guide to
pack200.exe : available:
Sun’s Javadoc on
Pack200 class : available: