Java archive files (JAR files) are Java’s suitcases. They are the standard and portable way to pack up all of the parts of your Java application into a compact bundle for distribution or installation. You can put whatever you want into a JAR file: Java class files, serialized objects, data files, images, sounds, etc. As we’ll see in Chapter 20, a JAR file can carry one or more digital signatures that attest to the integrity and authenticity of that data. A signature can be attached to the file as a whole or to individual items in the file.
The Java runtime system understands JAR files and can load class
files directly from an archive. So you can pack your
application’s classes in a JAR file and place it in your
CLASSPATH
. You can do the equivalent for
applets by
listing the JAR file in the
ARCHIVE
attribute of
the
HTML
<APPLET>
tag. Other types of files (data,
images, etc.) contained in your JAR file can
be retrieved using the
getResource( )
method. (described in Chapter 10). Therefore, your code doesn’t have to
know whether any resource is a plain file or a member of a JAR
archive. Whether a given class or data file is an item in a JAR file,
is an individual file on the class path, or is located on a remote
applet server, you can always refer to it in a standard way, and let
Java’s class loader resolve the location.
Items stored in JAR
files may be compressed with ZLIB[10] compression. JAR files are completely
compatible with the ZIP archives familiar to Windows users. You could
even use tools like pkzip
to create and maintain
simple JAR files. But jar
, the Java archive
utility, can do a bit more.
Compression makes downloading classes over a network much faster. A quick survey of the SDK distribution shows that a typical class file shrinks by about 40 percent when it is compressed. Text files such as arbitrary HTML or ASCII containing English words often compress by as much as 75 percent—to one-quarter of their original size. (On the other hand, image files don’t get smaller when compressed; both of the common image formats have compression built in.)
Compression is not the only advantage that a JAR file has for transporting files over a network. For an application with many components, the amount of time it takes to transport all of the parts may be less significant than the time involved in setting up the connections and making the requests for them. This is especially important for applets loaded via the Web. The typical web browser has to make a separate HTTP request for each class or data file. An applet comprising 100 classes, for example, would require at least 100 separate trips to the web server to gather all its parts. Placing all the classes in a single JAR file enables them to be downloaded in a single transaction. Eliminating the overhead of making HTTP requests is likely to be a big savings, since individual class files tend to be small, and a complex applet could easily require many of them.
The
jar
utility provided with the
SDK is a
simple tool for creating and reading JAR files. Its user interface
isn’t friendly; it mimics the Unix tar
(tape
archive) command. If you’re familiar with
tar
, you’ll recognize the following
incantations:
-
jar -cvf
jarFile
path
[
path
] [ .. . ]
Create
jarFile
containingpath
(s)-
jar -tvf
jarFile
[
path
] [ ... ]
List the contents of
jarFile
, optionally showing justpath
(s)-
jar -xvf
jarFile
[
path
] [ ... ]
Extract the contents of
jarFile
, optionally extracting justpath
(s)
In these
commands, the letters c
, t
, and
x
tell jar
whether it is
creating an archive, listing an archive’s contents, or
extracting files from an archive. The f
means that the
next argument will be the name of the JAR file on which to
operate. The
v
tells jar
to be more
verbose when displaying information
about files. In verbose mode you can get information about file
sizes, modification times, and compression ratios.
Subsequent items on the command line (i.e., anything aside from the
letters telling jar
what to do and the file on
which jar
should operate) are taken as names of
archive items. If you’re creating an archive, the files and
directories you list are placed in it. If you’re extracting,
only the filenames you list are extracted from the archive. (If you
don’t list any files, jar
extracts
everything in the archive.)
For example, let’s say we have just completed our new game: “spaceblaster.” All the files associated with the game are in three directories. The Java classes themselves are in the spaceblaster/game directory; spaceblaster/images contains the game’s images; and spaceblaster/docs contains associated game data. We can pack all of this in an archive with this command:
% jar cvf spaceblaster.jar spaceblaster
Because we requested verbose output, jar
tells us
what it is doing:
adding:spaceblaster/ (in=0) (out=0) (stored 0%) adding:spaceblaster/game/ (in=0) (out=0) (stored 0%) adding:spaceblaster/game/Game.class (in=8035) (out=3936) (deflated 51%) adding:spaceblaster/game/Planetoid.class (in=6254) (out=3288) (deflated 47%) adding:spaceblaster/game/SpaceShip.class (in=2295) (out=1280) (deflated 44%) adding:spaceblaster/images/ (in=0) (out=0) (stored 0%) adding:spaceblaster/images/spaceship.gif (in=6174) (out=5936) (deflated 3%) adding:spaceblaster/images/planetoid.gif (in=23444) (out=23454) (deflated 0%) adding:spaceblaster/docs/ (in=0) (out=0) (stored 0%) adding:spaceblaster/docs/help1.html (in=3592) (out=1545) (deflated 56%) adding:spaceblaster/docs/help2.html (in=3148) (out=1535) (deflated 51%)
jar
creates the file
spaceblaster.jar and adds the directory
spaceblaster, in turn adding the directories and
files within spaceblaster to the archive. In
verbose mode, jar
reports the savings gained by
compressing the files in the archive.
We can unpack the archive with this command:
% jar xvf spaceblaster.jar
Likewise, we can extract an individual file or directory with:
%jar xvf spaceblaster.jar
filename
But you normally don’t have to unpack a JAR file to use its contents; Java tools know how to extract files from archives automatically. We can list the contents of our JAR with the command:
% jar tvf spaceblaster.jar
Here’s the output; it lists all the files, their sizes, and creation times:
0 Thu May 15 12:18:54 PDT 1997 META-INF/ 1074 Thu May 15 12:18:54 PDT 1997 META-INF/MANIFEST.MF 0 Thu May 15 12:09:24 PDT 1997 spaceblaster/ 0 Thu May 15 11:59:32 PDT 1997 spaceblaster/game/ 8035 Thu May 15 12:14:08 PDT 1997 spaceblaster/game/Game.class 6254 Thu May 15 12:15:18 PDT 1997 spaceblaster/game/Planetoid.class 2295 Thu May 15 12:15:26 PDT 1997 spaceblaster/game/SpaceShip.class 0 Thu May 15 12:17:00 PDT 1997 spaceblaster/images/ 6174 Thu May 15 12:16:54 PDT 1997 spaceblaster/images/spaceship.gif 23444 Thu May 15 12:16:58 PDT 1997 spaceblaster/images/planetoid.gif 0 Thu May 15 12:10:02 PDT 1997 spaceblaster/docs/ 3592 Thu May 15 12:10:16 PDT 1997 spaceblaster/docs/help1.html 3148 Thu May 15 12:10:02 PDT 1997 spaceblaster/docs/help2.html
Note
that
jar
adds a directory called
META-INF to our archive. It contains
one file:
MANIFEST.MF. The
META-INF
directory holds files describing the
contents of the JAR file. The MANIFEST.MF file
that jar
adds is an automatically generated
packing list naming the files in the archive
along with cryptographic
checksums for each.
The manifest is a text file containing a set of lines in the form keyword: value. The format of the manifest file changed between SDK 1.1 and SDK 1.2. In SDK 1.2 and later, the manifest file is very simple, containing no information on the items in the archive:
Manifest-Version: 1.0 Created-By: 1.2.1 (Sun Microsystems Inc.)
Basically the file just describes its version number. In SDK 1.1, the manifest contains entries describing each item in the archive. In our case, the beginning of our manifest file looks like this (in SDK 1.1 only):
Manifest-Version: 1.0 Name: spaceblaster/game/Game.class Digest-Algorithms: SHA MD5 SHA-Digest: D5Vi4UV+O+XprdFYaUt0bCv2GDo= MD5-Digest: 9/W62mC4th6G/x8tTnP2Ng== Name: spaceblaster/game/Planetoid.class Digest-Algorithms: SHA MD5 SHA-Digest: SuSUd6pYAASO5JiIGlBrWYzLGVk= MD5-Digest: KN/4cLDxAxDk/INKHi2emA== ...
The first line is the same version number as before. Following it are
groups of lines describing each item. The first line tells you the
item’s name; in this case, the lines describing the files
Game.class and
Planetoid.class. The remaining lines in each
section describe various attributes of the item. In this
case, the
Digest-Algorithms
line specifies that the manifest
provides
message digests (similar to checksums)
in two forms: SHA and MD5.[11] This is followed by
the actual message digest for the item, computed using these two
algorithms.
As we’ll discuss in the next section, the META-INF directory and manifest file can also hold digital signature information for items in the archive. Since the message digest information is really necessary only for signed JAR files, it is omitted when you create an archive in SDK 1.2 and later.
You can add your own information to the manifest descriptions by specifying a supplementary manifest file when you create the archive. This is a good place to store other simple kinds of attribute information about the files in the archive, perhaps version or authorship information.
For example, we can create a file with the following keyword: value lines:
Name: spaceblaster/images/planetoid.gif RevisionNumber: 42.7 Artist-Temperment: moody
To add this information to the manifest in our archive, place it in a
file called myManifest.mf and give the following
jar
command:
% jar -cvmf myManifest.mf spaceblaster.jar spaceblaster
We’ve
added an additional option to the command, m
,
which specifies that jar
should read additional
manifest information from the file given on the command line. How
does jar
know which file is which? Because
m
is before f
, it expects to
find the manifest information before the name of the JAR file it will
create. If you think that’s awkward, you’re right; get
the names in the wrong order, and jar
will do the
wrong thing. Be careful.
Aside from information for your own use, there are special values (in
SDK 1.2) you can put in the manifest file that are useful. One of
these, Main-Class
, allows you to specify a class
that contains a main( )
method:
Main-Class: Game
If you incorporate this specification in your JAR file manifest
(using the m
option described earlier), you can
actually run the JAR from the command line:
% java -jar spaceblaster.jar
The interpreter looks for the Main-Class
value in
the manifest. Then it loads the named class as the
application’s initial class.
What can we do with the revision and temperament information
we’ve so cleverly included in the JAR file? Unfortunately,
nothing, except for unpacking the archive and reading the manifest.
However, if you were writing your own JAR utility or some kind of
resource loader, you could include code to look at the manifest,
check for your private keywords, and act accordingly—perhaps
darkening the display if the artist’s temperament is
moody
.
Another important keyword is
Java-Bean
. The value of this keyword should
be true
if the item is a Java Bean; this
information is used by the BeanBox and
other utilities that work
with
Beans (see Chapter 19).
[10] See http://www.simtel.net/pub/pd/2530.shtml and RFC 1950.
[11] SHA and MD5 stand for Secure Hashing Algorithm and Message Digest 5. That’s all you really need to know about them; an explanation of these algorithms is beyond the scope of this book.
Get Learning Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.