InformIT

Exploring the Java Archive (JAR) Format

Date: Apr 9, 2004

Return to the article

JAR files are ubiquitous to Java users and programmers. But just how much do you know about them? Learn the ins-and-outs of Java archive files, including their innate support for indexing, programmatic creation and manipulation, security, and data integrity.
The Java language has inherent support for working with JAR files programmatically - Title Page

You've probably worked with plenty of JAR files. But how much do you know about them?

In this article, we will perform a study of JAR files. Along the way, I'll point out features that have been added to the JAR format throughout Java's evolution to address the needs of the programming language.

Some Background

The Java Archive (JAR) format consolidates multiple files into a single archive file, which contain Java classes and other auxiliary files (such as properties files). Under the hood, JAR files use the ZIP file format. Accordingly, when you use a JAR file, you inherit offerings of the ZIP format, including lossless data compression and archiving.

Archiving class files and compressing them allows you to distribute applications quickly in a network environment. Not only are the files smaller, but multiple connections do not have to be established for each constituent file; one HTTP transaction can be used, as opposed to one for each file in the archive.

No study of the world of the JAR is complete without examining the tool to make them: the Java Archive Tool. The Java Archive Tool, which ships with the Java Development Kit, can be invoked using the jar command.

As you will see, usage of the JAR tool is not rocket science. We'll cover the basics of using the JAR tool before delving a bit deeper into some more extended offerings of the tool later in this article.

Creating JAR Files

To create a JAR file, you use the command format:

jar cf jar-filename.jar file1.txt subdir2/file2.txt subdir3

Let's scrutinize the syntax above. The c option tells the tool we are creating a JAR. The f option informs the tool that we want the output of our creation to be a file (as opposed to the output being sent to standard out). The jar-filename.jar holds the place of the JAR filename we want to create. (By convention, the .jar extension is used. However, it is not required.)

Next, we have a list of the files that we want added to the archive. As is implied in the example, you can include multiple files by specifying their names, separated by spaces. And you can use wildcards (*) in the file specification. Let's suppose that one of the arguments is a directory (subdir2, in our example); the contents of that directory are added to the archive recursively, preserving the path.

When you use the JAR tool for creation, you can use the v option to have the tool report (a verbose) progress of the files being added to the archive.

As stated earlier, JAR files allow for compression. This feature can be turned off by using the 0 option, indicating zero compression is desired. Whether to use compression or not is a thought for you to ponder. In a networked environment, using compression allows java archives to be delivered quicker. However, this comes at a price. The client must decompress the JAR file, before the client can use it; this decompression process can take a while in itself, depending on various factors.

Viewing Contents of Your JAR File

You can use the JAR tool to look inside a JAR file and view its contents. Doing this is simple. One example might be:

jar tf jar-filename.jar

In the syntax above, the t option states that we want to view the table of contents of jar-filename.jar. The f option indicates that we will provide the name of the archive we want to look into via the command line. (If we did not use this option, the tool would expect us to provide an argument via standard in.)

As we did during the creation of our JAR, we can also use the v option here to get a verbose output of its contents.

Below is the output of a typical view content operation:

jar tfv jar-filename.jar

   0 Fri Mar 12 18:08:20 EST 2004 META-INF/
  71 Fri Mar 12 18:08:20 EST 2004 META-INF/MANIFEST.MF
 1436 Fri Mar 12 18:07:56 EST 2004 jar-file.name.jar
  14 Fri Mar 12 17:45:54 EST 2004 file2.txt
  14 Fri Mar 12 17:47:32 EST 2004 file5.txt
   0 Fri Mar 12 17:46:26 EST 2004 subdir3/
  14 Fri Mar 12 17:46:16 EST 2004 subdir3/file3.txt
  14 Fri Mar 12 17:46:26 EST 2004 subdir3/file4.txt

Let's pay attention to a couple of things. The paths stored in our JAR file are relative, and pathnames are shown using forward slashes (UNIX style) even though I was using Microsoft Windows 2000. In JAR files, regardless of the operating system you are on, all pathnames are displayed with forward slashes. Also note the presence of a file named MANIFEST.MF in a directory called META-INF. We will talk about the significance of this file in just a bit.

Extracting Contents of a JAR File

As you probably guessed, extraction of the contents of a JAR file is not that hard either. The syntax below shows the basic command structure you would use:

jar xf jar-filename.jar [optional, space-delimited names of specific archived files]

In the syntax above, we use the JAR tool with the x option, indicating we want to perform an extraction. The f option indicates that we want our extraction to occur from the filename we specify (i.e., jar-filename.jar).

We can append the command with filenames of specific files that are housed in the archive which we want extracted. This is particularly useful if we are only interested in grabbing a subset of the files housed in our archive. By not specifying any specific file names, the tool goes ahead and extracts all the files in the archive.

