Java IoT Authors: Liz McMillan, Zakia Bouachraoui, Yeshim Deniz, Elizabeth White, Pat Romanski

Related Topics: Java IoT

Java IoT: Article

Manifest Destiny

Manifest Destiny

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
(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"
<fileset dir="classdir"
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.

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.

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 ("/").

More Stories By Norman Richards

Norman Richards is a JBoss developer living in Austin, Tx. He is co-author of JBoss: A Developer's Notebook and XDoclet in Action.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.

IoT & Smart Cities Stories
As IoT continues to increase momentum, so does the associated risk. Secure Device Lifecycle Management (DLM) is ranked as one of the most important technology areas of IoT. Driving this trend is the realization that secure support for IoT devices provides companies the ability to deliver high-quality, reliable, secure offerings faster, create new revenue streams, and reduce support costs, all while building a competitive advantage in their markets. In this session, we will use customer use cases...
Bill Schmarzo, author of "Big Data: Understanding How Data Powers Big Business" and "Big Data MBA: Driving Business Strategies with Data Science," is responsible for setting the strategy and defining the Big Data service offerings and capabilities for EMC Global Services Big Data Practice. As the CTO for the Big Data Practice, he is responsible for working with organizations to help them identify where and how to start their big data journeys. He's written several white papers, is an avid blogge...
When talking IoT we often focus on the devices, the sensors, the hardware itself. The new smart appliances, the new smart or self-driving cars (which are amalgamations of many ‘things'). When we are looking at the world of IoT, we should take a step back, look at the big picture. What value are these devices providing. IoT is not about the devices, its about the data consumed and generated. The devices are tools, mechanisms, conduits. This paper discusses the considerations when dealing with the...
Business professionals no longer wonder if they'll migrate to the cloud; it's now a matter of when. The cloud environment has proved to be a major force in transitioning to an agile business model that enables quick decisions and fast implementation that solidify customer relationships. And when the cloud is combined with the power of cognitive computing, it drives innovation and transformation that achieves astounding competitive advantage.
With 10 simultaneous tracks, keynotes, general sessions and targeted breakout classes, @CloudEXPO and DXWorldEXPO are two of the most important technology events of the year. Since its launch over eight years ago, @CloudEXPO and DXWorldEXPO have presented a rock star faculty as well as showcased hundreds of sponsors and exhibitors! In this blog post, we provide 7 tips on how, as part of our world-class faculty, you can deliver one of the most popular sessions at our events. But before reading...
If a machine can invent, does this mean the end of the patent system as we know it? The patent system, both in the US and Europe, allows companies to protect their inventions and helps foster innovation. However, Artificial Intelligence (AI) could be set to disrupt the patent system as we know it. This talk will examine how AI may change the patent landscape in the years to come. Furthermore, ways in which companies can best protect their AI related inventions will be examined from both a US and...
Poor data quality and analytics drive down business value. In fact, Gartner estimated that the average financial impact of poor data quality on organizations is $9.7 million per year. But bad data is much more than a cost center. By eroding trust in information, analytics and the business decisions based on these, it is a serious impediment to digital transformation.
Digital Transformation: Preparing Cloud & IoT Security for the Age of Artificial Intelligence. As automation and artificial intelligence (AI) power solution development and delivery, many businesses need to build backend cloud capabilities. Well-poised organizations, marketing smart devices with AI and BlockChain capabilities prepare to refine compliance and regulatory capabilities in 2018. Volumes of health, financial, technical and privacy data, along with tightening compliance requirements by...
DXWorldEXPO LLC, the producer of the world's most influential technology conferences and trade shows has announced the 22nd International CloudEXPO | DXWorldEXPO "Early Bird Registration" is now open. Register for Full Conference "Gold Pass" ▸ Here (Expo Hall ▸ Here)
@DevOpsSummit at Cloud Expo, taking place November 12-13 in New York City, NY, is co-located with 22nd international CloudEXPO | first international DXWorldEXPO and will feature technical sessions from a rock star conference faculty and the leading industry players in the world. The widespread success of cloud computing is driving the DevOps revolution in enterprise IT. Now as never before, development teams must communicate and collaborate in a dynamic, 24/7/365 environment. There is no time t...