Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Packaging Utility Classes or Library JAR Files in a Portable J2EE Application

 
By Sean Brydon, Carla Mott  

Contents

Introduction

Many Java 2, Enterprise Edition (J2EE) applications use utility classes or libraries that provide some extra functionality not supported by the application code or the J2EE platform APIs. Often these libraries are placed in a Java Archive (JAR) file so that they can be used by multiple applications. These JAR file libraries can be created by the application provider or obtained from a third party. The J2EE platform supports the common use case of an application's using a library that is provided to the application code.

This article

  • Describes the mechanisms available in the J2EE platform for including libraries in a portable application
  • Provides several use case scenarios showing different types of application uses of library files and explains which mechanisms should be used for each scenario
  • Describes the tools that are available for helping the developer determine if the mechanism is used correctly
  • Provides recommendations for handling some situations where the specifications provide only loose guidance

Application portability is an attractive aspect of the J2EE platform. The Java Blueprints program and the Java Application Verification Kit (AVK) for the Enterprise were put in place to help developers write portable J2EE applications, and both are free and available for use.

The Java AVK for the Enterprise is designed to identify enterprise applications developed with J2EE technology that are intended to be portable across different implementations of the J2EE platform. It includes a set of tools intended to help developers test their applications for correct use of J2EE APIs and portability across J2EE compatible application servers. You can use the Java AVK for the Enterprise to test your application for portability problems.

The Java BluePrints program provides an extensive set of guidelines, design patterns, and sample applications that serve as models for developers writing portable applications. In particular, the Java Adventure Builder Reference application showcases how to design interoperable and portable web services on the J2EE 1.4 platform, and the Java Pet Store Sample Application shows how to use the capabilities of the J2EE platform to develop robust, scalable, portable, and maintainable enterprise applications. We recommend that anyone implementing a J2EE application study these applications.

Scope

This article focuses primarily on static dependencies in the application code and the mechanisms to handle those dependencies. Some applications have dynamic dependencies where these mechanisms do not need to be used.

An example of static dependency is an application that uses a logging library to display progress and error information to the user. This library is required for proper execution of the application, and therefore the application needs to specify a dependency using one of the mechanisms described in Mechanisms for Using Libraries in J2EE Applications.

As an example of a dynamic dependency, consider an application that uses a logging package. The application can abstract away which logging implementation it is using. When the application starts up, the logging package configures itself as follows (unless you explicitly configure a hard-coded choice):

  1. Is the log4j API present? If so, use that.
  2. Is J2SE 1.4 logging available? If so, use that.
  3. Fall back to a simple file logger implementation.

In this example, the dependency is resolved at runtime, and the log4j API is not required, so no dependency needs to be specified. Currently the static verification tools in the Java AVK for the Enterprise will report that the log4j API is not found in the application, even if the user chooses not to use it, although it should not treat this situation as a failure. The dynamic verification tools will automatically report on the packages used at runtime.

We use the term library to mean a collection of classes that is packaged using the Java Archive (JAR) utility. These classes may be developed externally as third-party code or as utilities within the organization. Typically, they provide useful general-purpose functionality to many different applications.

If you are already familiar with the mechanisms in the J2EE platform for portable packaging of library JAR files, feel free to skip to Scenarios, where the mechanisms are applied to various application packaging scenarios.

The following are some common situations in which you would want to use library code:

  • An application uses Struts and its APIs to provide a Model-View-Controller (MVC) framework. The application code then depends on these Struts classes as well as the standard APIs provided by the J2EE application server.
  • An application uses the JavaServer Pages Standard Tag Library (JSTL), so it has a jstl.jar file to provide some tags that are used by the application's JSP pages.
  • An application uses a logging utility, which may be either a third-party library JAR file or one developed internally. This logging utility is used by many different applications, so it is packaged in a separate JAR file so that it can be reused.

