THIS chapter and the two following chapters discuss deploying your Java applications and applets.
Java versions 1.4.2, 1.5.0, and 1.6.0 bundle two deployment technologies:
Java Web Start, for deploying Java applications
Java Plug-in, for deploying Java applets
We recommend using Java Plug-in to deploy applets when you want your application to run within a browser, or when the application is tightly integrated with Web page content.
We recommend using Java Web Start when you want your application to run stand-alone, on your users’ desktops.
Java Web Start requires that applications be packed in JAR files, which is covered in this chapter.
The Java Archive (JAR) file format enables you to bundle multiple files into a single archive file. Typically a JAR file contains the class files and auxiliary resources associated with applets and applications.
The JAR file format provides many benefits:
Security. You can digitally sign the contents of a JAR file. Users who recognize your signature can then optionally grant your software security privileges it wouldn’t otherwise have.
Decreased download time. If your applet is bundled in a JAR file, the applet’s class files and associated resources can be downloaded to a browser in a single HTTP transaction without the need for opening a new connection for each file.
Compression. The JAR format allows you to compress your files for efficient storage.
Packaging for extensions. The extensions framework provides a means by which you can add functionality to the Java core platform, and the JAR file format defines the packaging for extensions. Java 3D[1] and JavaMail[2] are examples of extensions developed by Sun. By using the JAR file format, you can turn your software into extensions as well.
Package sealing. Packages stored in JAR files can be optionally sealed so that the package can enforce version consistency. Sealing a package within a JAR file means that all classes defined in that package must be found in the same JAR file.
Package versioning. A JAR file can hold data about the files it contains, such as vendor and version information.
Portability. The mechanism for handling JAR files is a standard part of the Java platform’s core API.
The documentation for the Java Development Kit includes information about the Jar tool:
JAR files are packaged with the ZIP file format, so you can use them for tasks such as lossless data compression, archiving, decompression, and archive unpacking. These tasks are among the most common uses of JAR files, and you can realize many JAR file benefits using only these basic features.
Even if you want to take advantage of advanced functionality provided by the JAR file format, such as electronic signing, you will first need to become familiar with the fundamental operations.
To perform basic tasks with JAR files, you use the Java Archive Tool provided as part of the Java Development Kit. Because the Java Archive Tool is invoked by using the jar
command, this tutorial refers to it as “the Jar tool.”
As a synopsis and preview of some of the topics to be covered in this section, Table 16.1 summarizes common JAR file operations.
Table 16.1. Common JAR File Operations
Operation | Command |
---|---|
To create a JAR file |
|
To view the contents of a JAR file |
|
To extract the contents of a JAR file |
|
To extract specific files from a JAR file |
|
To run an application packaged as a JAR file (requires the |
|
To invoke an applet packaged as a JAR file |
<applet code=AppletClassName.class archive="JarFileName.jar" width=width height= height> </applet> |
[*] |
This section shows you how to perform the most common JAR-file operations, with examples for each of the basic features.
The documentation for the Java Development Kit includes reference pages for the Jar tool:
The basic format of the command for creating a JAR file is:
jar cf jar-file input-file(s)
The options and arguments used in this command are:
The c
option indicates that you want to create a JAR file.
The f
option indicates that you want the output to go to a file rather than to stdout.
jar-file
is the name that you want the resulting JAR file to have. You can use any filename for a JAR file. By convention, JAR filenames are given a .jar
extension, though this is not required.
The input-file(s)
argument is a space-separated list of one or more files that you want to include in your JAR file. The input-file(s)
argument can contain the wildcard *
symbol. If any of the “input-files” are directories, the contents of those directories are added to the JAR archive recursively.
The c
and f
options can appear in either order, but there must not be any space between them.
This command will generate a compressed JAR file and place it in the current directory. The command will also generate a default manifest file for the JAR archive (see the Understanding the Default Manifest section, page 500).
The metadata in the JAR file, such as the entry names, comments, and contents of the manifest, must be encoded in UTF8.
You can add any of the additional options to the cf
options of the basic command shown in Table 16.2.
Table 16.2. JAR Command Options
Option | Description |
---|---|
| Produces verbose output on stdout while the JAR file is being built. The verbose output tells you the name of each file as it’s added to the JAR file. |
| Indicates that you don’t want the JAR file to be compressed. |
| Indicates that the default manifest file should not be produced. |
| Used to include manifest information from an existing manifest file. The format for using this option is: jar cmf existing-manifest jar-file input-file(s) See the Modifying a Manifest File section (page 501) for more information about this option. |
| To change directories during execution of the command. See the An Example section (page 491). |
When you create a JAR file, the time of creation is stored in the JAR file. Therefore, even if the contents of the JAR file do not change, when you create a JAR file multiple times, the resulting files are not exactly identical. You should be aware of this when you are using JAR files in a build environment. It is recommended that you use versioning information in the manifest file, rather than creation time, to control versions of a JAR file; see the Setting Package Version Information section (page 504).
Let us look at an example, a simple TicTacToe
applet. This demo contains a bytecode class file, audio files, and images having the structure shown in Figure 16.1.
The audio
and images
subdirectories contain sound files and GIF images used by the applet.
You can obtain all these files from jar/examples directory.[7] To package this demo into a single JAR file named TicTacToe.jar
, you would run this command from inside the TicTacToe
directory:
jar cvf TicTacToe.jar TicTacToe.class audio images
The audio
and images
arguments represent directories, so the Jar tool will recursively place them and their contents in the JAR file. The generated JAR file TicTacToe.jar
will be placed in the current directory. Because the command used the v
option for verbose output, you would see something similar to this output when you run the command:
adding: TicTacToe.class (in=3825) (out=2222) (deflated 41%) adding: audio/ (in=0) (out=0) (stored 0%) adding: audio/beep.au (in=4032) (out=3572) (deflated 11%) adding: audio/ding.au (in=2566) (out=2055) (deflated 19%) adding: audio/return.au (in=6558) (out=4401) (deflated 32%) adding: audio/yahoo1.au (in=7834) (out=6985) (deflated 10%) adding: audio/yahoo2.au (in=7463) (out=4607) (deflated 38%) adding: images/ (in=0) (out=0) (stored 0%) adding: images/cross.gif (in=157) (out=160) (deflated -1%) adding: images/not.gif (in=158) (out=161) (deflated -1%)
You can see from this output that the JAR file TicTacToe.jar
is compressed. The Jar tool compresses files by default. You can turn off the compression feature by using the 0
(zero) option, so that the command would look like:
jar cvf0 TicTacToe.jar TicTacToe.class audio images
You might want to avoid compression, for example, to increase the speed with which a JAR file could be loaded by a browser. Uncompressed JAR files can generally be loaded more quickly than compressed files because the need to decompress the files during loading is eliminated. However, there is a tradeoff in that download time over a network may be longer for larger, uncompressed files.
The Jar tool will accept arguments that use the wildcard *
symbol. As long as there weren’t any unwanted files in the TicTacToe
directory, you could have used this alternative command to construct the JAR file:
jar cvf TicTacToe.jar *
Though the verbose output doesn’t indicate it, the Jar tool automatically adds a manifest file to the JAR archive with path name META-INF/MANIFEST.MF
. See the Working with Manifest Files: The Basics section (page 500) for information about manifest files.
In the previous example, the files in the archive retained their relative path names and directory structure. The Jar tool provides the -C
option that you can use to create a JAR file in which the relative paths of the archived files are not preserved. It’s modeled after TAR’s -C
option.
As an example, suppose you wanted to put audio files and gif images used by the TicTacToe demo into a JAR file, and that you wanted all the files to be on the top level, with no directory hierarchy. You could accomplish that by issuing this command from the parent directory of the images
and audio
directories:
jar cf ImageAudio.jar -C images . -C audio .
The -C images
part of this command directs the Jar tool to go to the images
directory, and the .
following -C images
directs the Jar tool to archive all the contents of that directory. The -C audio .
part of the command then does the same with the audio
directory. The resulting JAR file would have this table of contents:
META-INF/MANIFEST.MF cross.gif not.gif beep.au ding.au return.au yahoo1.au yahoo2.au
By contrast, suppose that you used a command that did not employ the -C
option:
jar cf ImageAudio.jar images audio
The resulting JAR file would have this table of contents:
META-INF/MANIFEST.MF images/cross.gif images/not.gif audio/beep.au audio/ding.au audio/return.au audio/yahoo1.au audio/yahoo2.au
The basic format of the command for viewing the contents of a JAR file is:
jar tf jar-file
Let’s look at the options and argument used in this command:
The t
and f
options can appear in either order, but there must not be any space between them.
This command will display the JAR file’s table of contents to stdout.
You can optionally add the verbose option, v
, to produce additional information about file sizes and last-modified dates in the output.
Let’s use the Jar tool to list the contents of the TicTacToe.jar
file we created in the previous section:
jar tf TicTacToe.jar
This command displays the contents of the JAR file to stdout:
META-INF/MANIFEST.MF TicTacToe.class audio/ audio/beep.au audio/ding.au audio/return.au audio/yahoo1.au audio/yahoo2.au images/ images/cross.gif images/not.gif
The JAR file contains the TicTacToe
class file and the audio and images directory, as expected. The output also shows that the JAR file contains a default manifest file, META-INF/MANIFEST.MF
, which was automatically placed in the archive by the JAR tool. For more information, see the Understanding the Default Manifest section (page 500).
All pathnames are displayed with forward slashes, regardless of the platform or operating system you’re using. Paths in JAR files are always relative; you’ll never see a path beginning with C:
, for example.
The JAR tool will display additional information if you use the v
option:
jar tvf TicTacToe.jar
For example, the verbose output for the TicTacToe JAR file would look similar to this:
256 Mon Apr 18 10:50:28 PDT 2005 META-INF/MANIFEST.MF 3885 Mon Apr 18 10:49:50 PDT 2005 TicTacToe.class 0 Wed Apr 20 16:39:32 PDT 2005 audio/ 4032 Wed Apr 20 16:39:32 PDT 2005 audio/beep.au 2566 Wed Apr 20 16:39:32 PDT 2005 audio/ding.au 6558 Wed Apr 20 16:39:32 PDT 2005 audio/return.au 7834 Wed Apr 20 16:39:32 PDT 2005 audio/yahoo1.au 7463 Wed Apr 20 16:39:32 PDT 2005 audio/yahoo2.au 0 Wed Apr 20 16:39:44 PDT 2005 images/ 157 Wed Apr 20 16:39:44 PDT 2005 images/cross.gif 158 Wed Apr 20 16:39:44 PDT 2005 images/not.gif
The basic command to use for extracting the contents of a JAR file is:
jar xf jar-file [archived-file(s)]
Let’s look at the options and arguments in this command:
The x
option indicates that you want to extract files from the JAR archive.
The f
option indicates that the JAR file from which files are to be extracted is specified on the command line, rather than through stdin.
The jar-file
argument is the filename (or path and filename) of the JAR file from which to extract files.
archived-file(s)
is an optional argument consisting of a space-separated list of the files to be extracted from the archive. If this argument is not present, the Jar tool will extract all the files in the archive.
As usual, the order in which the x
and f
options appear in the command doesn’t matter, but there must not be a space between them.
When extracting files, the Jar tool makes copies of the desired files and writes them to the current directory, reproducing the directory structure that the files have in the archive. The original JAR file remains unchanged.
When it extracts files, the Jar tool will overwrite any existing files having the same pathname as the extracted files.
Let’s extract some files from the TicTacToe JAR file we’ve been using in previous sections. Recall that the contents of TicTacToe.jar
are:
META-INF/MANIFEST.MF TicTacToe.class audio/ audio/beep.au audio/ding.au audio/return.au audio/yahoo1.au audio/yahoo2.au images/ images/cross.gif images/not.gif
Suppose you want to extract the TicTacToe
class file and the cross.gif
image file. To do so, you can use this command:
jar xf TicTacToe.jar TicTacToe.class images/cross.gif
This command does two things:
The original TicTacToe JAR file remains unchanged.
As many files as desired can be extracted from the JAR file in the same way. When the command doesn’t specify which files to extract, the Jar tool extracts all files in the archive. For example, you can extract all the files in the TicTacToe archive by using this command:
jar xf TicTacToe.jar
The Jar tool provides a u
option that you can use to update the contents of an existing JAR file by modifying its manifest or by adding files.
The basic command for adding files has this format:
jar uf jar-file input-file(s)
In this command:
The u
option indicates that you want to update an existing JAR file.
The f
option indicates that the JAR file to update is specified on the command line.
jar-file
is the existing JAR file that’s to be updated.
input-file(s)
is a space-deliminated list of one or more files that you want to add to the Jar file.
Any files already in the archive having the same pathname as a file being added will be overwritten.
When creating a new JAR file, you can optionally use the -C
option to indicate a change of directory. For more information, see the Creating a JAR File section (page 490).
Recall that TicTacToe.jar
has these contents:
META-INF/MANIFEST.MF TicTacToe.class audio/ audio/beep.au audio/ding.au audio/return.au audio/yahoo1.au audio/yahoo2.au images/ images/cross.gif images/not.gif
Suppose that you want to add the file images/new.gif
to the JAR file. You could accomplish that by issuing this command from the parent directory of the images
directory:
jar uf TicTacToe.jar images/new.gif
The revised JAR file would have this table of contents:
META-INF/MANIFEST.MF TicTacToe.class audio/ audio/beep.au audio/ding.au audio/return.au audio/yahoo1.au audio/yahoo2.au images/ images/cross.gif images/not.gif images/new.gif
You can use the -C
option to “change directories” during execution of the command. For example:
jar uf TicTacToe.jar -C images new.gif
This command would change to the images
directory before adding new.gif
to the JAR file. The images
directory would not be included in the pathname of new.gif
when it’s added to the archive, resulting in a table of contents that looks like this:
META-INF/MANIFEST.MF TicTacToe.class audio/ audio/beep.au audio/ding.au audio/return.au audio/yahoo1.au audio/yahoo2.au images/ images/cross.gif images/not.gif new.gif
Now that you’ve learned how to create JAR files, how do you actually run the code that you’ve packaged? Consider these three scenarios:
Your JAR file contains an applet that is to be run inside a browser.
Your JAR file contains an application that is to be invoked from the command line.
Your JAR file contains code that you want to use as an extension.
This section will cover the first two situations. The Extension Mechanism by Alan Sommerer[8] covers the use of JAR files as extensions.
To invoke any applet from an HTML file for running inside a browser, you need to use the APPLET
tag. For more information, see Chapter 18. If the applet is bundled as a JAR file, the only thing you need to do differently is to use the ARCHIVE
parameter to specify the relative path to the JAR file.
As an example, let’s use (again!) the TicTacToe demo applet that ships with the Java Development Kit. The APPLET
tag in the HTML file that calls the demo looks like this:
<applet code=TicTacToe.class width=120 height=120> </applet>
If the TicTacToe demo were packaged in a JAR file named TicTacToe.jar
, you could modify the APPLET
tag with the simple addition of an ARCHIVE
parameter:
<applet code=TicTacToe.class archive="TicTacToe.jar" width=120 height=120> </applet>
The ARCHIVE
parameter specifies the relative path to the JAR file that contains TicTacToe.class
. This example assumes that the JAR file and the HTML file are in the same directory. If they’re not, you would need to include the JAR file’s relative path in the ARCHIVE
parameter’s value. For example, if the JAR file was one directory below the HTML file in a directory called applets
, the APPLET
tag would look like this:
<applet code=TicTacToe.class archive="applets/TicTacToe.jar" width=120 height=120> </applet>
You can run JAR-packaged applications with the Java interpreter. The basic command is:
java -jar jar-file
The -jar
flag tells the interpreter that the application is packaged in the JAR file format. You can only specify one JAR file, which must contain all the application-specific code.
Before you execute this command, make sure the runtime environment has information as to which class within the JAR file is the application’s entry point.
To indicate which class is the application’s entry point, you must add a Main-Class
header to the JAR file’s manifest. The header takes the form:
Main-Class: classname
The header’s value, classname
, is the name of the class that’s the application’s entry point.
For more information, see the Setting an Application’s Entry Point section (page 502).
When the Main-Class
is set in the manifest file, you can run the application from the command line:
java -jar app.jar
JAR files support a wide range of functionality, including electronic signing, version control, package sealing, and others. What gives a JAR file this versatility? The answer is the JAR file’s manifest.
The manifest is a special file that can contain information about the files packaged in a JAR file. By tailoring this “meta” information that the manifest contains, you enable the JAR file to serve a variety of purposes.
This section will explain the contents of the manifest file and show you how to work with it, with examples for the basic features.
A specification of the manifest format is part of the online JDK documentation.
When you create a JAR file, it automatically receives a default manifest file. There can be only one manifest file in an archive, and it always has the pathname:
META-INF/MANIFEST.MF
When you create a JAR file, the default manifest file simply contains the following:
Manifest-Version: 1.0 Created-By: 1.6.0 (Sun Microsystems Inc.)
These lines show that a manifest’s entries take the form of “header: value” pairs. The name of a header is separated from its value by a colon. The default manifest conforms to version 1.0 of the manifest specification and was created by the 1.6.0 version of the JDK.
The manifest can also contain information about the other files that are packaged in the archive. Exactly what file information should be recorded in the manifest depends on how you intend to use the JAR file. The default manifest makes no assumptions about what information it should record about other files.
Digest information is not included in the default manifest. To learn more about digests and signing, see the Signing and Verifying JAR Files section (page 507).
You use the m
command-line option to add custom information to the manifest during creation of a JAR file. This section describes the m
option.
The Jar tool automatically puts a default manifest (see the Understanding the Default Manifest section, page 500) with the pathname META-INF/MANIFEST.MF
into any JAR file you create. You can enable special JAR file functionality, such as package sealing (see the Sealing Packages Within a JAR File section, page 506), by modifying the default manifest. Typically, modifying the default manifest involves adding specialpurpose headers to the manifest that allow the JAR file to perform a particular desired function.
To modify the manifest, you must first prepare a text file containing the information you wish to add to the manifest. You then use the Jar tool’s m
option to add the information in your file to the manifest.
The text file from which you are creating the manifest must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.
The basic command has this format:
jar cmf jar-file manifest-addition input-file(s)
Let’s look at the options and arguments used in this command:
The c
option indicates that you want to create a JAR file.
The m
option indicates that you want to merge information from an existing file into the manifest file of the JAR file you’re creating.
The f
option indicates that you want the output to go to a file (the JAR file you’re creating) rather than to standard output.
manifest-addition is the name (or path and name) of the existing text file whose contents you want to add to the contents of JAR file’s manifest.
jar-file is the name that you want the resulting JAR file to have.
The input-file(s) argument is a space-separated list of one or more files that you want to be placed in your JAR file.
The m
and f
options must be in the same order as the corresponding arguments.
The remaining subsections of this section demonstrate specific modifications you may want to make to the manifest file.
If you have an application bundled in a JAR file, you need some way to indicate which class within the JAR file is your application’s entry point. You provide this information with the Main-Class
header in the manifest, which has the general form:
Main-Class: classname
The value classname is the name of the class that is your application’s entry point.
Recall that the entry point is a class having a method with signature public static void main(String[] args)
.
After you have set the Main-Class
header in the manifest, you then run the JAR file using the following form of the java
command:
java -jar JAR-name
The main
method of the class specified in the Main-Class
header is executed.
We want to execute the main
method in the class MyClass
in the package MyPackage
when we run the JAR file.
We first create a text file named Manifest.txt
with the following contents:
Main-Class: MyPackage.MyClass
The text file must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.
We then create a JAR file named MyJar.jar
by entering the following command:
jar cfm MyJar.jar Manifest.txt MyPackage/*.class
This creates the JAR file with a manifest with the following contents:
Manifest-Version: 1.0 Created-By: 1.6.0 (Sun Microsystems Inc.) Main-Class: MyPackage.MyClass
When you run the JAR file with the following command, the main method of MyClass executes:
java -jar MyJar.jar
The e flag (for “entrypoint”) creates or overrides the manifest’s Main-Class
attribute. It can be used while creating or updating a jar file. Use it to specify the application entry point without editing or creating the manifest file.
For example, this command creates app.jar
where the Main-Class
attribute value in the manifest is set to MyApp:
jar cfe app.jar MyApp MyApp.class
You can directly invoke this application by running the following command:
java -jar app.jar
If the entrypoint class name is in a package it may use a “.” (dot) character as the delimiter. For example, if Main.class
is in a package called foo
, the entry point can be specified in the following ways:
jar -cfe Main.jar foo.Main foo/Main.class
You may need to reference classes in other JAR files from within a JAR file.
For example, in a typical situation an applet is bundled in a JAR file whose manifest references a different JAR file (or several different JAR files) that serves as a utility for the purposes of that applet.
You specify classes to include in the Class-Path
header field in the manifest file of an applet or application. The Class-Path
header takes the following form:
Class-Path: jar1-name jar2-name directory-name/jar3-name
By using the Class-Path
header in the manifest, you can avoid having to specify a long -classpath
flag when invoking Java to run your application.
The Class-Path
header points to classes or JAR files on the local network, not JAR files within the JAR file or classes accessible over Internet protocols. To load classes in JAR files within a JAR file into the class path, you must write custom code to load those classes. For example, if MyJar.jar
contains another JAR file called MyUtils.jar
, you cannot use the Class-Path
header in MyJar.jar
’s manifest to load classes in MyUtils.jar
into the class path.
We want to load classes in MyUtils.jar
into the class path for use in MyJar.jar
. These two JAR files are in the same directory.
We first create a text file named Manifest.txt
with the following contents:
Class-Path: MyUtils.jar
The text file must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.
We then create a JAR file named MyJar.jar
by entering the following command:
jar cfm MyJar.jar Manifest.txt MyPackage/*.class
This creates the JAR file with a manifest with the following contents:
Manifest-Version: 1.0 Class-Path: MyUtils.jar Created-By: 1.6.0 (Sun Microsystems Inc.)
The classes in MyUtils.jar
are now loaded into the class path when you run MyJar.jar
.
You may need to include package version information in a JAR file’s manifest. You provide this information with the headers listed in Table 16.3 in the manifest.
Table 16.3. Headers in a Manifest
Header | Definition |
---|---|
| The name of the specification. |
| The title of the specification. |
| The version of the specification. |
| The vendor of the specification. |
| The title of the implementation. |
| The build number of the implementation. |
| The vendor of the implementation. |
One set of such headers can be assigned to each package. The versioning headers should appear directly beneath the Name
header for the package. This example shows all the versioning headers:
Name: java/util/ Specification-Title: Java Utility Classes Specification-Version: 1.2 Specification-Vendor: Sun Microsystems, Inc. Implementation-Title: java.util Implementation-Version: build57 Implementation-Vendor: Sun Microsystems, Inc.
For more information about package version headers, see the Package Versioning specification.[9]
We want to include the headers in the previous example in the manifest of MyJar.jar
.
We first create a text file named Manifest.txt
with the following contents:
Name: java/util/ Specification-Title: Java Utility Classes Specification-Version: 1.2 Specification-Vendor: Sun Microsystems, Inc. Implementation-Title: java.util Implementation-Version: build57 Implementation-Vendor: Sun Microsystems, Inc.
The text file must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.
We then create a JAR file named MyJar.jar
by entering the following command:
jar cmf MyJar.jar Manifest.txt MyPackage/*.class
This creates the JAR file with a manifest with the following contents:
Manifest-Version: 1.0 Created-By: 1.6.0 (Sun Microsystems Inc.) Name: java/util/ Specification-Title: Java Utility Classes Specification-Version: 1.2 Specification-Vendor: Sun Microsystems, Inc. Implementation-Title: java.util Implementation-Version: build57 Implementation-Vendor: Sun Microsystems, Inc.
Packages within JAR files can be optionally sealed, which means that all classes defined in that package must be archived in the same JAR file. You might want to seal a package, for example, to ensure version consistency among the classes in your software.
You seal a package in a JAR file by adding the Sealed
header in the manifest, which has the general form:
Name: myCompany/myPackage/ Sealed: true
The value myCompany/myPackage/
is the name of the package to seal.
Note that the package name must end with a “/
”.
We want to seal two packages firstPackage
and secondPackage
in the JAR file MyJar.jar
.
We first create a text file named Manifest.txt
with the following contents:
Name: myCompany/firstPackage/ Sealed: true Name: myCompany/secondPackage/ Sealed: true
The text file must end with a new line or carriage return. The last line will not be parsed properly if it does not end with a new line or carriage return.
We then create a JAR file named MyJar.jar
by entering the following command:
jar cmf MyJar.jar Manifest.txt MyPackage/*.class
This creates the JAR file with a manifest with the following contents:
Manifest-Version: 1.0 Created-By: 1.6.0 (Sun Microsystems Inc.) Name: myCompany/firstPackage/ Sealed: true Name: myCompany/secondPackage/ Sealed: true
If you want to guarantee that all classes in a package come from the same code source, use JAR sealing. A sealed JAR specifies that all packages defined by that JAR are sealed unless overridden on a per-package basis.
To seal a jar file, use the Sealed
manifest header with the value true. For example:
Sealed: true
specifies that all packages in this archive are sealed unless explicitly overridden for particular packages with the Sealed
attribute in a manifest entry.
For further information on how to seal JAR files, see JDC TechTips.[10]
You can optionally sign a JAR file with your electronic “signature.” Users who verify your signature can grant your JAR-bundled software security privileges that it wouldn’t ordinarily have. Conversely, you can verify the signatures of signed JAR files that you want to use.
This section shows you how to use the tools provided in the Java Development Kit to sign and verify JAR files.
The Java platform enables you to digitally sign JAR files. You digitally sign a file for the same reason you might sign a paper document with pen and ink—to let readers know that you wrote the document, or at least that the document has your approval.
When you sign a letter, for example, everyone who recognizes your signature can confirm that you wrote the letter. Similarly, when you digitally sign a file, anyone who “recognizes” your digital signature knows that the file came from you. The process of “recognizing” electronic signatures is called verification.
The ability to sign and verify files is an important part of the Java platform’s security architecture. Security is controlled by the security policy that’s in force at runtime. You can configure the policy to grant security privileges to applets and to applications.
For example, you could grant permission to an applet to perform normally forbidden operations such as reading and writing local files or running local executable programs. If you have downloaded some code that’s signed by a trusted entity, you can use that fact as a criterion in deciding which security permissions to assign to the code.
Once you (or your browser) have verified that an applet is from a trusted source, you can have the platform relax security restrictions to let the applet perform operations that would ordinarily be forbidden. A trusted applet can have freedoms as specified by the policy file in force.
The Java platform enables signing and verification by using special numbers called public and private keys. Public keys and private keys come in pairs, and they play complementary roles.
The private key is the electronic “pen” with which you can sign a file. As its name implies, your private key is known only to you so that no one else can “forge” your signature. A file signed with your private key can be verified only by the corresponding public key.
Public and private keys alone, however, aren’t enough to truly verify a signature. Even if you’ve verified that a signed file contains a matching key pair, you still need some way to confirm that the public key actually comes from the signer that it purports to come from.
One more element, therefore, is required to make signing and verification work. That additional element is the certificate that the signer includes in a signed JAR file. A certificate is a digitally signed statement from a recognized certification authority that indicates who owns a particular public key. Certification authorities are entities (typically firms specializing in digital security) that are trusted throughout the industry to sign and issue certificates for keys and their owners. In the case of signed JAR files, the certificate indicates who owns the public key contained in the JAR file.
When you sign a JAR file, your public key is placed inside the archive along with an associated certificate so that it’s easily available for use by anyone wanting to verify your signature.
To summarize digital signing:
The signer signs the JAR file using a private key.
The corresponding public key is placed in the JAR file, together with its certificate, so that it is available for use by anyone who wants to verify the signature.
When you sign a JAR file, each file in the archive is given a digest entry in the archive’s manifest (see the Working with Manifest Files: The Basics section, page 500). Here’s an example of what such an entry might look like:
Name: test/classes/ClassOne.class SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=
The digest values are hashes, or encoded representations, of the contents of the files as they were at the time of signing. A file’s digest will change if and only if the file itself changes.
When a JAR file is signed, a signature file is automatically generated and placed in the JAR file’s META-INF
directory, the same directory that contains the archive’s manifest. Signature files have filenames with an .SF
extension. Here is an example of the contents of a signature file:
Signature-Version: 1.0 SHA1-Digest-Manifest: h1yS+K9T7DyHtZrtI+LxvgqaMYM= Created-By: 1.6.0 (Sun Microsystems Inc.) Name: test/classes/ClassOne.class SHA1-Digest: fcav7ShIG6i86xPepmitOVo4vWY= Name: test/classes/ClassTwo.class SHA1-Digest: xrQem9snnPhLySDiZyclMlsFdtM= Name: test/images/ImageOne.gif SHA1-Digest: kdHbE7kL9ZHLgK7akHttYV4XIa0= Name: test/images/ImageTwo.gif SHA1-Digest: mF0D5zpk68R4oaxEqoS9Q7nhm60=
As you can see, the signature file contains digest entries for the archive’s files that look similar to the digest-value entries in the manifest. However, while the digest values in the manifest are computed from the files themselves, the digest values in the signature file are computed from the corresponding entries in the manifest. Signature files also contain a digest value for the entire manifest (see the SHA1-Digest-Manifest
header in the previous example).
When a signed JAR file is being verified, the digests of each of its files are re-computed and compared with the digests recorded in the manifest to ensure that the contents of the JAR file haven’t changed since it was signed. As an additional check, digest values for the manifest file itself are re-computed and compared against the values recorded in the signature file.
You can read additional information about signature files on the Manifest Format page of the JDK documentation.
In addition to the signature file, a signature block file is automatically placed in the META-INF
directory when a JAR file is signed. Unlike the manifest file or the signature file, signature block files are not human-readable.
The signature block file contains two elements essential for verification:
The digital signature for the JAR file that was generated with the signer’s private key
The certificate containing the signer’s public key, to be used by anyone wanting to verify the signed JAR file
Signature block filenames typically will have a .DSA
extension indicating that they were created by the default Digital Signature Algorithm. Other filename extensions are possible if keys associated with some other standard algorithm are used for signing.
For additional information about keys, certificates, and certification authorities, see:
For more information about the Java platform’s security architecture, see this related documentation:
You use the JAR Signing and Verification Tool to sign JAR files. You invoke the JAR Signing and Verification Tool by using the jarsigner
command, so we’ll refer to it as “Jarsigner” for short.
To sign a JAR file, you must first have a private key. Private keys and their associated public-key certificates are stored in password-protected databases called keystores. A keystore can hold the keys of many potential signers. Each key in the keystore can be identified by an alias, which is typically the name of the signer who owns the key. The key belonging to Rita Jones might have the alias “rita”, for example.
The basic form of the command for signing a JAR file is:
jarsigner jar-file alias
In this command:
jar-file
is the pathname of the JAR file that’s to be signed.
alias
is the alias identifying the private key that’s to be used to sign the JAR file, and the key’s associated certificate.
The Jarsigner tool will prompt you for the passwords for the keystore and alias.
This basic form of the command assumes that the keystore to be used is in a file named .keystore
in your home directory. It will create signature and signature block files with names x.SF
and x.DSA
respectively, where x
is the first eight letters of the alias, all converted to uppercase. This basic command will overwrite the original JAR file with the signed JAR file.
In practice, you may want to use this command in conjunction with one or more of the options shown in Table 16.4, which must precede the jar-file
pathname.
Table 16.4. Jarsigner Command Options
Option | Description |
---|---|
| Specifies a keystore to be used if you don’t want to use the |
| Allows you to enter the keystore’s password on the command line rather than be prompted for it. |
| Allows you to enter your alias’s password on the command line rather than be prompted for it. |
| Specifies the base name for the .SF and .DSA files if you don’t want the base name to be taken from your alias. file must be composed only of uppercase letters ( |
| Specifies the name of the signed JAR file to be generated if you don’t want the original unsigned file to be overwritten with the signed file. |
Let’s look at a couple of examples of signing a JAR file with the Jarsigner tool. In these examples we will assume:
Your alias is “johndoe”.
The keystore you want to use is in a file named “mykeys” in the current working directory.
The keystore’s password is “abc123”.
The password for your alias is “mypass”.
Under these assumptions, you could use this command to sign a JAR file named app.jar
:
jarsigner -keystore mykeys -storepass abc123 -keypass mypass app.jar johndoe
Because this command doesn’t make use of the -sigfile
option, the .SF and .DSA files it creates would be named JOHNDOE.SF
and JOHNDOE.DSA
. Because the command doesn’t use the -signedjar
option, the resulting signed file will overwrite the original version of app.jar
.
Let’s look at what would happen if you used a different combination of options:
jarsigner -keystore mykeys -sigfile SIG -signedjar SignedApp.jar app.jar johndoe
This time, you would be prompted to enter the passwords for both the keystore and your alias because the passwords aren’t specified on the command line. The signature and signature block files would be named SIG.SF
and SIG.DSA
, respectively, and the signed JAR file SignedApp.jar
would be placed in the current directory. The original unsigned JAR file would remain unchanged.
Complete reference pages for the JAR Signing and Verification Tool are online at Summary of Security Tools.[16]
Typically, verification of signed JAR files will be the responsibility of your Java Runtime Environment. Your browser will verify signed applets that it downloads. Signed applications invoked with the -jar
option of the interpreter will be verified by the runtime environment.
However, you can verify signed JAR files yourself by using the Jarsigner tool. You might want to do this, for example, to test a signed JAR file that you’ve prepared.
The basic command to use for verifying a signed JAR file is:
jarsigner -verify jar-file
This command will verify the JAR file’s signature and ensure that the files in the archive haven’t changed since it was signed. You’ll see the following message if the verification is successful:
jar verified.
If you try to verify an unsigned JAR file, the following message results:
jar is unsigned. (signatures missing or not parsable)
If the verification fails, an appropriate message is displayed. For example, if the contents of a JAR file have changed since the JAR file was signed, a message similar to the following will result if you try to verify the file:
jarsigner: java.lang.SecurityException: invalid SHA1 signature file digest for test/classes/Manifest.class
The Java platform contains several classes for use with JAR files. Some of these APIs are:
To give you an idea of the possibilities that are opened up by these new APIs, this section guides you through the inner workings of a sample application called JarRunner.
JarRunner enables you to run an application that’s bundled in a JAR file by specifying the JAR file’s URL on the command line. For example, if an application called TargetApp
were bundled in a JAR file at http://www.xxx.yyy/TargetApp.jar
, you could run the application using this command:
java JarRunner http://www.xxx.yyy/TargetApp.jar
In order for JarRunner to work, it must be able to perform the following tasks, all of which are accomplished by using the new APIs:
Access the remote JAR file and establish a communications link with it.
Inspect the JAR file’s manifest to see which of the classes in the archive is the main class.
Load the classes in the JAR file.
The JarRunner application consists of two classes, JarRunner
and JarClassLoader
. JarRunner
delegates most of the JAR-handling tasks to the JarClassLoader
class. JarClassLoader
extends the java.net.URLClassLoader
class. You can browse the source code for the JarRunner
and JarClassLoader
classes before proceeding with the section:
The JarClassLoader
class extends java.net.URLClassLoader
. As its name implies, URLClassLoader
is designed to be used for loading classes and resources that are accessed by searching a set of URLs. The URLs can refer either to directories or to JAR files.
In addition to subclassing URLClassLoader
, JarClassLoader
also makes use of features in two other new JAR-related APIs, the java.util.jar
package and the java.net.JarURLConnection
class. In this section, we’ll look in detail at the constructor and two methods of JarClassLoader
.
The constructor takes an instance of java.net.URL
as an argument. The URL passed to this constructor will be used elsewhere in JarClassLoader
to find the JAR file from which classes are to be loaded:
public JarClassLoader(URL url) { super(new URL[] { url }); this.url = url; }
The URL
object is passed to the constructor of the superclass, URLClassLoader
, which takes a URL[]
array, rather than a single URL
instance, as an argument.
Once a JarClassLoader
object is constructed with the URL of a JAR-bundled application, it’s going to need a way to determine which class in the JAR file is the application’s entry point. That’s the job of the getMainClassName
method:
public String getMainClassName() throws IOException { URL u = new URL("jar", "", url + "!/"); JarURLConnection uc = (JarURLConnection)u.openConnection(); Attributes attr = uc.getMainAttributes(); return attr != null ? attr.getValue(Attributes.Name.MAIN_CLASS) : null; }
You may recall from the JAR Files as Applications section (page 499) that a JAR-bundled application’s entry point is specified by the Main-Class
header of the JAR file’s manifest. To understand how getMainClassName
accesses the Main-Class
header value, let’s look at the method in detail, paying special attention to the new JAR-handling features that it uses.
The getMainClassName
method uses the JAR URL format specified by the java.net.JarURLConnection
class. The syntax for the URL of a JAR file is as in this example:
jar:http://www.xxx.yyy/jarfile.jar!/
The terminating !/
separator indicates that the URL refers to an entire JAR file. Anything following the separator refers to specific JAR-file contents, as in this example:
jar:http://www.xxx.yyy/jarfile.jar!/mypackage/myclass.class
The first line in the getMainClassName
method is:
URL u = new URL("jar", "", url + "!/");
This statement constructs a new URL
object representing a JAR URL, appending the !/
separator to the URL that was used in creating the JarClassLoader
instance.
This class represents a communications link between an application and a JAR file. It has methods for accessing the JAR file’s manifest. The second line of getMainClassName
is:
JarURLConnection uc = (JarURLConnection)u.openConnection();
In this statement, the URL
instance created in the first line opens a URLConnection
. The URLConnection
instance is then cast to JarURLConnection
so it can take advantage of JarURLConnection
’s JAR-handling features.
With a JarURLConnection
open to a JAR file, you can access the header information in the JAR file’s manifest by using the getMainAttributes
method of JarURLConnection
. This method returns an instance of java.util.jar. Attributes
, a class that maps header names in JAR-file manifests with their associated string values. The third line in getMainClassName
creates an Attributes
object:
Attributes attr = uc.getMainAttributes();
To get the value of the manifest’s Main-Class
header, the fourth line of getMainClassName
invokes the Attributes.getValue
method:
return attr != null ? attr.getValue(Attributes.Name.MAIN_CLASS) : null;
The method’s argument, Attributes.Name.MAIN_CLASS
, specifies that it’s the value of the Main-Class
header that you want. (The Attributes.Name
class also provides static fields such as MANIFEST_VERSION
, CLASS_PATH
, and SEALED
for specifying other standard manifest headers.)
We’ve seen how JarClassLoader
can identify the main class in a JAR-bundled application. The last method to consider, JarClassLoader.invokeClass
, enables that main class to be invoked to launch the JAR-bundled application:
public void invokeClass(String name, String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException { Class c = loadClass(name); Method m = c.getMethod("main", new Class[] { args.getClass() }); m.setAccessible(true); int mods = m.getModifiers(); if (m.getReturnType() != void.class || !Modifier.isStatic(mods) || !Modifier.isPublic(mods)) { throw new NoSuchMethodException("main"); } try { m.invoke(null, new Object[] { args }); } catch (IllegalAccessException e) { // This should not happen, as we have disabled access checks } }
The invokeClass
method takes two arguments: the name of the application’s entry-point class and an array of string arguments to pass to the entry-point class’s main
method. First, the main class is loaded:
Class c = loadClass(name);
The loadClass
method is inherited from java.lang.ClassLoader
.
Once the main class is loaded, the reflection API of the java.lang.reflect
package is used to pass the arguments to the class and launch it. You can refer to the tutorial “The Reflection API”[22] for a review of reflection.
The JarRunner application is launched with a command of this form:
java JarRunner url [arguments]
In the previous section, we’ve seen how JarClassLoader
is able to identify and load the main class of a JAR-bundled application from a given URL. To complete the Jar-Runner application, therefore, we need to be able to take a URL and any arguments from the command line, and pass them to an instance of JarClassLoader
. These tasks belong to the JarRunner
class, the entry point of the JarRunner application.
It begins by creating a java.net.URL
object from the URL specified on the command line:
public static void main(String[] args) { if (args.length < 1) { usage(); } URL url = null; try { url = new URL(args[0]); } catch (MalformedURLException e) { fatal("Invalid URL: " + args[0]); } ...
If args.length < 1
, that means no URL was specified on the command line, so a usage message is printed. If the first command-line argument is a good URL, a new URL
object is created to represent it.
Next, JarRunner creates a new instance of JarClassLoader
, passing to the constructor the URL that was specified on the command-line:
JarClassLoader cl = new JarClassLoader(url);
As we saw in the previous section, it’s through JarClassLoader
that JarRunner taps into the JAR-handling APIs.
The URL that’s passed to the JarClassLoader
constructor is the URL of the JAR-bundled application that you want to run. JarRunner next calls the class loader’s getMainClassName
method to identify the entry-point class for the application:
String name = null;
try {
name = cl.getMainClassName();
} catch (IOException e) {
System.err.println("I/O error while loading JAR file:");
e.printStackTrace();
System.exit(1);
}
if (name == null) {
fatal("Specified jar file does not " +
"contain a 'Main-Class' manifest attribute");
}
The key statement is highlighted in bold. The other statements are for error handling.
Once JarRunner
has identified the application’s entry-point class, only two steps remain: passing any arguments to the application and actually launching the application. JarRunner
performs these steps with this code:
// Get arguments for the application
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
// Invoke application's main class
try {
cl.invokeClass(name, newArgs);
} catch (ClassNotFoundException e) {
fatal("Class not found: " + name);
} catch (NoSuchMethodException e) {
fatal("Class does not define a 'main' method: " + name);
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
System.exit(1);
}
Recall that the first command-line argument was the URL of the JAR-bundled application. Any arguments to be passed to that application are therefore in element 1
and beyond in the args
array. JarRunner
takes those elements and creates a new array called newArgs
to pass to the application (bold line above). JarRunner
then passes the entry-point’s class name and the new argument list to the invokeClass
method of JarClassLoader
. As we saw in the previous section, invokeClass
will load the application’s entry-point class, pass it any arguments, and launch the application.
[3] docs/technotes/guides/jar/index.html
[4] docs/technotes/guides/jar/jar.html
[5] docs/technotes/tools/windows/jar.html
[6] docs/technotes/tools/solaris/jar.html
[7] tutorial/deployment/jar/examples/TicTacToe.java
[8] tutorial/ext/index.html
[9] docs/technotes/guides/versioning/spec/versioning2.html
[11] docs/technotes/tools/
[12] docs/technotes/guides/security/cert3.html
[13] docs/books/tutorial/security/
[15] docs/technotes/tools/
[16] docs/technotes/guides/security/SecurityToolsSummary.html
[17] docs/api/java/util/jar/package-summary.html
[18] docs/api/java/net/JarURLConnection.html
[19] docs/api/java/net/URLClassLoader.html
[20] tutorial/deployment/jar/interfaces/examples/JarRunner.java
[21] tutorial/deployment/jar/interfaces/examples/JarClassLoader.java
[22] tutorial/reflect/index.html
3.22.181.47