All Java developers know that JAR files are just ZIP files that contain the tree of Java classes. However, not everyone (including experienced developers) knows about the additional benefits you can get from this file format. In this article, I briefly review the JAR file format and describe the possibilities that you can enable by using it.
The ABCs of JAR files
A JAR file is based on the popular binary ZIP file format and is used for aggregating many files into one. It can also contain an optional directory called META-INF located in the root of file contents.
There are two ways that you can create JAR files: by the command-line tool jar or programmatically by using the java.util.jar API in the Java application. JAR files contain Java classes and/or resources that can be run, used, and loaded by class loaders—if the JAR files are included into the Java classpath in order to be visible for JVM.
In many cases, JAR files are not just simple archives of Java classes files and/or resources; they are used as building blocks for applications and extensions. The META-INF directory (if it exists) is used to store package and extension configuration data, including security, versioning, extension, and services.
The magic of the META-INF directory
The following files and directory in the META-INF directory are recognized and interpreted by the Java 2 Platform to configure applications, extensions, class loaders, and services:
- MANIFEST.MF: The manifest file that is used to define extension and package-related data.
- INDEX.LIST: This file is generated by the new "-i" option of the JAR tool, which contains location information for packages defined in an application or extension. It is part of the JarIndex implementation and used by class loaders to speed up the class loading process.
- x.SF: The signature file for the JAR file. 'x' stands for the base file name.
- x.DSA: The signature block file associated with the signature file with the same base file name. This file stores the digital signature of the corresponding signature file.
- services/: This directory stores all the service provider configuration files.
Let's go through each possible component.
Manifest file
The manifest file consists of "AttributeName: Value" pairs separated by a newline split into two sections: main and individual. The sections are divided by an additional newline symbol.
- Main: This section contains security and configuration information about the JAR file itself, as well as the application or extension that this JAR file is a part of. It also defines main attributes that apply to each individual manifest entry. No attribute in this section can have its name equal to "Name." This section is terminated by an empty line.
- Individual: This section defines various attributes for packages or files contained in this JAR file. Not all files in the JAR file need to be listed in the manifest as entries, but all files that are to be signed must be listed. The manifest file itself must not be listed. Each section must start with an attribute with the name as "Name," and the value must be a relative path to the file or an absolute URL referencing data outside the archive. (I discuss JAR signing later in the article.)
The following are the most important attributes of the manifest file:
- Manifest-Version: Defines the manifest file version. The value is a legitimate version number as described in the above spec.
- Created-By: Defines the version and the vendor of the Java implementation on top of which this manifest file is generated. This attribute is generated by the JAR tool.
- Signature-Version: Defines the signature version of the JAR file. The value should be a valid version number string.
- Class-Path: The value of this attribute specifies the relative URLs of the extensions or libraries that this application or extension needs. URLs are separated by one or more spaces. The application or extension class loader uses the value of this attribute to construct its internal search path.
- Main-Class: This is used for stand-alone applications. The value of this attribute defines the relative path of the main application class, which the launcher will load at startup time. The value must not have the .class extension appended to the class name. If you have specified this attribute, the JAR file becomes executable, and an application will be automatically launched by the command java -jar x.jar.
- Sealed: This attribute defines whether this JAR file is sealed. The value can be either true or false and case is ignored. When the JAR file is sealed, an optional package can enforce consistency within a particular version. A package sealed within a JAR specifies that all classes defined in that package must originate from the same JAR; otherwise, a SecurityException is thrown. For example, this code:
Name: javax/servlet/internal/
Sealed: true
specifies that the javax.servlet.internal package is sealed and that all classes in that package must be loaded from the same JAR file. To find out about optional packages, look at the extension mechanism.
Signed JAR file
Any JAR file can be signed by using the command line jarsigner tool or directly through the java.security API. By signing the file, you can ensure that nobody has changed the contents of a JAR and that you are using the JAR file from a well-known producer. Every file entry, including non-signature related files in the META-INF directory, will be signed if the JAR file is signed by the jarsigner tool. The signature-related files are:
- META-INF/MANIFEST.MF
- META-INF/*.SF
- META-INF/*.DSA
- META-INF/*.RSA
- META-INF/SIG-*
Each signer is represented by a signature file with extension .SF. The major part of the file is similar to the manifest file. It consists of a main section, which includes information supplied by the signer but not specific to any particular JAR file entry. The main section entry, x-Digest-Manifest-Main-Attributes (where x is a digest algorithm), contains the digest value for the main attributes of the manifest.
To validate a file, a digest value in the signature file is compared against a digest calculated against the corresponding entry in the manifest file. Then, a digest value in the manifest file is compared against a digest calculated against the actual data referenced in the "Name:" attribute, which specifies either a relative file path or URL.
For example, this is a manifest file for a signed JAR:
Manifest-Version: 1.0
Created-By: 1.3 (Sun Microsystems, Inc)
Name: common/class1.class
MD5-Digest: (base64 representation of MD5 digest)
Name: common/class2.class
MD5-Digest: (base64 representation of MD5 digest)
SHA-Digest: (base64 representation of SHA digest)
This is the corresponding signature:
Signature-Version: 1.0
MD5-Digest-Manifest-Main-Attributes: (base64 representation of MD5 digest)
Name: common/class1.class
MD5-Digest: (base64 representation of MD5 digest)
Name: common/class2.class
MD5-Digest: (base64 representation of MD5 digest)
A digital signature is a signed version of the .SF signature file. These are binary files that are not intended to be interpreted by humans. Digital signature files have the same filenames as the .SF files but different extensions. The extension varies depending on the type of digital signature and is generally either RSA or DSA.
JAR Index
JAR Index (often referred to as the JarIndex mechanism) was introduced to optimize the class searching process of class loaders for network applications, especially applets. The JarIndex mechanism collects the contents of all the JAR files defined in an applet and stores the information in an index file in the first JAR file on the applet's class path. After the first JAR file is downloaded, the applet class loader will use the collected content information for efficient downloading of JAR files.
The existing JAR tool is enhanced to be able to examine a list of JAR files and generate directory information as to which classes and resources reside in which JAR file. This directory information is stored in a simple text file named INDEX.LIST in the META-INF directory of the root JAR file. When the classloader loads the root JAR file, it reads the INDEX.LIST file and uses it to construct a hash table of mappings from file and package names to lists of JAR file names. In order to find a class or a resource, the class loader queries the hashtable to find the proper JAR file and then downloads it if necessary.
Service providing
Files in the META-INF/services directory are service provider configuration files. A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service. The classes in a provider typically implement the interfaces and inherit the classes defined in the service itself.
A service provider identifies itself by placing a provider-configuration file in the resource directory META-INF/services. The file's name should consist of the fully-qualified name of the abstract service class.
Using the features
Now that you see what you can do with the features of the JAR files, it's up to you to put them to practical and creative uses in your development work. If you have used JAR files in some interesting ways, please share your experiences in the article discussion.