Once you have decided to use one of these libraries, you face the important design decision of how to package these extra libraries along with the application code. The decision you make can have major effects on the following:

  • The portability of your application
  • The size of your WAR and EAR files
  • The maintenance of the application
  • Version control as libraries and application servers are updated

Mechanisms for Using Libraries in J2EE Applications

For portable applications, there are several mechanisms in the J2EE platform to support using optional packages such as a library or utility JAR file. These mechanisms are reviewed briefly in this section. For each of these mechanisms, you need to determine where to place the library JAR file and how the referencing application files (which may be JAR, EAR, WAR, or RAR files) can indicate where the library JAR file can be found, either through an explicit reference or by placing it in a well-known location such as WEB-INF/lib or the Java 2 Runtime Environment lib/ext directory.

Overview of Mechanisms to Support Optional Packages

Some solutions for packaging library JAR files are specific to a particular application server: for example, placing a library JAR file in an application server's classpath so that applications can use the APIs in that JAR file. Some application servers have container-specific locations where you can place JAR files to be shared by applications and modules. But these mechanisms are not portable, unlike the mechanisms provided by the J2EE platform. This section describes the portable mechanisms for using optional packages such as library JAR files. Some of these mechanisms are specific to the J2EE platform, while some use the Java 2 Platform, Standard Edition (J2SE) extension architecture mechanisms for handling optional packages. For detailed descriptions of these mechanisms, see References at the end of this document.

Some application servers allow for mechanisms beyond these portable mechanisms. Often, placing a JAR file in a certain location will allow an application to deploy and run. But it is best to use the portable mechanisms described below.

Mechanism 1: The WEB-INF/lib Directory

If you place a library JAR file (for example, struts.jar) into the WEB-INF/lib directory of the WAR file, a web application or web module can use the Struts APIs. You can use this mechanism only with web modules (WAR files). With this mechanism, the library JAR file is included as part of the WAR file. The WEB-INF/lib directory is a well-known location for libraries and is supported by the J2EE platform. The library JAR file is available only to the web module, where it is is bundled inside the WEB-INF/lib directory, and cannot be shared by other modules or applications.

This mechanism cannot be used by EJB modules.

Mechanism 2: Bundled Optional Classes

Use the Class-Path attribute in the manifest file to reference one or more library JAR file(s) included in the EAR file. With this mechanism, the library JAR file is included as part of the referencing JAR file (EAR file); such a library is said to be bundled. Since EAR, WAR, and RAR files are also JAR files, they can use the JAR mechanisms to reference dependent JAR files. You include a manifest file, META-INF/MANIFEST.MF, in the referencing JAR file; in the manifest file, you use the Class-Path attribute to specify the library JAR, which is included in the application EAR file. More than one library can be listed in the Class-Path. Class-Path is one of the JAR file manifest attributes.

The J2EE 1.4 platform specification states that top-level JAR files, such as EAR files, should not contain Class-Path references, since they would reference files external to the top-level JAR file. With this mechanism, the library JAR file is bundled as part of the EAR or WAR file and is not external.

Here is an example for an EAR file containing a web module that uses Struts:

application.ear: 
  META-INF/application.xml 
  struts.jar
  webapp.war
     META-INF/MANIFEST.MF:
        Manifest-Version: 1.0
        Class-Path: struts.jar
     WEB-INF/web.xml

Mechanism 3: Installed Packages

Use the Extension-List attribute in the manifest file to reference one or more library JAR files that are not bundled in the EAR file but are installed in some well-known location, usually the lib/ext directory of the Java Runtime Environment (JRE). With this mechanism, the library JAR file is not included as part of the EAR file. Any library JAR file referenced is external to the EAR file and instead installed in the lib/ext directory, and this external location is referenced using the Extension-List attribute. Such libraries are often called installed libraries. In the application EAR file, the Extension-List attribute of the manifest file is used to express the dependency of the EAR file on a library JAR file. More than one library can be listed in the Extension-List. The applications will not deploy if the application server cannot find the library and resolve the dependency. Extension-List is one of the JAR file manifest attributes, indicating the optional packages that are needed.

