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, 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 run-time environment understands JAR files and can load class files
directly from an archive. So you can pack your application's classes
in a JAR and place it in your CLASSPATH.
You can do the equivalent for applets by listing the JAR file in the
ARCHIVES
attribute of the HTML <APPLET> tag.
Other types of files (data, images, etc.) contained in your JAR file can be
retrieved using the getResource() methods.
(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 resident in a JAR
file, located separately in the local class path, or accessible at the server
of a remotely loaded applet, 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[4] 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.
[4] See http://www.cdrom.com/pub/infozip/zlib/ and RFC 1950.
Compression makes downloading classes over a network much faster. A quick survey of the JDK 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 slow 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. As it is widely implemented today, your Web browser has to make a separate HTTP request for each class or data file. An applet comprised of 100 classes, for example, would require at least 100 separate trips to the Web server to gather all its parts. By placing all of these classes in a JAR file, they can be downloaded together, 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 JDK is a simple tool for creating and reading JAR files. Its user interface isn't friendly; it mimics the UNIX tar (tape archive) command.[5] If you're familiar with tar, you'll recognize the incantations in Table 3.1
[5] Although I'm discussing the rather ugly jar utility, you shouldn't find it surprising that people are already creating alternatives. One nice free tool that's available is MoaJar; it is available from http://www.opengroup.org/RI/java/moa/moajar/index.html. Check it out.
| jar -cvf jarFile path [ path ] [ ... ] | Create jarFile containing path(s) |
| jar -tvf jarFile [ path ] [ ... ] | List the contents of jarFile, optionally show just path(s) |
| jar -xvf jarFile [ path ] [ ... ] | Extract the contents of jarFile, optionally extract just path(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. If you don't want
this information, you can omit the v.
Anything left over on the command line (i.e., anything aside from the letters telling jar what to do and the file on which jar should operate) is taken as a filename. If you're creating an archive, the files and directories you list are placed in it. If you're extracting, the filenames you give 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.
Now that we have an archive, we can unpack it with the command:
% jar xvf spaceblaster.jar
Likewise, we can extract an individual file or directory with:
% jar xvf spaceblaster.jar filenameYou normally don't have to unpack a JAR file to use its contents; Java knows 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:
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.htmlNote that something odd has appeared. jar has added 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. The MANIFEST.MF file that jar has added 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 section of keyword: value lines describing each item in the archive. The beginning of our manifest file looks like this:
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 simply a version number. Following it are
groups of lines describing each item. The first line tells you the
item's name; in this case, I'm showing you 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.[6]
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.
We'll talk more about this in the next section.
[6] 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 way beyond the scope of this book.
You can add your own information to the manifest descriptions by specifing a supplimentary 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 info.
For example, we can create a file with the following name:value pairs:
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. That's another good
reason to look into the MoaJar program I mentioned earlier.
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." At present, only one
keyword is defined, aside from the ones we've seen already: 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 Java Beans (see Chapter 18).