Introduction
The packaging mechanisms defined in Chapter 8 of the J2EE 1.3
specification provide a framework for pulling together all the pieces
of a J2EE application. However, application server vendors are free to
design a proprietary class loading hierarchy for obtaining the classes
and resources found in an application. A class loading hierarchy is
typically used to enable features such as hot redeployment and
application independence.
Understanding the class loading architectures of major application
server vendors helps J2EE developers design application packaging
structures that are both portable and efficient. After a brief primer
on the basics of class loading, the class loading hierarchies of three
major application servers (BEA WebLogic 6.1 SP2, IBM WebSphere 4.0, and
HP-AS 8 Maintenance Pack 3) are presented. The discussion is limited to
J2EE application modules (.ear), EJB modules (.jar) and web application
modules (.war). A familiarity with J2EE packaging mechanisms (.ear,
.war, and .jar) is assumed; please see the References section for introductory material.
After reading this article, J2EE developers will have a better
understanding of how classloading architectures can affect J2EE
packaging decisions. As an added bonus, classloading architecture
knowledge can also be enormously beneficial when debugging the common
ClassNotFoundException often generated by J2EE application servers.
Class Loading Basics
Typically class loaders are arranged in a parent/child hierarchy.
When a class loading request is presented to a class loader, it first
asks its parent class loader to fulfill the request. The parent, in
turn, asks its parent for the class until the request reaches the top
of the hierarchy. If the class loader at the top of the hierarchy
cannot fulfill the request, then the child class loader that called it
is responsible for loading the class. If the child is also unable to
load the class, the request continues back down the hierarchy until a
class loader fulfills it or a ClassNotFoundException is produced by the class loader at the bottom of the hierarchy.
Figure 1 illustrates a basic class loading hierarchy. Note that a class
loaded at a given level in the hierarchy may not reference any classes
loaded at a lower level in the hierarchy. Stated another way, a class
loader has no visibility into classes loaded by its descendants. In
figure 1, if class Foo is loaded by class loader B, and Foo depends on class Baz, then class Baz must be loadable by either class loader A or B. If Baz is only visible to class loader C or D, then a ClassNotFoundException will occur.
If the class Bar is visible to two sibling class loaders
(e.g., C and D in Figure 1) but not to their parent class loaders, and
if a request for Bar is sent to both sibling class loaders, then each class loader will load its own version of the class. Instances of Bar
based on class loader C will not be type compatible with instances
based on class loader D. This fundamental fact can lead to confusing
bugs especially when the class loader hierarchy is not well understood.
See the References section for pointers to specific discussions of class loading oddities (e.g., multiple singleton instances).
Section 8.1.1.2 of the J2EE 1.3 specification mandates explicit support
for the bundling of dependent .jar files via the Extension Mechanism
Architecture (see the References
section). Application servers must load dependent .jar files listed in
the Manifest Class-Path entry of a primary .jar file (typically an EJB
.jar). This requirement is not mandated for .ear or .war files. The
extension mechanism is a valuable technique that should not be
overlooked when packaging an application. Whenever possible, be sure to
investigate and understand exactly which class loader is used to load
dependent jar files.
Programmatically uncovering the class loader hierarchy is a simple
exercise, but the resulting output can help identify how classes are
being loaded. The code below will emit the class loading hierarchy from
the perspective of a given class.
ClassLoader classLoader = getClass().getClassLoader();
// Implies that we're at the top of the hierarchy when null.
while (classLoader != null) {
System.out.println("Class/Method Name Here: parent classLoader == " +
classLoader.toString());
// Note that getParent() may require opening up the
// security settings in the JVM.
classLoader = classLoader.getParent();
}
System.out.println("Class/Method Name Here: parent classLoader == null");
J2EE application servers typically make use of at least two levels
of class loaders. Without a firm grasp of how classes are loaded in a
given application server, difficult bugs can arise and confusing
runtime errors may occur. With this basic background in place, let's
look at how three major application servers choose to implement their
class loading hierarchy.
WebLogic 6.1 with Service Pack 2
When deploying a J2EE application in .ear form with WebLogic 6.1
SP2, two or more new class loaders are created below the standard
system class loaders for each J2EE application. Figure 2 shows the
resulting class loader architecture. One EJB class loader is created as
a child of the system class loaders. It is responsible for loading all
EJB .jar classes for all EJB .jar files in the .ear. One web
application class loader is created for each .war in the .ear, and each
of the web application class loaders is a child of the EJB class
loader. (Note: The WebLogic 6.1 documentation indicates that only one
class loader is created for all .wars in the .ear; this is not
accurate.) The web application class loaders are responsible for
loading the classes and jars in the WEB-INF/classes and WEB-INF/lib
directories in the corresponding .war.
As of Service Pack 2 of WebLogic 6.1 (WebLogic issue 056911 -- see the References
section below), .jar files listed in a Manifest Class-Path entry are
loaded by the application's EJB class loader. This applies to Manifest
Class-Path entries found in EJB .jar files and web application .war
files within the .ear. Note that the Manifest Class-Path approach is
only supported when deploying components within an .ear.
One advantage of this class loading architecture is the automatic
availability of all EJB classes from the web application class loaders.
However, classes found in the WEB-INF/classes or WEB-INF/lib
directories of a .war are not available to any EJB classes.
If .war or .jar files are deployed into WebLogic 6.1 SP2
independently (i.e., not within an .ear), then they are considered
separate applications and will effectively have sibling class loaders.
This implies that an independent .war no longer has default access to
EJB classes.
WebSphere 4.0
The WebSphere 4.0 class loading architecture is relatively complex
compared to WebLogic 6.1. The WebSphere documentation explains the
necessary details in terms of classpaths rather than class loaders;
however, the References section below contains a link to a classloader-based description of the same material.
WebSphere 4.0 defines the concept of an "isolation mode". A particular
isolation mode alters the view that class loaders have into other class
loaders. Four isolation modes exist in WebSphere 4.0:
- Module: One class loader is created for each module in an
.ear. A module is defined as a web app .war, an EJB .jar, or a .jar
referenced from the Manifest Class-Path of a .war or .jar. The logical
class loader hierarchy is formed by dependencies specified in the
Manifest Class-Path attributes of the modules. For an application with
two .war files, two EJB .jar files, and two common .jar utility
libraries listed in Manifest Class-Path attributes, six class loaders
would be created.
- Application: This mode allows all class loaders
associated with a given J2EE application .ear to have access to all
other class loaders within the application. Logically it is like having
one class loader for the entire application.
- Compatibility: This mode is intended to provide
backward compatibility with the class loading semantics of WebSphere
3.5.x and 3.0.2.x. This mode is similar to WebLogic 6.1 in that all web
application .war modules have visibility into all EJB .jar modules, and
all EJB .jar modules can see all other EJB .jar modules.
- Server: This mode is logically equivalent to having one class loader for all applications in the entire app server.
Figure 3 shows the typical class loading architecture in Module mode.
Notice that all the module class loaders are siblings in the hierarchy.
However, in WebSphere 4.0 this does not imply that they are unaware of
one another. A class loader grouping mechanism is used to ensure the
appropriate class loading hierarchy semantics. For example, if EJB1
lists common.jar in its Manifest Class-Path attribute, common.jar will
be loaded in a separate sibling class loader. However, EJB1 logically
acts like it is a child of the common.jar class loader. The grouping
mechanism is used to obtain the appropriate semantics for all four
isolation modes; however, the details of exactly how this mechanism
works is not exposed in the WebSphere documentation.
The WebSphere 4.0 documentation highly recommends using the Module
isolation mode in conjunction with Manifest Class-Path entries. This
combination is advertised as the most portable approach.
It is interesting to note that both WebSphere 4.0 and WebLogic 6.1
SP2 support Manifest Class-Path entries in .jar and .war files;
however, the same .ear file may run fine in one and not the other. This
is because WebLogic 6.1 SP2 loads a .jar file specified in any Manifest
Class-Path (.jar or .war) into the EJB class loader for the
application. As long as one .jar or .war in the application specifies
the dependent .jar in its Manifest Class-Path, then all other .jar and
.war files can successfully reference it without explicitly listing it
in their Manifest Class-Paths. In contrast, WebSphere 4.0 requires that
such Manifest Class-Path references be explicitly listed in every J2EE
module (.war or .jar) that needs them.
Section 8.1.1.2 of the J2EE 1.3 specification states that Manifest
Class-Path support is only required of .jar files. Supporting the
technique in .war files can be seen as a non-standard extension.
Indeed, the J2EE 1.3.1 reference implementation does not pay attention
to Manifest Class-Path entries in .war files. In many cases, however,
support for Manifest Class-Path entries in .war files can be extremely
helpful in designing a clean .ear package structure. However, the
non-standard (and hence non-portable) nature of this technique must be
factored into the decision to use it.
HP-AS 8 Maintenance Pack 3
The class loading architecture of HP-AS 8 most resembles WebSphere
4.0, although the logical semantics are closer to WebLogic 6.1 SP2.
Figure 4 shows the HP-AS 8 approach.
The library class loader is the parent of all application class
loaders. The classpath it searches is specified by entries in the
proprietary application-classloader-service-config.xml file typically
found in <HP-AS-HOME>/config/hpas. An entry in this file looks
like this:
<library name="common" url="file:///d:/lib/common.jar"/>
One application class loader is created as a child of the library
class loader for each J2EE application deployed in HP-AS 8. One EJB
class loader is created as a child of the application class loader. The
EJB class loader is responsible for loading all classes within all
EJB .jar archives in the J2EE application. One web application class
loader is created as a child of the application class loader for each
web application archive in the J2EE application. The fact that each
.war gets its own class loader allows web application independence to
be achieved within an application. When J2EE components are deployed
individually (i.e., not within an .ear), they are automatically
associated with a default application and its application classloader.
The application class loader is unique in that it does not load
classes. Instead it delegates that responsibility to its children in
the following order: connector classloader (not covered here), EJB
classloader, and web application classloader(s). Experiments show that
classes contained in an EJB .jar are visible to other EJB .jars and all
web application class loaders. However, classes contained in a web
application archive are not visible to the EJB class loader or to other
web application archive class loaders. These logical class loading
semantics are similar to WebLogic 6.1 SP2.
As of maintenance pack 3, the HP-AS 8 application server does not
support Manifest Class-Path extension mechanism for structuring
dependencies. However, this feature is slated for an upcoming
maintenance pack. For common utility .jar files normally placed in the
Manifest Class-Path on other application servers, several options are
available. Placing them in an EJB jar will provide appropriate
visibility as will loading them into the library class loader via the application-classloader-service-config.xml file.
Conclusion
Understanding the class loading architectures of a variety of
application servers demystifies the facts surrounding how the
components of a particular J2EE application are loaded. Armed with this
information, J2EE developers can design portable J2EE packaging
structures or at least understand the tradeoffs when using proprietary
techniques. This is particularly important for J2EE component and
framework providers who must take on the extra task of shipping their
components in the most portable manner.