When you use this installed packages mechanism, the library JAR file is available to all applications.

Here is an example (from the J2EE 1.4 Platform Specification) where the file util.jar is placed in the install directory lib\ext, and util.jar is used by the app1.ear application:

app1.ear:
META-INF/application.xml
ejb1.jar:
META-INF/MANIFEST.MF:
Extension-List: util
util-Extension-Name: com/example/util
util-Extension-Specification-Version: 1.4
META-INF/ejb-jar.xml

util.jar:
META-INF/MANIFEST.MF:
Extension-Name: com/example/util
Specification-Title: example.com’s util package
Specification-Version: 1.4
Specification-Vendor: example.com
Implementation-Version: build96

Brief Description of the J2SE 1.4 Extensions for Optional Packages

The J2EE 1.4 mechanisms for portably packaging library JAR files use the J2SE mechanisms for allowing applications to use libraries packaged as JAR files. This section briefly reviews some of these concepts. Note that optional packages is the new term for what used to be known as standard extensions or just extensions.

The J2SE platform allows optional packages in the following two ways (paraphrased from the Optional Packages Overview document):

  • Installed optional packages are JAR files located in the directory lib/ext (in the Java 2 Runtime Environment) or jre/lib/ext (in the Java 2 SDK). The JAR file should have a manifest file describing itself.
  • Download optional packages, also called bundled optional packages, are JAR files that are specified in the Class-Path attribute in the manifest of another JAR file. Classes in download optional packages may be used by classes in the referencing JAR file. Unlike the case of installed optional packages, the location of the JAR files that serve as bundled/download optional packages is irrelevant. A download optional package is an optional package because it is specified as the value of the Class-Path attribute in another JAR file's manifest, not because it has any particular location.

Checking that Your Application Is Packaged Correctly

The Java AVK for the Enterprise provides tools to scan an application archive for incompatibilities with the specification, correct use of APIs, and packaging rules. The Ant task ArchiveTest allows you to easily run the static archive tests against an application that has been packaged as an archive. This section describes how to use the Ant task to run the static tests and include a copy of the results. Although hundreds of tests can be run against an archive, this section discusses only the results for packaging libraries.

You must create a build.xml file with a target to call the Ant task. The task will run the tests and generate an HTML report that includes a summary of the results and links to details of each test result. The target should look something like this:

<target name="static-archive-test"
 description="static archive tests for application containing web components, reporting on all tests">
  <ArchiveTest appName="/export/home/apps/samples/bookstore/bookstorebundled.ear"
   reportingOpts="a" />
</target>

Below is the part of the report that shows the failure found by static verification because the archive was not packaged correctly. Here the classes samples.webapps.bookstore.util.Currency and samples.webapps.bookstore.util.Counter are both in a separate JAR file called myutil but are not referenced properly. The structure of the EAR file is as follows:

bookstorebundled.ear
myutil.jar
META-INF/application.xml
bookstore.war

Static verification reports the problem as follows.

  Description: For [ bookstoreInstalledpkg.war ] classes [ samples.webapps.bookstore.util.Currency ] referenced by [ samples.webapps.bookstore.BookDetailsServlet] are not found classes [ samples.webapps.bookstore.util.Counter ] referenced by [ samples.webapps.bookstore.listeners.ContextListener] are not found classes [ samples.webapps.bookstore.util.Counter ] referenced by [ samples.webapps.bookstore.filters.HitCounterFilter] are not found classes [ samples.webapps.bookstore.util.Currency ] referenced by [ samples.webapps.bookstore.ShowCartServlet] are not found classes [ samples.webapps.bookstore.util.Currency ] referenced by [ samples.webapps.bookstore.CashierServlet] are not found classes [ samples.webapps.bookstore.util.Currency samples.webapps.bookstore.util.Counter ] referenced by [ samples.webapps.bookstore.filters.OrderFilter] are not found classes [ samples.webapps.bookstore.util.Currency ] referenced by [ samples.webapps.bookstore.CatalogServlet] are not found
  Assertion: All classes in this Web Archive are loadable. Please refer to J2EE 1.4 Specification Section #8.1.2 for further information.
  TestName: tests.web.WebArchiveClassesLoadable

To correct this error using the bundled optional classes mechanism, the manifest file in bookstore.war needs to contain a reference to the classes in myutil.jar. Since the library file is included in the EAR file along with the referencing WAR file, you would use the Class-Path attribute in the MANIFEST.MF file in bookstore.war. The contents of the manifest file should look something like this:

META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Class-Path: myutil.jar
Created-By: 1.4.2_05-b04 (Sun Microsystems Inc.)

You can use a command like the following to update the MANIFEST.MF file in the existing bookstore.war file:

jar uvfm bookstore.war META-INF/MANIFEST.MF

Once this version of bookstore.war is included in the application EAR file, the EAR file will pass the static archive tests.

Scenarios

This section examines how the mechanisms for portably packaging an optional package provided as a library JAR file can be applied to some common scenarios for J2EE applications. These scenarios are based on the questions most commonly asked by users.

Scenario 1: The application is a stand-alone WAR file that uses one or more library files.

The application consists of a WAR file that is not packaged in an EAR file. In J2EE 1.4, a stand-alone module such as a WAR file is a valid J2EE application, and no EAR file wrapper is required.

In this case, you can use the WEB-INF/lib mechanism. Include the JAR files (struts.jar, for example) in the WEB-INF/lib directory of the WAR file; no other steps are required. The library JAR files are packaged as part of the WAR file.

The bundled optional packages mechanism would not work, because the WAR file in this case is a top-level application file (a stand-alone WAR or EAR file) and top-level application files must not have a Class-Path attribute. The attribute implies a file external to the top-level JAR file, not one that is bundled inside it.

The installed optional packages mechanism would work. For example, a library JAR file such as struts.jar could be placed in the extensions directory (lib/ext). The application would need to include a manifest file that uses the Extension-List attribute to indicate that it needs one or more optional packages.

Scenario 2: The application is an EAR file that contains more than one web module (WAR file), and you want these web modules to share the same library JAR file rather than bundle duplicate copies of the library.

For example, suppose you have an EAR file with two WAR files, both of which use Struts, and you want them to share the struts.jar file instead of having duplicate copies.

In this case, you can use either the bundled optional packages mechanism or the installed packages mechanism. You cannot use the WEB-INF/lib mechanism for sharing, since in this mechanism each web module has its own copy of the library JAR file.

For the bundled optional packages mechanism, each web module uses the manifest file and Class-Path attribute to point to the same location for the JAR file bundled within the EAR file. Only one copy of the library (for example, struts.jar), is included within the EAR file.

For the installed optional packages mechanism, each web module uses a manifest file and Extension-List attribute to point to the library installed in the extensions directory, which is not included in the EAR file. This way each module shares the same library JAR file.

Scenario 3: Multiple applications (different EAR files and/or different stand-alone WAR files) need to share the same library JAR file.

In this case, you want multiple applications to use the same library, and you do not want to include duplicate copies of the library JAR file in each application. For example, multiple applications may need to use the same version of the JSTL tag libraries. This case covers applications that are deployed as EAR files or as stand-alone WAR files.

In this case, you can use the installed optional packages mechanism. Put one copy of the library (for example, struts.jar) in the extensions directory. In each application that uses Struts, use the manifest file and Extension-List attribute to express this dependency.

Scenario 4: For developers of libraries intended to be used and packaged with applications.

