A Solution

So what is the right solution to this dilemma? One of the possible solutions is to eliminate the need for multiple JARs in your J2EE application by converging all of your EJBs and their utility classes into a single, unified package. The EJB 2.0 public final draft 2 (PFD2) specification is driving some projects to do this. This new version of the specification mandates that entity EJBs participating in a relationship do so using local interfaces and requires both of the EJBs in the relationship to be packaged into the same JAR file. PFD1 allowed EJBs in different JAR files to participate in relationships, further promoting greater modularity of the system, but ultimately limited the persistence optimizations that containers could do for CMP entity beans in a relationship. Now that PFD2 eliminates this capability, many vendors are providing tools that perform EJB JAR convergence. These tools will take as input two valid EJB JAR files and merge their contents and deployment descriptors into a single, unified package. You could potentially use one of these convergence tools to re-package your existing JAR applications.

Keep in mind that even if you converge all of your EJBs into a single JAR application, you will have eliminated copies of your utility library among the EJBs, but a copy will still exist in your WEB-INF\lib library. Additionally, the need for modularity of EJB applications still exists, since many companies desire to re-deploy EJBs on an individual basis. Since every EJB in a JAR will be re-deployed when that JAR file is re-deployed, an unnecessary amount of deployment processing could occur if your only desire is to re-deploy a single EJB.

A Better Solution

With the release of JDK 1.3, Sun Microsystems redefined the "extension mechanism," which is the functionality necessary to support optional packages. The extension mechanism is designed to support two things:

JAR files can declare their dependency upon other JAR files allowing an
application to consist of multiple modules.

Classloaders are modified to search optional packages and application paths for classes.

Additionally, the J2EE 1.3 specification's section 8.1.2 mandates that compliant application servers must support the extension mechanism as defined for JAR files. This requires that any deployment tool that references a JAR file be capable of loading any optional libraries defined through the extension mechanism. This also implies that if an application server or deployment tool supports runtime undeployment and re-deployment of EJB applications that use libraries via the extension mechanism, then that tool or application server must also support undeployment and re-deployment of any dependent
libraries!

Support for the extension mechanism does not exist for EAR, WAR, or RAR
applications as defined in the J2EE specification, since these applications are not directly loaded by a ClassLoader instance. Web applications still have to have libraries packaged in the WEB-INF\lib directory and resource-adapter applications can have libraries bundled directly in the RAR file. Enterprise applications will need to package any libraries within the Web, EJB, or resource-adapter application that requires them. So how does the extension mechanism work with EJB applications? A JAR file can reference a dependent JAR file by adding a Class-Path manifest attribute. The Class-Path manifest attribute lists the relative URLs to search for utility libraries. Multiple URLs can be specified in a single Class-Path entry and a single JAR file can contain multiple Class-Path entries. For example, a JAR file might have:

Class-Path: log4j.jar xmlx.jar foo/bar/util.jar

NOTE: If you use the extension mechanism in a J2SE application, the Class-Path manifest entry can reference directories, too. But for J2EE applications that are wholly contained within JAR files, the Class-Path manifest entry can only reference other JAR files.

The extension mechanism is a nice capability, especially since it is designed to handle circular redundancies by creating a unified class path containing all dependencies in first parsed-based ordering. For example, if the first EJB application parsed is EJB1.jar and it references:

Class-Path: jaxp.jar EJB2.jar xmlx.jar

A classloader will then parse EJB2.jar that references:

Class-Path: jaxp.jar EJB1.jar

The resulting "application" class path that a classloader would ultimately use would be:

Class-Path: jaxp.jar EJB2.jar xmlx.jar EJB1.jar

Conclusion

The manifest class path will definitely spur better modularity of J2EE packages in the future. Using this model, developers can employ a simple scheme for determining which EJBs should be packaged into a single JAR file and which should be packaged in separate JAR files:

Identify an entity EJB that participates in a CMR relationship. Identify all entity EJBs that are reachable via CMR relationships from the source entity EJB. This graph of entity EJBs must be packaged into a single JAR application. Repeat this process for each unique graph of entity EJB relationships

Package all remaining EJBs into separate JAR files.

Analyze your business and technical requirements and "converge" multiple JAR files if it makes sense.

Each EJB JAR file should list its dependencies using the manifest class path attribute, as described above. An intelligent classloader will resolve any circular or repeating dependencies.

The only limitation that developers are now faced with is locating an application server that fully supports this packaging capability. Developers will need to look for application servers that run in a 1.3 JDK and fully support section 8.1.2 of J2EE 1.3. Without this support, developers will need to fall back to one of the other, less-desirable solutions posted in this article. Happy packaging!