Java class loading problems can be vexing, especially in advanced environments established to address complex requests. Java 2 Platform Enterprise Edition (J2EE) and WebSphere© Application Server use sophisticated techniques for structuring and loading classes. Like many developers, you're probably wondering how to design your J2EE projects to make proper use of the various classloaders.
This article describes the J2EE specification and how you can use it to build projects inside WebSphere Studio Application Developer (Application Developer). In addition to creating basic J2EE applications, this article explores some best practices and advanced features of Application Developer. It also gives you a solid foundation for tackling the dreaded ClassNotFoundException.
The J2EE specification describes three types of modules: Web modules, Enterprise JavaBean (EJB) modules, and application client modules. When deploying to a J2EE application server, all of these modules are normally compressed into a single J2EE application Enterprise Architecture (EAR) file. The following sections describe each type of module and how it is built using Application Developer.
A Web module contains HTML, images, JSPs, JavaTM classes and
servlets, and all other resources required to create a Web application.
Like the other modules, Web modules contain a deployment descriptor. In
Web modules, the deployment descriptor,
web.xml, has servlet
initialization and mapping information, and well as other settings for
running the Web module within an application server.
A Web module has two special folders for Java code:
WEB-INF/lib. The classes
folder may contain "loose" Java classes (classes that are not inside a JAR
file), and it can be used for servlet or utility classes within the scope
of the Web application. Often a special class loader is used for this
folder, so that if changes are made to the classes, they are automatically
reloaded by the application server. The lib folder may contain JAR files
(not ZIP files!) that the Web application also uses. You should place
third-party JAR files and other utility JAR files in this folder. However,
if other Web or EJB modules use the JAR files, move them into the
Enterprise Application project instead, as explained in the Enterprise applications section below.
In Application Developer, Web modules are represented by Web projects,
which contain two folders: source and webApplication. The webApplication
folder contains the complete J2EE Web module in its expanded form. The
source folder is used for
.java files, as they often are not
part of the deployed Web module. As you create Java resources in this
folder, they are automatically compiled and placed in the
webApplication/WEB-INF/classes folder. This keeps the Web
project in sync at all times, and ready for testing or exporting.
If you have imported your Web module from a Web Application Archive (WAR)
file, you may notice that the lib folder contains a
projectname_classes.jar file. This file contains the original
contents of the imported WAR file. If the WAR file included source code,
delete this file, as the classes will redundantly appear in the classes
EJB modules contain EJBs, their server-specific deployment code, a deployment descriptor, and optional helper classes. They contain the business logic of your application, and are typically called by Web, Application Client, or other EJB modules.
In Application Developer, an EJB module is represented by an EJB project.
These projects also have two folders,
ejbModule. The source of your EJB module is kept in the
ejbModule folder. As you make changes and generate deployment
code, the Java classes from this folder are compiled into the
bin folder. Any remaining resources (for instance, the
deployment descriptor) are also copied into the
Similar to the Web project's
webApplication folder, the
bin folder always contains the complete deployed EJB module.
As with the Web project, you should not manually modify the
bin folder in any way as your changes may be lost. Make all
changes within the
ejbModule folder, and these changes will
automatically be compiled or copied into the
If you import an EJB from an EJB JAR file, you may notice an
Xxx_importedClasses.jar file sitting in the root of your
project. This file contains the original contents of the imported EJB JAR
file. If the JAR included source code, delete this file, as the classes
will redundantly appear in the bin folder.
Application client modules
An application client module is used to contain a full-function client Java application (non Web-based) that connects to and uses the J2EE resources defined in your server. By placing the client code in an application client module instead of a simple JAR file, the application client benefits from the server's resources (it does not need to re-specify the classpath to J2EE and server JAR files) as well as easier JNDI lookup (the server fills in the initial context and other parameters).
In Application Developer, application client modules are represented by application client projects. For the most part, you can work as if you are creating a standalone Java application in a Java project.
Enterprise (J2EE) applications
An enterprise application is a grouping of one or more Web, EJB, or application client modules. As a superset of these other modules, it can contain a complete application that may be a combination of multiple modules. Besides being an efficient grouping mechanism, enterprise applications make it much easier to deploy and maintain code at the level of a complete application instead of as individual pieces. Enterprise applications can also override settings within the contained modules' deployment descriptors to combine or deploy them in a more useful way.
An enterprise application may contain JAR files that the contained modules will use. This allows sharing of code at the application level, and is the best place to put utility JAR files that are used by multiple Web or EJB modules. By placing these JAR files in the enterprise application instead of on the global classpath, they are also within the J2EE specification, and do not require special publishing and setup when moving to a new server.
In Application Developer, enterprise applications are represented by enterprise application projects. Since no source code is built directly into an enterprise application, these projects do not have subfolders.
WebSphere Application Server uses several classloaders to enable the J2EE specification. Besides the regular classloader that uses the CLASSPATH environment variable to locate and load classes, there are many other classloaders at work.
Figure 1 shows a simplified diagram of the classloaders at work inside WebSphere. Each oval represents a classloader, and the text in brackets describes where that classloader searches for classes:
Figure 1. WebSphere classloaders
At the top of Figure 1, the regular Java system classloader uses the
CLASSPATH environment variable to load classes. The second classloader is
WebSphere-specific and uses a
variable to load classes. In Application Developer, this environment
variable is set by using the Server Instance Editor in the Server
Perspective. Besides loading any user code, this classloader also loads
all of the WebSphere and J2EE classes that are required at run time. The
third classloader, AEX, may be used for application extensions. It loads
JARs from the
<WAS>/lib/app directory. Finally, the
modules running in the server are loaded by one or more module
classloaders. They follow the J2EE class loading rules discussed earlier
to load the classes and JAR files from your application.
The most important concept in Figure 1 above is that each classloader is defined as a child of the classloader above it. A classloader can have delegation turned on or off. The first two classloaders (Java and WebSphere) have it turned on and the last two (AEX and module) have it turned off.
When delegation is turned on, the classloader first delegates the request to its parent classloader. If none of the parent classloaders can find the class, the original classloader attempts to load the class.
When delegation is turned off, the classloader first attempts to load the class itself. If it can't load the class, it asks its parent to load the class.
In either case, requests can only go up the tree; they cannot go down. If the WebSphere classloader is requested to find a class in a J2EE module, it cannot go down to the module classloader to find that class, and a ClassNotFoundException will occur. After a class is loaded by a classloader, any new classes that it tries to load will reuse the same classloader, or go up the chain until the class is found.
Given the above delegation properties, the effective search order becomes:
1) Module classloader
2) AEX -- Application Extensions classloader
3) Java classloader
4) WebSphere ws.ext.dirs classloader
There are two cases where you may run into problems:
- WebSphere, J2EE, and any global classes cannot "see" the classes that
are contained within your applications. If you add a JAR file to the
global classpath or
ws.ext.dirsproperties, it cannot depend on classes within your modules.
- If you need to globally add database drivers or utility JARs to the
classpath, you must add them to the
ws.ext.dirsproperty, not to the global classpath. If you add them to the global classpath by mistake, they won't be able to load (for example) the connection pooling classes in the J2EE JAR files. Once again, this would appear as a ClassNotFoundException on the database driver, but it is a failure for the database driver classes to see down to the J2EE classes.
A common portable technique for finding resources (property files, images, and so on) is to use the classloader's findResourceXX methods. Based on the previous discussion, you can see why it is important to use the right classloader for this job. For example, if you use the WebSphere classloader, you won't be able to find any resources in your modules.
WebSphere classloader isolation modes
As mentioned earlier, WebSphere uses several classloaders to load the application code from the modules deployed to the server. The number and function of these classloaders depends on the classloader isolation mode (otherwise known as module visibility mode) that has been specified in the WebSphere server configuration. There are four settings you can choose:
- Server -- uses the same classloader for the entire server.
- Application -- uses a separate classloader for each Enterprise application.
- Module -- uses a separate classloader for each module.
- Compatibility -- uses the same classloader scheme as WebSphere Application Server 3.0.x and 3.5.x. This allows code to have visibility across enterprise application boundaries. This mode is not recommended unless you are migrating code from older servers.
When you are building applications that span multiple modules within an enterprise application, ensure that the classes within one module can see the classes that it uses from another module. This is also true for a module that wants to use a JAR file out of the enterprise application. This must be done for compile time as well as for run time.
At compile time, updating the classpath is simple. All you have to do is edit your project's properties and change the Java build path to include the other project or JAR file, but don't touch that dial yet! This article introduces a much easier way to update this at compile time and run time in a single step. In general, don't change the Java build path of a J2EE project by hand. The steps in this section will automatically update and maintain the build path and keep it in sync with the run time.
At run time, your applications must follow the J2EE specification when referring to other modules. As discussed in the WebSphere classloaders section, do not simply place a project on one of the global classpaths, as this could have disastrous effects, both on classloading and on making your code visible to all other applications.
You also should not rely on the WebSphere classloader isolation modes. If your server is running in (for example) application mode, you may not need to make any changes to allow your application to work at run time. Do not rely on this! You may later need to deploy your application to a server running in module mode, and you will find that your application no longer works. Follow the steps below to ensure that your application is correct, regardless of the isolation mode you are using.
The solution is to make use of the fact that J2EE modules are JAR files.
All JAR files may have a META-INF folder that contains a
MANIFEST.MF file, and the manifest file may contain a
classpath that refers to other JARs. By adding one of the other modules as
an entry in the manifest's classpath, the current module can make use of
its contained classes. You can also add JAR files from within the
enterprise application to the manifest. The only restriction to this
otherwise simple solution is that, since Web modules are not structured
with classes at their root, other modules can never depend on classes from
within Web modules. In other words, no modules may use the manifest to
depend on classes from within a Web module.
Application Developer provides an easy way to update the manifest file. Right-click on a J2EE project from within the Navigator or J2EE view, and select Edit Module Dependencies. This brings up a dialog (see Figure 2 below) that shows a checkbox list containing all other modules and JAR files that this project can depend on. By selecting the modules or JAR files in the list, both the manifest and build path are updated at the same time. This ensures that the manifest and build path are always in sync, and provides an easier way to change the dependencies of your project.
Figure 2. Edit Module Dependencies dialog
Where should I put my JAR files?
If your JAR file is only used in a single Web application, always put the
JAR file in the Web project's
folder. JAR files in this folder are automatically added to the Java build
path, and will not require any further setup when moving to a different
If the JAR file is used by multiple modules within the same application, put the JAR file in the enterprise application. You will need to use the Edit Module Dependencies feature to set up the manifest files and the Java build classpaths.
You can also put the JAR on one of the global classpath, but we don't recommend this. It complicates your deployment and leaves your application vulnerable to incompatibility problems if those JARs are later upgraded. For example, suppose that you depend on some third-party logging classes, and since these logging classes are used by almost all applications, you decide to deploy them on a global classpath. You test your application with version 1 of the logging classes. Six months later another application is deployed and it requires version 2 of the logging classes, so the logging JAR is updated. Your application is now running in an environment in which it was never tested.
If you still want to put the JAR file on the global classpath, you must
decide whether it should go on the classpath or
This decision is simple. If the JAR file needs to access any J2EE or
WebSphere classes, or any other JARs that have been added to
ws.ext.dirs, it must also be placed on the
ws.ext.dirs property. Otherwise, you are free to use either
If you have dependencies on the JAR file, update the Java build path of
each project that uses the JAR file. Adding it to the global classpath or
ws.ext.dirs properties also means that you will have to
publish the JAR file separately from your application, and you will have
to set up the server classpath again when you move to a different
Classes or JARs?
Another decision you need to make is whether to use loose classes or JARs.
The big advantage of using loose classes is that they are easier to debug
and deploy. With a loose class, you make your change, press Ctrl-S, and if
it is on a reloadable classpath (like
WEB-INF/classes) it is
available immediately. If it is not on a reloadable classpath, and if you
are using WebSphere Application Server, you only have to restart the
project and then the changes are available. To do this, right-click on the
project from within the Navigator, and select Restart
Project from the pop-up menu.
If you are using JAR files, you are faced with the additional build step of gathering all of the classes and rebuilding the JAR file. Your changes aren't available quite as quickly. The advantage of using JAR files is that deployment is a little cleaner, and closer to what your production environment would expect.
If you are in the early stages of a project where your helper classes are
changing frequently, you will find it more convenient to use loose
classes. If the classes are only used by a single Web module, place the
classes in the
WEB-INF/classes folder. Always use packages
for your classes, as the Java spec is imprecise when it comes to dealing
with classes in the default package.
If the classes are used by your EJBs, then make one of your EJB projects hold the helper classes (place them under the ejbModule folder), and have the other EJB modules that need those helper classes depend on that EJB project. Collect all of your helper classes in a single EJB project, because it is very important not to have circular dependencies between your projects. Good project structure dictates that dependencies must always be in the form of a tree. Once the helper classes mature and aren't changing as much, switch to a JAR approach.
What if you have utility classes that are used both by enterprise applications and normal Java applications? The simplest approach is to use JARs for those classes.
Development time versus run-time classpaths
At compile time, a Java compiler needs to know about every class or JAR file that your code refers to, so that it can safely compile and type-check against these classes. The Java compiler uses the project level property Java Build Path as its one and only source for this information. Other operations, like Edit Module Dependencies, manipulate the build path, but the build path is the authoritative source.
At run time, the application server uses a completely different mechanism to find and load classes. The run time doesn't know anything about the Java build path, and therefore your application may compile correctly, but still have ClassNotFoundExceptions at run time.
Now that you understand how the various classloaders work together, you should be in a much better position to understand how to structure your projects and avoid the dreaded ClassNotFoundException.
Questions from users
Question 1: When you say "Follow the steps below to ensure that your application is correct, regardless of the isolation mode you are using," how can you guarantee that two enterprise applications that use different versions of the same JAR will work together in the same VM if the WebSphere classloader isolation mode is set to "Server"? Won't there be conflicts as each enterprise app tries to load its own version of the same JAR using a single classloader (single VM)?
Response from author: Excellent point. When you switch to "Server" classloading mode, you are basically giving up all rights to run two different versions of the same JAR or class. Of all the possible modes, this one puts restrictions on deploying multiple applications together. I guess that we missed saying it in the article, but I strongly suggest avoiding "Server" classloading unless there is something in your application that demands it, and even then I would try to change the application first. This is the one case where even a well-coded application could have problems running on the server.
Question 2: Where do you mention the order of loading -- Web, EJB, and application client modules? You say "They follow the J2EE classloading rules discussed earlier to load the classes and JAR files from your application." Where is the "discussed earlier" section? I couldn#x2019t find it anywhere.
Response from author: Sorry if this the article is not helping to explain J2EE and WebSphere Application Server classloading. I hope I can clear up some issues by answering your questions. J2EE does not define any application loading order, and you should not build your applications to depend on one. If you do need to depend on the ordering, contact WebSphere Application Server Support to see if they can help you define the ordering. The section "discussed earlier" is the discussion of the various J2EE modules and their contents. The classloading rules define the locations within each module type where it is valid to put class files and JARs. J2EE defines these locations, and putting classes or JARs outside of them will prevent the objects from getting loaded at run time. When developing with WebSphere Studio, these locations become folders within the expanded J2EE module structure of the project, so it's important to know about these locations.
Question 3: I'd like to know more about what is specifically in the J2EE spec vs. what is an extension provided by WebSphere.
Response from author: Check the full J2EE documentation and specifications, which are based on the standard J2SE specification. They are a bit much to read -- there are a number of books and online tutorials that summarize them. Anything that is not included or implied by these two specs is WebSphere specific. When you are using WebSphere Studio for development, the majority of the WebSphere-specific features (such as EAR reloading settings in the EAR deployment descriptor editor) have been specifically named or grouped to make it clear that they are extensions to the spec. Other features, such as layout of the projects in the IDE and specific classloading modes, are not laid out by the specs, but evolve out of writing the appropriate development tool or runtime to manage the artifacts in the specs. If you have questions about specific features or settings, try posting to one of the WebSphere newsgroups or contacting WebSphere Support.
Question 4: Is a similar article available for WebSphere Studio V5.0? I have created three Web projects with one having common class files and the other two using those common class files. I have set up proper project references in the Java build path and the projects build perfectly. But when I deploy the projects, I get a "class not found" error for the classes in the common Java project.
Response from author: There is no similar article for WebSphere Studio V5.0. Adding the project to the Java build path does exactly that -- it adds it to the compile time build path, and does not make any changes that would affect the running of the project. In this case, the utility classes are not being found because they are not visible as utility JARs within the Web or EAR projects. WebSphere Studio V5.0 has a new option to set up Java utility projects. It's in the EAR and Web Deployment Descriptor Editors, and it lets a Java project build and run as if it were a utility JAR within the EAR or Web project. On export, the utility Java project is packaged up as a JAR file according to spec. For additional information about this feature, see the WebSphere Studio V5.0 online help.
Question 5: If I need to associate property files with my EAR file, like for database connectivity, this path of the file could be given in the server classpath and made available. Is there any better way of doing this, like making it part of the EAR file?
Response from author: Yes, property files can be made part of the EAR file. This is equivalent to putting utility classes or a utility JAR file in the EAR file. Here are the basic steps:
- Add the property files to the EAR file as a JAR file or within a directory.
- Update the manifest file of the module that needs access to the property file to contain the run-time relative path to the folder or JAR file within the EAR file. Doing this will add the property file to the classpath of that module.
Class.getResourceAsStream()from the module to load the properties file from its classpath.
Dig deeper into WebSphere on developerWorks
Get samples, articles, product docs, and community resources to help build, deploy, and manage your cloud apps.
Keep up with the best and latest technical info to help you tackle your development challenges.
Software development in the cloud. Register today to create a project.
Evaluate IBM software and solutions, and transform challenges into opportunities.