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.
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
need. 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.
Class-Path: myplace/myjar.jar . myplace/other.jar jardir/
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.
- 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: