Java Archive (JAR) Files

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.

File Compression

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

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 containing path(s)

jar -tvf jarFile [ path ] [ ... ]

List the contents of jarFile, optionally showing just path(s)

jar -xvf jarFile [ path ] [ ... ]

Extract the contents of jarFile, optionally extracting 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.

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

JAR manifests

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).



[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.