When the extraction process occurs, the JAR tool automatically creates any directories that it sees were stored in the archive. It is also very important to take note: any existing files with the same name will be overwritten if there is a duplicate entry in the target extraction destination. The user is not prompted that an overwrite is occurring, so users should be prudent when using the tool.

Updating Your JAR Files

Once you create a JAR file, you can add to it using the JAR tool. This is done using the u option which is showcased with the syntax below.

jar uf jar-filename.jar [space delimited list of files to be added]

For example, we might have:

jar uf jar-filename.jar file1.txt subdir3/file2.txt 

In our syntax, we use the u option to indicate that we are performing an update of our existing JAR, jar-filename.jar. The f option, as before, signifies that we shall indicate our filename via the command line (i.e., jar-filename.jar)

We also specify a list of the files that we want added to our JAR file. Any files that already exist in our archive will be overwritten if there is a conflict of a same pathname.

The Manifest

So what was that MANIFEST.MF file we saw earlier? The JAR embeds a manifest, containing information about the files which constitute the JAR file. There is only one manifest file per JAR file and it is always housed in the META-INF directory. This information might include meta information about electronic signing, version control, package sealing, and a variety of other information.

Below, you can see the contents of a sample manifest file.

Manifest-Version: 1.0
Created-By: 1.4.2_03 (Sun Microsystems Inc.) 

As you can see, manifest entries take the form of name-value pairs, using the form: "headername: value".

The manifest file does not really play a role for basic JAR functionality. It's a lot more important when we start to deal with more advanced JAR functionality, such as signing and authenticating JAR files. Now that you have a base foundation of knowledge of the JAR format, let's address some of that advanced functionality now.

Executing JARs: Specifying the Application Entry Point using the JAR File Manifest

You can execute a Java class directly from a JAR file. This is done using the syntax:

java -jar ExecutableJarFileName.jar

Before we do this, we need to do some work. Create a file named manifest that has the following line in it:

Main-Class: com.yourpackagename.YourEntryClassName

In short, you are defining the Class of your JAR and specifying its fully qualified pathname.

Next create the executable JAR file using the JAR tool using the syntax:

jar cmf manifest ExecutableJarFileName.jar 

In the syntax above, we used the c (create), f (file will be specified on the command line) and m (update manifest) options. Using these options, the manifest is updated with the manifest header we created from the file named manifest. After doing this, the JAR file can be executed using the java –jar command prefix which was shown earlier.

An important point to take note about executing from a JAR: the auxiliary JARs used by the executed JAR must be referenced through the Class-Path header of the manifest file. In a –jar type execution, the environment variable CLASSPATH and command line class paths are ignored by the JVM. You can learn more about this in the Extensions section of this article.

Sealing Packages in JARs

In a JAR archive, you can seal a package within the JAR. Doing this informs the JVM that classes defined in the package must be found in the same JAR file. The advantage is that a basic security measure is introduced. A SecurityException is thrown by the JVM if you try to load a class in a sealed package from a source other than the housing JAR of the sealed package.

To seal a package, a Name header is used, followed by the package's relative name. The Name header is followed by the Sealed header with a value of true, to state that the package specified should be sealed. The following is an example of the appropriate headers needed to seal a package:

Name: com/yourcompanyname/thePackageName/
Sealed: true

As before, you would have these headers in a text file and then use the JAR tool to take the header and incorporate into manifest file and finally into the JAR.

Extensions

The extension mechanism of the JAR format allows you to specify auxiliary JAR files that a given JAR file requires for operation. Instead of having to manipulate the system CLASSPATH and add these auxiliary JARS to it, you can leverage Class-Path headers in the manifest file.

This might best be described by example. Let's say that one.jar depends on two.jar. Given that these two JAR files are in the same directory, we could use the following header in one.jar's manifest file:

Class-Path: two.jar

Using this header, we are stating that the classes of two.jar are extension classes of the classes of one.jar. As you can see, this facility allows us to use the manifest file instead of having to manipulate system CLASSPATH variables, thereby making our code more portable. The JVM, in essence, will add JARs in the Class-Path header to the class path for us.

Signing JARs

The JDK now ships with a tool called jarsigner, which allows you to digitally sign JARs. When a JAR is digitally signed, the manifest file is updated with security information, and a signature and block file are added to the META-INF directory.

JAR files use a public/private key security scheme tied together with a certificate authority to verify authenticity of a public key.

NOTE

It is beyond the scope of this article to delve into the details of these security concepts. You can learn more about these topics by visiting the links in the references section of this article.

The signing of a JAR file requires a private key. This private key, as well as the associated public key and certificate, is stored in a password protected database called a keystore. These so-called keystores can hold the keys of many signers. The keys in the key store is associated with an alias. This alias is typically the name of the key holder. For example, Bob Jones's key might be named BobJones.

