HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Releasing Java applications can be a real challenge. Fortunately, Java provides a rich set of features for packaging and deploying applications that can simplify the release process significantly.

This article presents some of the issues involved with packaging Java code. I'll explore the Java manifest file and suggest ways it can be used to manage JAR file dependencies and to eliminate classpath issues normally associated with cross-platform deployment. I'll also explain how to use the package-versioning features of the manifest to ensure the compatibility of packages used.

What Is a JAR File?
During development, Java classes are normally compiled into local directories on a disk. Java applications can be run and even distributed in this manner, but it's not a practical way to work. Fortunately, a more manageable approach is available. Java class files can be packaged in Java Archive (JAR) files for distribution and execution.

A JAR file is actually just a ZIP archive of the class files. Using this well-known archive format makes JAR files exceptionally easy to work with. Many tools and libraries exist for manipulating ZIP files, so for the developer the tool set is very rich. However, since they're in a simple archive format, JAR files can't natively express metainformation about the applications they contain.

The Manifest Is Born
To provide metainformation that describes the archive, the JAR file designates a special directory to hold the metainformation, the META-INF directory. For our purposes, I'm concerned with only one file in this directory, MANIFEST.MF. This is the manifest file for the JAR. It describes the contents of the JAR file and provides application information to the JVM at runtime. Most JAR files contain a very basic manifest file by default. Try examining the META-INF/MANIFEST.MF file using any JAR (or ZIP) program. This can be done with any ZIP file tool or by using the JAR command directly.

jar xvf myapplication.jar METAINF/MANIFEST.MF

If your JAR file was created by the JAR tool, you should see a simple default manifest file.

Manifest-Version: 1.0
Created-By:1.4.0-beta
(Sun Microsystems Inc.)

This trivial manifest file lets me know I'm working with a version 1.0 manifest file. Right now this is the only defined manifest file format. The next line tells me this JAR was created by the JAR utility in the Sun Java 1.4 beta SDK. If the manifest file was created, for example, by the Ant build tool, then you might expect to see something like "Created-By: Ant 1.2". When creating your own manifest files, put in text that's relevant to your own project.

The Basic Format
The format of the manifest is simple. Each line of the manifest file is a name-value pair. The attribute name is first, followed by a colon, and then the attri- bute value. Lines should be limited to 72 characters, but if you need more than that, you can continue a value on the next line by starting the line with a space. Any line that begins with a space is considered a continuation of the previous line.

All the attributes at the top of the file are global. You can also apply an attribute to a specific class or package. I'll show examples of that later.

Inserting the Manifest
To add a manifest file to a JAR file, the "m" option is used and the filename of manifest file is passed to the JAR program after the name of the JAR file.

jar cvfm myapplication.jar myapplication.mf -C classdir .

If you use the Ant build tool, the manifest can be specified by adding a manifest attribute to the JAR specification:

<target name="jar">
<jar jarfile ="myapplication.jar"
manifest="myapplication.mf">
<fileset dir="classdir"
includes="**/*.class"/>
</jar>
</target>
A Java Executable
Now that you've had a small taste of the manifest, let's step back and look at a few deployment issues that can be greatly simplified by the manifest. To launch a standard Java application, I need to invoke a Java Virtual Machine and include the application JAR in the classpath, then specify the class I want to invoke. Let's assume again that the JAR is myapplication.jar and the application main class is com.example.myapp.MyAppMain. The application start script will need to invoke:

java -classpath myapplication.jarcom.example.myapp.MyAppMain

While this isn't an overwhelmingly complicated task, it's awkward specifying the main class external to the JAR file. If I were writing a standalone application, I'd expect the application to know the class I want to start execution at. I can specify this with the Main-Class attribute in our manifest.

Manifest-Version: 1.0
Created-By: JDJ example
Main-Class: com.example.myapp.MyAppMain

Now, if I add the manifest to our JAR, I can invoke our Java application much more simply.

java -jar myapplication.jar

This is definitely a much simpler and less error-prone way to launch an application.