In the previous scenarios, a developer is creating an application that uses libraries in a portable way. But what if you are a developer of a library, such as Struts or JSTL or utility classes, that must be portable among different J2EE 1.4 application servers? In particular, what if the library you are creating has dependencies on some third-party code? (For example, JSTL uses a specific version of xalan.jar.) What is the proper way to package these libraries so that they can be used by a portable J2EE application?

According to the J2EE 1.4 platform specification (Sections 8.2 and 8.4), if you want to create a JAR file to be used as an optional package, it should be packaged as a .jar file according to the Extension Mechanism Architecture, and the JAR file should have a manifest file that declares its dependencies, if any. In the example under Checking that Your Application Is Packaged Correctly, myutil.jar contains only the classes in the library, and there are no other dependencies;

myutil.jar
   META-INF/MANIFEST.MF
      Manifest-Version: 1.0
      Created-By: 1.4.2_03 (Sun Microsystems Inc.)
      Extension-Name: myutil
      Specification-Title: Utility
      Specification-Version: 1.4
      Implementation-Version: 1.4
      Implementation-Vendor-Id: com.sun
      Implementation-Vendor: Sun Microsystems, Inc 
      Specification-Vendor: Sun Microsystems
samples/webapps/bookstore/util/Counter.class
samples/webapps/bookstore/util/Currency.class

If your library does have dependencies on external code, use one of the three mechanisms described in the section Mechanisms for Using Libraries in J2EE Applications. The writer of the library is bound by the same rules and mechanisms as the user of the library code.

Issues and Recommendations

The J2EE 1.4 Platform Specification is vague about how to handle multiple versions of a library. The classloader can be different in the various application servers, so libraries can be handled inconsistently. The best way to guard against this problem is to have a policy for handling versions of libraries. Before discussing the policies that application developers should use, we need to go over the policies the server vendors use to load classes.

One such policy involves precedence order. What if an application EAR file uses more than one mechanism to reference a library in its packaging? Or what if multiple versions (or multiple copies of the same version) of a library JAR file are present? Which instance of the JAR file would be used at runtime by the application? The order of precedence in the J2EE and J2SE platforms may be different, and the J2EE platform specification applies to applications deployed on application servers. The J2EE platform specification recommends a precedence order for resolving the finding of an optional package. Generally an application server will look for a class in a library JAR file in the following order:

  1. The WEB-INF/lib mechanism
  2. The bundled optional package mechanism
  3. The installed optional package mechanism

If a class cannot be found, the application is not deployed.

Application servers often will not allow a library JAR file to replace a platform API, so, for example, an inclusion of your own servlet.jar file using one of these mechanisms will often be ignored. This is a safety precaution to avoid having mistakes alter the container behavior for standard platform APIs. It is important to be aware of this issue when writing applications that run on multiple servers.

Application developers should define and follow policies to help ensure that their applications work as expected on the different servers. Policies should assume that the application server follows the loading steps recommended in the platform specification, as described above. Then, if a version of the library exists in the deployment environment, applications should include the library in the archive as a bundled optional package so that the bundled library will be selected by the application server instead of the library that has been installed in the extension directory.

Summary

Writing portable J2EE applications can be easy when you know the mechanisms available. The J2SE and J2EE platforms define three mechanisms that applications can use to include libraries in a portable way. An application can:

  • Include the library in its WEB-INF/lib directory
  • Bundle the library as an optional package if it will be shared by components in the application
  • Install the library in the lib/ext directory, where multiple applications can use the same library

The Java AVK for the Enterprise includes tools that allow users to determine if the application is packaged correctly and is portable. In addition to presenting the mechanisms, this article presented several scenarios for including libraries in applications along with the appropriate mechanism for each scenario. Finally, the article discussed vague areas in the J2EE 1.4 Platform Specification that the application developer needs to be aware of and recommendations for dealing with these areas. For more information, see References.

References

The following resources are recommended for further information on this topic.