Sunday, May 27, 2012

Packaging Programs in JAR Files

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

Using JAR File

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'll 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 (JDK). 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, the following table summarizes common JAR file operations:
Common JAR file operations
OperationCommand
To create a JAR filejar cf jar-file input-file(s)
To view the contents of a JAR filejar tf jar-file
To extract the contents of a JAR filejar xf jar-file
To extract specific files from a JAR filejar xf jar-file archived-file(s)
To run an application packaged as a JAR file (requires the Main-class manifest header)java -jar app.jar
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:

  1. Creating a JAR File: This section shows you how to use the Jar tool to package files and directories into a JAR file.
  2. Viewing the Contents of a JAR File: You can display a JAR file's table of contents to see what it contains without actually unpacking the JAR file.
  3. Extracting the Contents of a JAR File: You can use the Jar tool to unpack a JAR file. 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.
  4. Updating a JAR File: This section shows you how to update the contents of an existing JAR file by modifying its manifest or by adding files.
  5. Running JAR-Packaged Software: This section shows you how to invoke and run applets and applications that are packaged in JAR files.

Working With Manifest File

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 lesson will explain the contents of the manifest file and show you how to work with it, with examples for the basic features:

Signing and Verifying JAR File

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 lesson shows you how to use the tools provided in the JDK to sign and verify JAR files:

Understanding Signing and Verification: If you're not familiar with the concepts of signing and verification, this section will help to bring you up to speed. It contains definitions of the relevant terms, explanations of some of the benefits provided by signing, and an outline of the signing mechanism used by the Java platform as it relates to JAR files.

Signing JAR Files: In this section, you'll learn how to use the JDK™ tools to digitally sign your JAR files.

Verifying Signed JAR Files:This section shows you how to use the JDK tool set to verify signed JAR files.
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

Using JAR related API

The Java platform contains several classes for use with JAR files. Some of these APIs are:

  1. The java.util.jar package
  2. The java.net.JarURLConnection class
  3. The java.net.URLClassLoader class
To give you an idea of the possibilities that are opened up by these new APIs, this lesson guides you through the inner workings of a sample application called JarRunner.

An Example - The JarRunner Application
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.example.com/TargetApp.jar, you could run the application using this command:
$ java JarRunner http://www.example.com/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 lesson:
  • JarRunner.java
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.lang.reflect.InvocationTargetException;

/**
 * Runs a jar application from any url. Usage is 'java JarRunner url [args..]'
 * where url is the url of the jar file and args is optional arguments to
 * be passed to the application's main method.
 */
public class JarRunner {
    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]);
        }
        // Create the class loader for the application jar file
        JarClassLoader cl = new JarClassLoader(url);
        // Get the application's main class name
        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");
        }
        // 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);
        }
    }

    private static void fatal(String s) {
        System.err.println(s);
        System.exit(1);
    }

    private static void usage() {
        fatal("Usage: java JarRunner url [args..]");
    }
}
  • JarClassLoader.java
import java.net.URL;
import java.net.URLClassLoader;
import java.net.JarURLConnection;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.util.jar.Attributes;
import java.io.IOException;

/**
 * A class loader for loading jar files, both local and remote.
 */
class JarClassLoader extends URLClassLoader {
    private URL url;

    /**
     * Creates a new JarClassLoader for the specified url.
     * @param url the url of the jar file
     */
    public JarClassLoader(URL url) {
        super(new URL[] { url });
        this.url = url;
    }

    /**
     * Returns the name of the jar file main class, or null if
     * no "Main-Class" manifest attributes was defined.
     */
    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;
    }

    /**
     * Invokes the application in this jar file given the name of the main class and an array of arguments. The class must define a
     * static method "main" which takes an array of String arguments and is of return type "void".
     *
     * @param name the name of the main class
     * @param args the arguments for the application
     * @exception ClassNotFoundException if the specified class could not be found
     * @exception NoSuchMethodException if the specified class does not contain a "main" method
     * @exception InvocationTargetException if the application raised an exception
     */
    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
        }
    }
}
This lesson has two parts:
The JarClassLoader Class: This section shows you how JarClassLoader uses some of the new APIs to perform tasks required for the JarRunner application to work.

The JarRunner Class: This section summarizes the JarRunner class that comprises the JarRunner application.


Question & Answer


Q: How do you invoke an applet that is packaged as a JAR file?
Ans: To invoke an applet packaged as a JAR file, open a page containing the applet:
<applet code="AppletClassName.class" archive="JarFileName.jar" width=320 height=240></applet>

Q: What is the purpose of the -e option in a jar command?
Ans: This option is available since Java SE 6. It sets the entry point as the application entry point for stand-alone applications bundled into executable jar file. The use of this option creates or overrides the Main-Class attribute value in the manifest file. This option can be used during creation of jar file or while updating the jar file. This option specifies the application entry point without editing or creating the manifest file. For example, this command creates Main.jar where the Main-Class attribute value in the manifest is set to Main: 
$ jar cfe Main.jar Main Main.class

Q: What is the significance of the manifest in a JAR file?
Ans: A JAR file's manifest provides meta-information about the other contents of the JAR file. The manifest itself resides in META-INF/MANIFEST.mf. The meta-information can include:
  • Dependencies on other jar files
  • The name of a class to run when "java -jar file.jar" is invoked
  • versioning information
  • Security information

Q: How do you modify a JAR's manifest file?
Ans: Typically, modifying the default manifest involves adding special-purpose 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 with a complete and valid manifest file. You then use the JAR tool's m option to add the information in your file to the manifest.
The manifest file your prepare 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.