Managing JAR Dependencies
This first step is good, but very few Java applications can be distributed as a single JAR. Typically, I need support libraries. Suppose my application is using Sun's Javamail classes and I need to include activation.jar and mail.jar in my classpath. My previously simple Java -jar command becomes a bit less pleasant.

java -classpath mail.jar:activation.jar -jar myapplication.jar

Matters are further complicated by the fact that the classpath specification varies between operating systems. On Unix, classpath elements are separated by a colon, but on Windows, classpath elements are separated by a semicolon.

Again, the manifest file provides a way for me to manage this complexity. What I really have is a JAR dependency. myapplication.jar now depends on mail.jar and activation.jar. Anytime I use myapplication.jar I'll want those two JARs. I can express this dependency in the myapplication.jar manifest.

Manifest-Version: 1.0
Created-By: JDJ example
Main-Class: com.example.myapp.MyAppMain
Class-Path: mail.jar activation.jar

Now I can once again invoke the application in the original simple manner.

java -jar myapplication.jar

Let's look more closely at how this works. The Class-Path attribute is a space-separated list of relative URLs pointing to JAR files that the current JAR references. Keep in mind that I need to escape spaces and special characters as I would in a URL (a space would be escaped as "%20"). I also need to use a forward slash ("/") as the directory separator, regardless of the platform preference. Also note that the URL reference is indeed relative. I couldn't specify C:/some.jaror/usr/local/java/lib/someother.jar as a dependency if my intent is to root my file search path anywhere other than the current directory.

Also, note that when I install the application I still need to make sure that mail.jar and activation.jar are in the same directory as myapplication.jar, otherwise the application will fail to find the JARs I need.

As an alternative, some developers prefer to place support JARs in a subdirectory. For example, suppose that next to the application JAR is a directory ext that contains the support libraries. I could use a manifest like this:

Manifest-Version: 1.0
Created-By: JDJ example
Main-Class: com.example.myapp.MyAppMain
Class-Path: ext/mail.jar ext/activation.jar

Again, keep in mind that the classpath components need to be specified as if they were a relative URL component and were not using platform-specific naming conventions.

Multiple Main Classes
It's not uncommon for Java applications to provide multiple entry points. Perhaps you have both a command-line and a GUI version of your application, or perhaps a main application with several support tools that share a lot of the same code base.

In this case, a good approach is to package all the common JARs into a single application code JAR. Separate application JARs could be created for each entry point, each containing a manifest file with a dependency on the library JAR and a main-class entry that represents the entry point. Depending on the development setup, you may find it simpler to keep all the classes (including the front-end classes) in the library JAR or to package the front-end classes only in their corresponding JAR. The latter is cleaner, but may be troublesome if you haven't organized your classes for easy separation.

The resulting application would look like the following: the lib JAR contains the class files and the dependencies for those class files; the remaining JARs are empty, minus the manifest, and depend only on the classes JAR.

Manifest for myapplicationlib.jar:
Manifest-Version: 1.0
Created-By: JDJ example
Class-Path: mail.jar activation.jar

Manifest for myappconsole.jar:
Manifest-Version: 1.0
Created-By: JDJ example
Class-Path: myapplicationlib.jar
Main-Class: com.example.myapp.MyAppMain

Manifest for myappadmin.jar:
Manifest-Version: 1.0
Created-By: JDJ example
Class-Path: myapplicationlib.jar
Main-Class: com.example.myapp.MyAdminTool

Package Versioning
After you've distributed code, a key issue in release management is figuring out exactly what code you have. What version of the code is running now? What versions of the support libraries are you using? There are a number of approaches to this problem, but the manifest file provides a very elegant solution. In the manifest file, you can describe every package provided in the JAR file according to the Java versioning system.

Java is based on the principal of separating the specification of a technology from its implementation. A package's specification defines what the package is and the implementation defines who is providing the implementation of the specification. The specification and implementation are described by a name, a version number, and a vendor. To get a feel for this information, let's first look at a few JVM system properties (queryable via java.lang.System.getProperty()).

Java first defines the version of the JVM specification being used (see Table 1). This explains that the JVM I'm running is an implementation of Sun's JVM Specification 1.0. It doesn't tell me who created the JVM, it just states that the JVM conforms to a certain standard. To see implementation details, I have to look at the implementation properties. Table 2 is from a Java 1.3 release from Sun.

