Understanding Classloaders: log4j in a J2EE Environmentby Vikram Goyal
A previous article of
mine explained the basics of
log4j is an open source logging tool
developed under the Jakarta Apache project. The previous article demonstrated
how to use
log4j in a strictly JSP/servlet environment, which forms half of the
whole J2EE world. The other half, EJBs, requires a subtler way of handling your
log4j code and configuration. This article will show you why this is the case
and how to go about it.
log4j is a popular logging tool, as it provides flexible control over logging
and debugging requirements of a Java project. It is hierarchical in nature and
provides runtime control over all aspects of logging without having to change
the source code.
log4j controls logging with three main properties: loggers, appenders, and
layouts. A logger logs to an appender in a particular
layout (style). These can be specified using an external
configuration file, which is the best way of doing so. These configuration
properties are loaded during your application startup and can be changed at
The steps involved in using
1. Write a configuration file. In this file:
- Specify the level of the root logger and attach an appender to it.
- Specify the properties of the appender.
- Specify a layout for the appender.
2. In your code, acquire a logger by class or name. Typically, this should be the logger associated with the current class.
3. Start logging using any of the methods of the logger you acquired in step 2
The examples in this article are tested on BEA WebLogic 7.0 SP2 demo
version. The reason for using WebLogic instead of an open source equivalent
like JBoss is because WebLogic provides the hardest challenge in terms of
log4j. It is also the most prevalent application server and the one
for which I received the most requests for help after my previous article.
This setup is by no means the recommended setup for WebLogic. It is only
intended as a testing environment towards configuring
log4j in a J2EE
environment for this article.
WebLogic 7.0 SP 2 can be downloaded from BEA's trial page. Select and download the installer with which you are most comfortable. Note that you will need to register with BEA and that the download size may be huge (approximately 150MB). Alternatively, you could ask for a free demo CD to be sent to you.
Once you get hold of the installer, installing WebLogic is a simple process
of answering wizard-style questions. Choose the typical install for this
testing environment. When you reach the end of the install, you will be asked
if you want to configure a domain. Select "yes" to run the domain configuration
wizard. The first screen will ask you to select the domain type and name.
WLSDomain as the domain type and leave the name as
mydomain. In the next screen, leave the server type as single
server (standalone server). Use the default location in the next screen for
the domain location. On clicking "next," you will be taken to the standalone
server configuration. Leave all entries as they are and click "next." You will
now need to select a username and password for this domain. I used
admin as the username and
password as the password
(Highly original!). Select "No" on the next screen for registering the service
as a Windows service (if you are running this on Windows). Finally, select "Yes"
to placing a shortcut on the Windows Start Menu. The final screen lets you
review everything that you have just done. Clicking the "create" button will
create this domain for you.
To run WebLogic for this domain, go to Start->All Programs->BEA WebLogic Platform 7.0->User Projects->mydomain->Start Server. This simply launches a Windows command file located in your BEA home directory under user_projects\mydomain called startWebLogic.cmd. You will be asked for the username and password that you provided while running the domain configuration wizard. You will know the server is running successfully when you see the message "Server started in running mode." To exit the server, simply close this DOS window.
Once your server is running successfully, fire up a browser window and navigate to http://localhost:7001/console. This will bring up the console window from where you can install and configure the applications to run on this server. You will be asked for your username and password, as before. Once you are in, you will see various administrative tasks that you can perform.
As mentioned before,
log4j can be downloaded from the
log4j web site. Please refer to the previous article on how to download and install the
binaries. We will leave the configuration of
log4j until later in this
Why is It Different? The Concept of Classloaders
Although a full and thorough discussion on classloaders is outside of the scope
of this article, I will try and explain what classloaders are and how they
impact our configuration of
log4j in an application server.
Classloaders, as the name suggests, are responsible for loading classes within the JVM. Before your class can be executed or accessed, it must become available via a classloader. Given a class name, a classloader locates the class and loads it into the JVM. Classloaders are Java classes themselves. This brings the question: if classloaders are Java classes themselves, who or what loads them?
When you execute a Java program (i.e., by typing
java at a
command prompt), it executes and launches a native Java launcher. By native, I
mean native to your current platform and environment. This native Java
launcher contains a classloader called the bootstrap classloader. This
bootstrap classloader is native to your environment and is not written
in Java. The main function of the bootstrap classloader is to load the core
Figure 1; Classloader delegation hierarchy
The JVM implements two other classloaders by default. The bootstrap
classloader loads the extension and application classloaders into memory. Both
are written in Java. As mentioned before, the bootstrap classloader loads the
core Java classes (for example, classes from the
package). The extension classloader loads classes that extend the core Java
classes (e.g., classes from the
packages, or the classes under the
ext directory of your runtime).
The application classloader loads the classes that make up your
All three default classloaders follow the delegation model. Before a child classloader tries to locate a class, it delegates that task to a parent. When your application requests a particular class, the application classloader delegates this request to the extension classloader, which in turn delegates it to the bootstrap classloader. If the class that you requested is a Java core class, the bootstrap classloader will make the class available to you. However, if it cannot find the class, the request returns to the extension classloader, and from there to the application classloader itself. The idea is that each classloader first looks up to its parent for a particular class. Only if the parent does not have the class does the child classloader try to look it up itself.
In application servers, each separately-deployed web application and EJB gets its own classloader (normally; this is certainly the case in WebLogic). This classloader is derived from the application classloader and is responsible for that particular EJB or web application. (Note that if an application is deployed as an EAR file--a combination of EJB and webapps--it gets one classloader, no more). This new classloader loads all classes that the webapp or EJB require that are not already part the Java core classes or the extension packages. It is also responsible for loading and unloading of classes, a feature missing from the default classloaders. This feature helps in hot deploy of applications.
When WebLogic starts up, it uses the Java-supplied application classloader to load the classes that make up its runtime. It then launches individual classloaders, derived from the Java application classloader, which load the classes for individual applications. The individual classloaders are invisible to the classloaders of the other applications; hence, classes loaded for one particular application will not be seen by another application.
Figure 2: Individual application classloaders
What if you want to make a single class available to all applications? Load it in a top-level classloader. This could be in the classpath of WebLogic. When WebLogic starts, it will automatically load this class in memory using the Java-supplied application classloader. All sub-application classloaders get access to it. However, the negatives of this approach are clear too. First, you lose the capability of hot deploy for this particular class in all individual applications. Second, any change in this class means that the server needs to be restarted, as there is no mechanism for a Java application classloader to reload classes. You will need to weigh in the pros and cons before you take this approach.
log4j is an external library to your J2EE application. Where do you store
this library? As mentioned in the previous paragraph, one option is in the
WebLogic startup classpath. However, this seems to be an easy way out, and is
not recommended for the reasons stated earlier. Configuration of
log4j in a
J2EE environment is different because EJBs don't see the classes loaded by a
relevant webapp--EJBs are loaded by a different classloader! This is the
general case, unless you package your application to use the same classloader for loading your EJB as well as the webapp. I will illustrate this concept with
We will start with deploying the original webapp from the previous article along with an EJB and a JSP that exercises this EJB in our newly-configured WebLogic server. The EJB is a very simple EJB, containing an even simpler method that returns "Hello World" when invoked.
Example: Why Doesn't It Work?
Deploy the updated .war file and then the EJB .jar file for this example using the WebLogic console. When you try to install the EJB .jar file, you will get an error stating
org.apache.log4j.Logger classes cannot be found.
The EJB .jar file has no knowledge about the
log4j classes in the
WEB-INF directory of the corresponding webapp, as the EJB and webapp use different classloaders. Even if you combine these two into an .ear file and try to deploy it, you will still get
the same error message. Why? Even though there is only one classloader for the
.ear file and it theoretically should be loaded when you deploy, we have not
told our EJB where to access the log4j.jar files.
Example: Why It Will Work
Here is an updated .ear file with a working example. Let me list the changes to make this example work:
- I moved the log4j.jar file from the webapp's lib directory into a top-level directory. This gives the .jar file the EJB's visibility.
- I modified the Manifest.mf for the EJB .jar to contain an entry
Classpath, pointing to log4j.jar.
These changes are enough to make this .ear file deployable in WebLogic. When
you deploy this application using the console, you will not get any errors,
because our EJB now has access to the
log4j classes. There is only one
classloader for the Log4JDemoEAR2 application. It is responsible for loading
the libraries and classes for this application as a whole. The
Classpath entry for the EJB is used to resolve dependencies and
load log4j.jar. The webapp still has access to this library, even
after moving it out of lib, as we use the same classloader!
Strategy For a J2EE Environment
Depending on your requirements, I suggest the following strategy. You will
realize that it is not just relevant for
log4j, but to any external utility
classes that your application might require.
- If the utility class is required only by your web application, keep it under the WEB-INF/lib directory.
- If it is required by both your web application and your EJB(s), make an
entry in your manifest file for the EJB and specify all of the utility classes on
Classpathentry. Keep this utility file either at the top level or under a top-level directory in your .ear file. This way, all of your EJBs and .war files will have visibility to this utility class without you needing to duplicate it.
J2EE packaging can be quite tiresome if you are not clear on how classloading works. A good understanding of this "under the hood" mechanism is critical to deploying your J2EE applications successfully. It helps to have a deployment engineer specifically geared towards this task.
log4j works seamlessly in both aspects of a J2EE application. It just has to
be configured correctly. I hope this article has shown you the correct way.
- Tyler Jewell's commentary on EJB 2 and J2EE packaging explains this strategy in more detail.
- IBM has published a document on Websphere-specific Classloader issues.
- WebLogic's classloader architecture can be accessed here.
- Some information on
log4jand classloader issues can be found in the
Vikram Goyal is the author of Pro Java ME MMAPI.
Return to ONJava.com.