IBM WebSphere AppServer (WAS) is a true enterprise-class server which is used to service a wide variety of applications. WAS is a robust, scalable application server which provides a plethora of options; however, some of the options can be downright confusing. One item which can be particularly confusing for users new to the way that WAS operates is the concept of Class Loader policies.
Out of the box, WAS provides three options for class loading:
1) Bootstrap Class Loader.
2) Extensions Class Loader.
3) Application Class Loader.
WAS also provides the option to create a custom class loader if the options provided out of the box don’t match the requirements of the application. In addition to custom class loaders, WAS also provides isolated class loaders for Shared libraries.
So what exactly is a “class loader”?
A class loader is a part of the Java virtual machine (JVM) that loads classes into memory; a class loader is responsible for finding and loading class files at run time.
When you start a JVM, you use three class loaders:
- The bootstrap class loader is responsible for loading only the core Java libraries in the Java_home/jre/lib directory. This class loader, which is part of the core JVM, is written in native code.
- The extensions class loader is responsible for loading the code in the extensions directories (Java_home/jre/lib/ext or any other directory specified by the java.ext.dirs system property). This class loader is implemented by the sun.misc.Launcher$ExtClassLoader class.
- The application class loader is responsible for loading code that is found on java.class.path, which ultimately maps to the system CLASSPATH variable. This class loader is implemented by the sun.misc.Launcher$AppClassLoader class.
The parent-delegation model is a key concept to understand when dealing with class loaders. It states that a class loader delegates class loading to its parent before trying to load the class itself. The parent class loader can be either another custom class loader or the bootstrap class loader. But what is very important is that a class loader can only delegate requests to its parent class loader, never to its child class loaders (it can go up the hierarchy but never down).
The extensions class loader is the parent for the application class loader. The bootstrap class loader is the parent for the extensions class loader. If the application class loader needs to load a class, it first delegates to the extensions class loader, which, in turn, delegates to the bootstrap class loader. To illustrate an example of class loading, I am including a class loader hierarchy diagram from IBM’s WAS Redbook here.
The IBM WAS Redbook can be found at the following URL: http://www.redbooks.ibm.com/redpapers/pdfs/redp4581.pdf
If the parent class loader cannot load the class, the child class loader tries to find the class in its own repository. In this manner, a class loader is only responsible for loading classes that its ancestors cannot load. However, this may not always be what you need for your program. To provide flexibility, one of the features that WAS provides is the ability to change the class loader delegation behavior. It can either be PARENT_FIRST (the default behavior) or can be modified to PARENT_LAST.
- PARENT_FIRST causes the class loader to delegate the loading of classes to its parent class loader before attempting to load the class from its local class path.
- PARENT_LAST causes the class loader to attempt to load classes from its local class path before delegating the class loading to its parent.
For example, assume that you have an application and it uses the popular log4j package to perform logging from both the EJB module and the two Web modules. Also assume that each module has its own unique log4j.properties file packaged into the module. You could configure log4j as a utility JAR so you would only have a single copy of it in your EAR file. However, if you do that, you might be surprised to see that all modules, including the Web modules, load the log4j.properties file from the EJB module. The reason is that when a Web module initializes the log4j package, the log4j classes are loaded by the application class loader. Log4j is configured as a utility JAR. Log4j then looks for a log4j.properties file on its class path and finds it in the EJB module.
Even if you do not use log4j for logging from the EJB module and the EJB module does not, therefore, contain a log4j.properties file, log4j does not find the log4j.properties file in any of the Web modules anyway. The reason is that a class loader can only find classes by going up the hierarchy, never down.
To solve this issue, you can use one of the following approaches:
1) Create a separate file (for example, Resource.jar), configure it as a utility JAR, move all log4j.properties files from the modules into this file, and make their names unique (like war1-1_log4j.properties, war1-2_log4j.properties, and ejb1_log4j.properties). When initializing log4j from each module, tell it to load the proper configuration file for the module instead of the default log4j.properties.
2) Keep the log4j.properties for the Web modules in their original place (/WEB-INF/classes), add log4j.jar to both Web modules (/WEB-INF/lib) and set the class loading mode for the Web modules to Classes loaded with local class loader first (PARENT_LAST). When initializing log4j from a Web module, it loads the log4j.jar from the module itself and log4j would find the log4j.properties on its local classpath, the Web module itself. When the EJB module initializes log4j, it loads from the application class loader and it finds the log4j.properties file in its own class path and loads the log4j.jar from its own class path.
In conclusion, the ability to set class loader policies and to manage them at a micro level shows us the extent to which applications deployed on WAS can be customized. There is absolutely no parallel to the flexibility provided by WAS when it comes to application management.