Table 1
Table 1

Table 2
Table 2

Again, this states that the JVM is from Sun and is the HotSpot Client JVM. The version is 1.3.0_04, which happens to correspond with the API version number but is not formally related to it. The Java API specification and implementation are also defined in system properties ("java.specification.version", "java.specification.name"), but since I'm focusing on my own releases, not the JVM, I'll show how to apply Java versioning to the libraries.

In the manifest file, I can define both a specification and an implementation version for each package by declaring a named entity and then adding specification and implementation attributes. These attributes are:

  • Specification-Title
  • Specification-Version
  • Specification-Vendor
  • Implementation-Title
  • Implementation-Version
  • Implementation-Vendor
Specification information is useful primarily when providing a library or programmatic interface to the outside world, or implementing a programmatic interface defined by a standards body. However, another candidate for specification information for an application would be a design or requirements document, if your development process calls for it.

Implementation attributes can be used to track the internal release information. A common technique is to track internal build numbers in the Implementation-Version field.

Let's extend the manifest to include package information. Suppose I'm releasing build 2002-03-05-A of version 2.4 of MyApp; I could represent this with the following manifest:

Manifest-Version: 1.0
Created-By: JDJ example
Class-Path: mail.jar activation.jar

Name: com/example/myapp/
Specification-Title: MyApp
Specification-Version: 2.4
Specification-Vendor: example.com
Implementation-Title: com.example.myapp
Implementation-Version: 2002-03-05-A
Implementation-Vendor: example.com

Remember to include a blank line between the main attributes section and the entity attributes section and to use slashes, not dots, when referring to packages and class-attribute sections. Think of this as referring to the name of the entity as it exists in the JAR file, not by the true package or class names.

It's quite likely that the JAR file will provide multiple packages. They can all be specified in the manifest. Just remember to leave a blank line between each section.

Querying Package Version
Releasing package information in the manifest is good, but ideally I'd like to be able to access package information at runtime. Java provides convenient access to the manifest package attributes via the java.lang.Package class. There are three basic ways to get a package object.

  1. Package.getPackages(): Returns a list of all the packages currently defined by the system
  2. Package.getPackage(String packagename): Returns a package by name
  3. Class.getPackage(): Returns the package object for a given class
Using these functions, I can provide dynamic logging of package version information (see Listing 1).

It's very important to note that if no classes have been loaded from a given package by the class loader, then there will be no Package object available for the package. That's why I test for null at the beginning. This is important because you may be tempted to log all the packages in the system at startup. However, it's unlikely that all the relevant packages in the application will have been loaded yet. Still, package version information can be quite valuable (particularly for support purposes) so it's a good idea to make this information readily available.

Summary
The manifest file provides a wealth of useful metainformation and greatly simplifies the task of packaging and releasing Java applications. We've seen how to package classes in JAR files, how to track dependencies between JARs, how to turn JARs into executables, and how to track the versions of packages in classes. These are the basic tools for application release management in the Java platform. Making proper use of this functionality can greatly simplify this task.

Manifest Tips

  • Always start a manifest with the Manifest-Version attribute.
  • Limit lines to 72 characters; if longer lines are needed, use line continuations.
  • Make sure the last line has a carriage return at the end; otherwise the line will be ignored.
  • If a Class-Path entry crosses directories, use "/" as the directory separator, regardless of the platform.
  • Separate main attributes from entity attribute sections by a blank line.
  • Use slashes, not dots, to delimit package and class entities.
  • Class entities should end with .class and package entities should end with a slash ("/").

Author Bio
Norman Richards works at Commerce One Labs, the research division of Commerce One. [email protected]

	


Listing 1

  void logPackage() 
    {
      Package package = 
        Package.getPackage("com.example.myapp");
      if (package == null) {
        System.out.println("com.example.myapp not loaded");
        return;
      }
      System.out.println("com.example.myapp version " + 
        package.getSpecificationVersion() +
        " build #" + 
        package.getImplementationVersion());
    }

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.