So where do we find this keystore? Well, they have to be created. You can do this by creating a keystore entry. If a keystore does not exist, it will be created. We can do just that with the keytool command:

keytool –genkey –alias BobJones –keypass BobJonesPassword –validity 80 –keystore ourKeyStore –storepass ourKeyStorePasword

in the syntax above, we specified that we want a keystore named ourKeyStore created with a password of ourKeyStorePassword. Inside this keystore, we want a key generated for an alias of BobJones, having a password of BobJonesPassword.

After issuing the command, you will be asked to answer various questions as shown in the example interaction with the keytool utility below:

C:\jarsstudy>keytool -genkey -alias BobJones -keypass BobJonesPassword -validity
 80 -keystore ourKeyStore -storepass ourKeyStorePassword
What is your first and last name?
 [Unknown]: Bob Jones
What is the name of your organizational unit?
 [Unknown]: IBM Software Services for WebSphere
What is the name of your organization?
 [Unknown]: IBM
What is the name of your City or Locality?
 [Unknown]: Austin
What is the name of your State or Province?
 [Unknown]: TX
What is the two-letter country code for this unit?
 [Unknown]: US
Is CN=Bob Jones, OU=IBM Software Services for WebSphere, O=IBM, L=Austin, ST=TX,
 C=US correct?
 [no]: yes

NOTE

In our interaction, we use a self-signed certificate. In a production environment, you would want to use a certificate authority, such as VeriSign. You can learn more about how to do this by looking at the references section.

Now that we have a keystore that contains the needed keys, we can go about signing our JAR. As mentioned earlier, we'll use the jarsigner tool to do this. Take a look at the example syntax below:

jarsigner –keystore ourKeyStore –storepass ourKeyStorePassword 
–keypass BobJonesPassword –signedjar
 SignedJARTargetName.jar SignedJARSourceName.jar BobJones

I intentionally used color in the example to better explain what is going on. Blue is used for values that we specified earlier when we used keytool to generate our keys. As you can gather from the syntax above, we provide the jarsigner tool with some information that we established when we generated our keys:

We also use the –signed option followed by the name of signed JAR we want to create (SignedJARTargetName.jar) trailed by the name of the JAR we want to sign (SignedJARSourceName.jar).

The consumer of a signed JAR file can verify if the JAR has been signed. This is done using the jarsigner tool with the syntax:

jarsigner –verify SignedJARTargetName.jar

If the JAR being scrutinized is verified, you will see receive a response from the JAR signer tool of:

jar verified

On the other hand, if the JAR file you are dealing with is not verified, you will receive a message:

jar is unverified (signatures missing or not parsable)

or if verification fails, an appropriate message will be relayed to the console.

JAR Indexing

When an application is spread out across multiple JAR files, the runtime process of finding the necessary class in the various JARs can be a lengthy process. The problem is especially exasperated when you are dealing with a larger project consisting of a huge number of JARs which in turn house a large number of classes. The class loader users a linear search algorithm to search through all of the JARs to find a requested resource. You can imagine that when dealing with large network applications, things could get ugly.

As a result, JDK 1.3 added indexing to the JAR file format. Indexing allows the search for classes through project JARs to be optimized.

When a JAR file is indexed, directory information is stored in a file named INDEX.LIST. This file is stored in the META-INF directory of the root JAR file. The sample command below shows sample usage of the JAR indexing feature:

jar –i first.jar subdir/second.jar third.jar

Programmatic Support of JAR Files in Java

The Java language has inherent support for working with (reading and writing) JAR files programmatically. This offering comes in the java.util.jar package. The package was introduced in JDK 1.2. The JarFile class is one of the major classes of the package. See http://java.sun.com/j2se/1.4.2/docs/api/java/util/jar/package-summary.html for the an API breakdown of usage of the package.

In the Oven

An interesting Java Specification Request (JSR) currently being developed is JSR 200: Network Transfer Format for Java Archives. The JSR defines a dense download format for Java classfiles. The new format is expected to achieve major size savings over JAR files.

Conclusion

In this article, you were introduced to the subject of Java Archives (JARs). JAR files are ubiquitous in the Java realm. As Java has evolved, so have the offerings of JARs. They are no longer provide just traditional archiving as we see in the ZIP and TAR format. As you were shown, JARs now have innate support for indexing, programmatic creation/manipulation, security, and integrity.

Resources

How to Sign Java Code: http://www.securingjava.com/appdx-c/appdx-c-6.html

Security in Java 2 SDK 1.2: http://java.sun.com/docs/books/tutorial/security1.2/overview/index.html

800 East 96th Street, Indianapolis, Indiana 46240