Need help understanding how to use the J2EE-specified Web modules, EJB modules, and applicationclient modules? This article explains the sophisticated techniques used by J2EE and WebSphere Application Server for structuring and loading classes. Learn how to make the most of the J2EE spec when building projects with WebSphere Studio Application Developer, and how to avoid the ClassNotFoundException.

Tim deBoer (deboer@ca.ibm.com), WebSphere Studio Development Team, IBM Toronto Lab

Photo: Tim deBoerTim deBoer is a software developer on the WebSphere Studio Application Developer, Server Tools team at the IBM Toronto Lab. With his teammates, he is currently responsible for the WebSphere and Tomcat test environments and the EJB test client. You can contact Tim at deboer@ca.ibm.com .



Gary Karasiuk (karasiuk@ca.ibm.com), WebSphere Studio Performance Analyst, IBM Toronto Lab

Photo of Gary KarasiukGary Karasiuk is a performance analyst for WebSphere Studio Application Developer. You can contact Gary at karasiuk@ca.ibm.com .



21 August 2002

Introduction

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.


J2EE modules

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.


Web modules

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/classes and 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 folder.


EJB modules

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, bin and 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 bin folder. 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 bin folder.

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 classloaders

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
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 ws.ext.dirs environment 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.dirs properties, 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.dirs property, 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.

Module interdependencies

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
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 webApplication/WEB-INF/lib 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 server.

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 ws.ext.dirs. 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 property.

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 server.


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.


Conclusion

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:

  1. Add the property files to the EAR file as a JAR file or within a directory.
  2. 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.
  3. Use Class.getResourceAsStream() from the module to load the properties file from its classpath.

Top of page

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=14236
ArticleTitle=J2EE Class Loading Demystified
publish-date=08212002