Полезная информация

Exploring Java

Previous: 3.4 The Applet TagChapter 3
Tools of the Trade
Next: 3.6 Code and Data Signing
 

3.5 Packaging: Java Archive (JAR) 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, 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.

3.5.1 Compression

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.

3.5.2 The jar Utility

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.

Table 3.1: Common jar Commands
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 filename

You 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.html

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

JAR manifests

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


Previous: 3.4 The Applet TagExploring JavaNext: 3.6 Code and Data Signing
3.4 The Applet TagBook Index3.6 Code and Data Signing

Other Books in this LibraryJava in a NutshellJava Language ReferenceJava AWTJava Fundamental ClassesExploring Java