Classloaders Demystified - Understanding How Java Classes Get Loaded in Web Applications
By Andrew Tee
What is a classloader?
Java applications run on a Java Virtual Machine (JVM).
Our J2EE applications run on a Java Virtual Machine as well, after all, they are written in Java.
In the WebSphere world, the JVM that our J2EE applications run on is often referred to as an ‘application server,’ since it is responsible for serving our J2EE applications up to clients. Under the covers though, an application server is nothing more than a souped-up JVM running on the server.
If you are so inclined, you can even monitor the JVM responsible for serving up your J2EE applications by bringing up the Windows Task Manager and looking for the java.exe process that’s hogging your memory and consuming all of your processor’s clock cycles. That’s the Java Virtual Machine that enables your WebSphere applications.
J2EE applications are written in Java, and JVMs have the job of running and executing the Java code you have written.
When a client calls a Servlet, or an EJB calls the constructor of a JavaBean, compiled Java code, also known as bytecode, must be loaded and subsequently run on a JVM. Finding the appropriate Java bytecode file, and throwing that bytecode onto a piping hot JVM is the job of a very special, and extremely important component known as a classloader.
Classloaders locate Java bytecode files and load that Java bytecode on the JVM. Without a classloaders, our J2EE applications simply would not work.
Important Java classes that must be loaded onto the Java Virtual Machine in order to get our applications to perform properly include:
F classes that implement the basic Java runtime environment such as java.lang.Interger
F the code IBM has written to support and implement the J2EE specification
F EJBs and the JavaBeans they use to implement complex business logic
F all of the Servlets, JSPs and helper classes that make our web based applications behave the way they should
Understanding how classloaders work is one of the most important aspects of WebSphere development, packaging and deployment. Unfortunately, classloading is also one of the most misunderstood aspects of J2EE development, leading to innumerable problems at both runtime and design time.
To understand how the WebSphere runtime works, you must have a good grasp of J2EE classloading.
If a Java class is invoked and needs to be executed on a Java Virtual Machine, a special Java component, called a classloader, is used to find the Java class of interest, pull that Java class off of the file system, and execute the bytecode of that class file on the Java Virtual Machine.
A classloader is a special Java class file that is responsible for loading other classes onto a Java Virtual Machine. While this may seem simple and straightforward, don’t get too cocky, ‘cuz from here, things get really complicated.
Life would be simple if there was just one, monolithic classloader that did all of the classloading, but life just isn’t that simple. There are actually quite a number of different classloaders that work together to locate and load Java bytecode. If you want to package your applications properly, and avoid ClassNotFoundExceptions, you must be familiar with the many WebSphere classloaders.
The old chicken and egg scenario, rearing its ugly head. If classloaders are Java classes, and Java classes must be loaded onto a JVM to run, how does the first classloader get loaded? It’s a good question.
When a JVM starts up, a special chunk of machine code runs that loads the system classloader. This special hunk of machine code is known as the null classloader.
The Null Classloader
The machine code that initializes the system classloader is referred to as the null classloader because it is not a Java class at all, as are all other classloaders. The null classloader is platform specific machine instructions that kick off the whole classloading process.
All classloaders, with the exception of the null classloader, are implemented as Java classes. Something must load the very first Java classloader to get the process started. Loading the first pure Java classloader is the job of the null classloader.
The null classloader also takes care of loading all of the code needed to support the basic Java Runtime Environment (JRE), including classes in the java.util and the java.lang packages.
The System Classloader: sun.misc.Launcher
The null classloader loads a true Java class of type sun.misc.Launcher. The sun.misc.Launcher classloader prepares the basic Java Runtime Environment needed by our application server.
Truth be told, the sun.misc.Launcher is actually made up of two inner classes that do the actual classloading:
F sun.misc.Launcher$AppClassLoader (system)
ExtClassLoader is the parent of AppClassLoader. Of the two, the inner class, sun.misc.Launcher$AppClasLoader holds the true title of system classloader
Quite appropriately, the sun.misc.Launcher is often referred to as the bootstrap classloader as well as the system classloader. Any jars found in the jre/lib directory of the JRE (not the WebSphere lib directory), will be loaded by the system classloader. The system classloader is tightly integrated with the components that implement the Java Runtime Environment (JRE).
From within the WebSphere administrative console, you have the option of adding Java files and resources to the WebSphere CLASSPATH. When you add libraries to the WebSphere CLASSPATH, you are actually asking the system classloader to load those classes when they are needed. Appropriately, the system classloader is also known as the CLASSPATH classloader.
The system classloader is great at initializing the basic Java Runtime Environment (JRE), but from a WebSphere and J2EE perspective, the system classloader isn’t nearly as sexy as the WebSphere extensions classloader.
WebSphere Classloader: com.ibm.ws.bootstrap.ExtClassLoader
WebSphere doesn’t want to step on any toes when it starts loading the classes that it needs at runtime. Rather than using the system classloader, WebSphere uses its own special classloader to load the J2EE resources it needs.
When the system classloader is finished initializing, WebSphere takes over, kicking off its own classloader.
The WebSphere classloader physically manifests itself as the Java class of type com.ibm.ws.bootstrap.ExtClassLoader
Whereas the system classloader loads resources that support the Java Runtime Environment (JRE), the WebSphere classloader is responsible for initializing the J2EE runtime environment.
The WebSphere classloader, also known as the WebSphere extensions
classloader, loads all of the classes and files located in the \lib directory of
the WebSphere Application Server.
Figure 19-2 The WebSphere\AppServer\lib Directory
The WebSphere extensions classloaders looks for files stored in JAR files in the lib directory of the Application Server.
You can also add files to the WebSphere classloader’s classpath by editing the WebSphere system property ws.ext.dirs.
Since the com.ibm.ws.bootstrap.ExtClassLoader loads all of the classes required to support WebSphere and the J2EE specification, it is also known as the WebSphere bootstrap classloader, as it essentially bootstraps the WebSphere runtime environment.
The WebSphere j2ee.jar file, which is pretty much the bytecode embodiment of the WebSphere Application Server, falls onto the classpath of the WebSphere extensions classloader. The ExtClassLoader pretty much loads WebSphere, and all of the peripheral components that make WebSphere run.
So, a null classloader kick starts
the JVM, a bootstrap classloader loads classes needed by the basic Java Runtime Environment (JRE),
and then WebSphere takes over, using the extensions classloader to invite all of its friends and
family to come over and join in on the fun.
Table 19-1 Basic Classloader Hierarchy for WebSphere
The Application Classloader:
Of course, our humble enterprise applications are by no means on the same level as the ominous JVM and the ever so important WebSphere Application Server. Correspondingly, our ear files are given their own humble little classloaders, separate from that of the WebSphere Application Server (WAS) and the Java Runtime Environment (JRE). A special classloader, called the com.ibm.ws.classloader.CompoundClassLoader, loads the resources found packaged inside of our EAR files.
The CompondClassLoader can pull classes out of all of the different modules in an ear file. By default, all utility Jar files, connector modules, and EJB modules will have their classes loaded by the same instance of a common compound classloader.
Since all of the utility Jars, EJB modules and connector modules (notice that I
didn’t say web modules) share the same classloader instance, files in one
module of the ear will be visible to other modules in the ear. This is a manifestation of the fact
that a classloader is entitled to pull classes out of any JAR on its classpath.
Web Modules and the CompoundClassLoader
By default, web modules do not share the same classloader instance as
the other modules inside of an ear. Web modules get their own, private instance of
the CompoundClassLoader, which will only load classes that exist within the web module. War files
have their own private classloader, ensuring that none of the other modules can peek inside the war
file for classes they might need.
The classloader instance used by the web module is a sibling of the instance used by the ear. As a result, a web module can see files inside other jars in an ear. However, other modules cannot see classes inside a war. Child classloaders can see classes on the classpath of the parent. A parent cannot see classes on the classpath of a child.
The picture I am trying to draw here is one of a crazy classloader hierarchy, with the null classloader at the top, the classpath classloader a little bit below, the WebSphere extensions classloader directly underneath, the application classloader residing under the WebSphere classloader, and a lonely web module classloader resting at the very bottom.
In its simplest form, this is the classloader hierarchy for J2EE applications deployed to the WebSphere Application Server.
When a classloader, such as the CompoundClassLoader for a web module, looks for a Java file, where do you think it looks?
Logically, you’d assume that the web module classloader would look in the web module for a given Java file or Servlet, wouldn’t you? Well, this type of foolish thinking is wrong, and has caused all sorts of deployment problems.
By default, each classloader, with the exception of the ExtJarClassLoader, is set up with a ‘PARENT_FIRST’ configuration. A PARENT_FIRST configuration forces a classloader to ask its parent to try and load a given a Java class first.
If a web module needs some chunk of Java code that is sitting right there in the \lib directory of the war, the web module classloader first asks its parent classloader, the application classloader, to find the Java class file.
And what do you think the application classloader does when asked to load a Java class file?
When the application classloader is asked to load a class, it delegates to its parent, the WebSphere extensions classloader, to see if it can find and load the file.
By now, you can probably guess where the WebSphere extensions classloader goes before looking on its own local classpath for the file in question. Yes, it defers to the system classloader, which then defers to the null classloader. The null classloader then tries to find the Java class of interest on its own local classpath, and if it does find the file, it will load it on the JVM.
So, what happens when the system classloader can’t load the class your looking for?
Of course, there’s a good chance that the Java class of interest will not be on the system or null classloader’s classpath. After all, why would you put a class required by your web module in the lib directory of the JRE upon which WebSphere runs?
When we hit the top of the classloading hierarchy, the class-finding baton gets passed back down the ladder, one classloader at a time, opposite to the order in which the hierarchy was climbed.
Figure 19-3 Inverted Classloading Hierarchy for an EJB
When loading EJB module classes, the CompoundClassLoader delegates to its parent according to the rules of classloading.
Classloader for the EJB module:
Figure 19-4 Inverted Classloading Hierarchy for a Servlet
Classloader for the web module:
If the null or system classloader can’t find the class file of interest, the WebSphere classloader then starts looking for it, and if the WebSphere classloader can’t find the class file, the classloader with authority over the ear starts looking, and if the classloader for the ear can’t find the file, it’s up to the web module to locate it, which in the case of a Servlet or JSP, was the classloader that was asked to load the file in the first place!
If the class in question was in the lib directory of the web module to begin with, it’s not until the entire hierarchy has exhaustively been explored, that the web module classloader can finally load the file of interest.
A Java file might be found by any classloader in the hierarchy, but it is the first classloader that finds the file, starting from the top of the hierarchy down, that actually does the loading.
If a class file is found, life is good, and our Java applications will hopefully run fine. If none of the classloaders can find the file, then we run head first into the java.lang.ClassNotFoundException.
While the behavior of the classloading hierarchy may seem a little bit convoluted, it is actually quite a good idea, and for the most part, it works quite well.
Imagine for a second that some cheeky monkey wrote their own implementation of the java.lang.Integer class, hacking around, really messing up the implementation of this fundamental Java class.
Even if a developer packaged this file right inside of an ear that was designed to use it, the new implementation of the java.lang.Integer class would never get loaded by any of the classloaders.
The java.lang.Integer class already exists on the classpath of the null classloader, right at the top of the classloading hierarchy, so if this new, hacked implementation of the java.lang.Integer class was packaged along with an ear or war file, the hacked implementation would never be loaded. Regardless of the number of applications that might be deployed with a fraudulent implementation of the java.lang.Integer class, only the proper class, the one loaded by the null classloader, would ever be used.
The classloading hierarchy provides a secure, consistent and reliable class loading behavior.
Now, while your basking in the glow of the effectiveness of the Java classloading hierarchy, think about this alternative scenario:
Perhaps one of your developers has gone hog wild, XML crazy, and has been using the latest, greatest version of the Xerces XML parser in all of their web applications. The developer correctly packages the files required to implement the XML parser in the lib directory of the war, right alongside all of the Servlets and helper classes that need to use the xerces parser. WebSphere shouldn’t have any problem finding these Xerces-XML parser files, right?
Well, the developer may have configured everything to work ‘honkie dorie’ in the development tool, but as soon as the application goes into production, all sorts of errors will be generated about deprecated methods, classes not conforming, or even that certain methods being invoked don’t actually exist.
This is where things get interesting. J
The IBM implementation of the WebSphere Application Server relies on a boatload of JAR files and Java files, many of which come from common, open source projects such as xerces, xalan and ant.
If you check out the ‘lib’ directory of the application server, which is the directory the WebSphere extensions classloader pulls classes from, you’ll discover a plethora of JAR files and Java classes that just might conflict with your attempt to use the latest and greatest technologies on the open market.
In this particular scenario, the WebSphere extensions classloader is picking up its own, older version of the xerces.jar file. None of the calls the developers are making to the latest xerces implementation are working properly, despite having placed the latest xerces.jar file right there in the lib directory of the web module. The older xerces.jar file in the lib directory of the application server trumps any xerces.jar file found below it on the classpath hierarchy.
In this scenario, we have a conflict between the xerces.jar file in the WebSphere lib directory, and the xerces.jar file in the lib directory of the web module. What do we do when we want to use the latest and greatest edition of a technology that WebSphere uses behind the scenes?
Whatever you do, do not replace the existing JAR file in WebSphere lib directory with the latest and greatest edition. That will hose everything.
But at the same time, you can’t just throw away all of the work your development team has done using the latest and greatest frameworks from Apache.
So the question now becomes: ‘has all of the development done using the latest version of the xalan.jar file been in vain?’ Well, not necessarily.
Although ‘parent first’ is the default classloading behavior, WebSphere allows you to configure your classloaders to employ a ‘parent-last’ classloading configuration instead.
What is ‘parent-last’ classloading?
A ‘parent-last’ configuration, which is set on a per classloader level, instructs a classloader to look locally for resources before passing the job off to a parent. With a parent-last configuration, a classloader will only delegate to the parent classloader if the Java file in question is not on its own local classpath.
If your developers have been working with the latest version of xerces, but WebSphere keeps using the older version that sits in its lib directory, you can configure the classloader for the web module, or even for the ear, to use a parent last configuration when looking for Java files to load.
Table 19-2 Classloader Hierarchy
Machine code that initializes the Java environment
classes from the basic Java runtime libraries
Loads classes used by the basic Java Runtime Environment
Classes found in the lib directory of the JRE
WebSphere Bootstrap Classloader
Loads the J2EE and WebSphere runtime environment
The WebSphere \lib directory
Loads security related Java classes, I think
Not sure L
External Jar Classloader
Loads common classes needing a PARENT_LAST mode of delegation
The \lib\app directory of the application server
Non WAR Classloader
Used to load files inside modules of an ear, with the exception of the web module
This instance of the CompoundClassLoader has scope in all non-web modules of an EAR
Internal Jar Classloader
Makes classes in non-web modules of an EAR visible to web module components
Parent of the WAR classloader
This instance of the CompoundClassLoader has scope in all non-web modules of an EAR
Loads the web components and Java code found in the WAR
Individual WAR files within an EAR
When a web module is configured to use a parent last configuration, if the xerces.jar file is placed in the lib directory of the war, the war classloader will pull classes from the web module first, and defer to the WebSphere extensions classloader, which pulls in the older version of xerces, later.
Furthermore, when WebSphere itself needs the older version of the xerces.jar file, it will use the WebSphere classloader, which will look into its local lib directory and find the older version. Both WebSphere and the war file will be using the correct, yet different, versions of the same set of classes.
Figure 19-5 Classloader Settings
What is a module or application classloader policy?
It was stated earlier that the web module, by default, uses an instance of the CompoundClassLoader that is separate from the other modules in the ear. This is the default classloader policy for an ear, known as module centric classloading.
However, you can switch from module centric classloading to application centric classloading. By making this change, all modules in an ear, including web and ejb modules, will use the same classloader instance. The web module will not be given a separate and unique classloader
The implication of an application classloader policy is that any module will be able to see classes in any other module in the ear, including web modules. With a module centric classloader policy, ejbs and utility classes cannot see classes inside of a WAR.
People find it difficult to get their head around the fact that it is possible to load two or more different versions of the same class onto a JVM. Not only is this possible, it is extremely common.
One of the rules of classloading is that a classloader can never use or access classes loaded by a classloader beneath it in the hierarchy. If a web module configured with a parent-last configuration loads a class called com.pulpjava.User, and then the application classloader, or even the WebSphere classloader, is asked to load the com.pulpjava.User class, so long a version of the class file can be found on their classpath, which is indeed different from the classpath of the web module, a separate instance of the same class will be loaded onto the JVM.
You must be very careful where you package your Java classes, because placing a file in a war file, an EJB module, a utility jar inside of an ear, or even on the WebSphere classpath, will have a significant impact on classloading, class instantiation, and class visibility.
Is it possible to have multiple instances of a singleton running on a JVM?
A singleton is a Java class that is programmed in such a way that ensures only one instance of that class is ever loaded onto the JVM. A singleton is one of the most common design patterns in existence, and is extremely effective when trying to mitigate access to various resources.
However, if a singleton class is packaged and placed on the classpath of different classloaders, it is indeed possible to have many instances of a singleton running. If a singleton is managing some mission critical resources, be sure that your applications are packaged in such a way that the classloading hierarchy won’t start spinning off multiple instances.
Why am I getting Serialization errors when I run my applications?
Another interesting problem that can arise from having a different classloader for each application and web module, especially when you configure a parent-last configuration, is conflicting versions of the same class being loaded.
For example, say a web module was using com.pulpjava.User version 1.1, and an EJB module in another project was using com.pulpjava.User version 1.2. You can only have one version of a JavaBean loaded on a JVM right? Wrong!
Even though both applications might be running on the same JVM, if the web module was configured with a parent-last classloading configuration, and it then tried to pass a User object to the EJB module, there would be all sorts of serialization and version errors, because both modules would be using different versions of the same JavaBean.
Both modules may have a User class, and both modules can load that class onto the JVM. However, when they try to pass the User object back and forth, runtime exceptions will be thrown because the versions are out of sync.
This type of scenario can produce a variety of cryptic error messages, and troubleshooting comes down to figuring out which module is loading which version of the class.
The solution usually requires redeploying the applications in such a way that they can communicate with each other properly. This may be implemented by repackaging the applications to ensure that both EJB and web modules are using the same classloader to load the com.pulpjava.User class.
An alternative solution is to play around with the serialization id of the
com.pulpjava.User class to make the JVM think the two
classes are the same. Regardless, solving serialization id errors are never fun.
A classloader can never use or access a class loaded by a classloader below itself on the hierarchy. This can create a bizarre scenario where a web module might be able to find a Java class named User, but an EJB, which uses a different classloader, gets a ClassNotFoundException when trying to access the exact same class.
Reckoning with the fact that all of this is happening on the same JVM, I usually just start shouting at the JVM, demanding to know why it can find the file just fine in the web module, but not in the EJB module. For what it’s worth, I’ve never found shouting at a JVM to be successful way of resolving ClassNotFoundException, but it does tend to make me feel better.
Class visibility describes which classes can be seen by which classloader, and subsequently, which J2EE components.
Troubleshooting classloader issues is extremely difficult without a fundamental understanding of how classloading works.
An interesting scenario happens when you’ve got three classes all accessing a fourth. Let’s just say the forth is a database driver.
If the driver is packaged in the root of the ear, the Servlet will be able to call on the driver when it runs. An EJB in the same project will also be able to call on the driver when it runs. However, a common component packaged on the WebSphere extension classpath, will NOT be able to call upon the driver and will instead cough up a ClassNotFoundException.
Again, you will be able to prove that indeed the driver can be found by the JVM, since both your EJB and your Servlets will be able to use it. You can even profile the driver on the JVM and prove without a doubt that not only can the JVM find the class, but the JVM can load it and run it without any hint of a glitch.
It’s only when the component packaged at the ws.ext level tries to make a call to the component below it on the classloader hierarchy that your application runs into a problem.
It’s a hard lesson to learn, but a classloader cannot make calls to classes that are on the path of a classloader below it on the classloader hierarchy.
In the preceding scenario, you could do one of two things to eliminate the ClassNotFoundException:
F move the driver up to the ws.ext directory along with the common component
F move the common component down into the ear along with driver
Personally, I would do both. Typically, drivers go on the ws.ext classpath, and common components go into the root of the ear.
So, with all of these classloaders and various classpath issues, the question becomes, “how should we package our applications, and where should we place various jar files such as common components, drivers, utility classes and others?”
The first rule is: all of your EJBs go in EJB modules, and all of your Servlets should go in you web modules. Unfortunately, from there it gets a little tricky.
For example, where should you put the jar files that have the implementation of your custom tags? Custom tags make JSP development much easier, and they are a web centric component, so conventional wisdom says you should put it in the lib directory of your web module. But what if your ear file has four or even fourteen web modules that use those custom tags? Should you duplicate the jar file that contains the code for your custom tags and place a copy of that jar in each of the fourteen web modules, or should you put just one central copy inside of the ear.
Should you duplicate the file 14 times, or should you have every module reference just one, single copy? Duplication seems like such a waste, doesn’t it?
Placing the jar containing the Java code that implements the customs tags in the root of the ear means you have one central location for you code, and when the application runs, only one instance of a class will be created to handle the requests, despite having many modules. That definitely sounds like the best alternative, but to be honest, that’s not the alternative I’d go with.
Personally, I’d place a copy of the jar file in each war. That means the code is redundantly copied from war to war fourteen times, which in itself is a bad thing. It also means that I’ll have extra instances of common tag classes running, which means a slight degradation in performance. But for my money, I’d rather duplicate that common code in each war.
The long-term cost of any project is not the cost of getting it up and running, but the cost of managing and maintaining that application over time.
Having every web module pointing to the same custom tag code is going to make future version changes a real headache. If I update the custom tag code at the ear level, then every web module must upgrade to use the newer version as well. Expecting every web module in the application to update at the same time is unreasonable.
It would be much easier to place this jar file in each war, and when a new version
of the library comes out, the battle to migrate the application can be waged on a war-by-war basis.
After all, when was the last time every single application in your enterprise migrated at exactly
the same time? Most likely never.
How should you package common utility components?
Now let’s think about a general set of common components. Say for instance, your company has a set of common components that include a User object, Employee interface and a bunch of other useful and reusable objects that get used in all of your EJB and web modules. Where would place a jar file like this?
Smart money says you should put one copy in the root of the ear, and not duplicate the common component jar file in each of your web and EJB modules.
Now I know that this directly contradicts the maintainability argument of the previous scenario, but the fact is, with common components, you often need to enforce a common versioning.
A common design pattern has EJBs and Servlets passing JavaBeans back and forth to each other. These JavaBeans must be at the same version level, and have the same set of properties and methods; otherwise, the process of marshalling these objects back and forth between EJBs and Servlets is going to fail.
Packaging these common components in the root of the ear will help maintain a common version across all EJB and Web modules in the application.
Keep in mind that you may, and likely will, have components from one ear file calling components in another ear file, and they’ll probably pass data back and forth using classes defined in your common components jar file. If this is the case you could still run into the problem of having version incompatibilities between jar files, forcing you to upgrade the common components in several ear files at the same time.
For me, that’s a chance I’m willing to take, and I’m going to depend on my architects to have articulated the application interdependency effectively and in excruciating detail within the design documents.
Of course, if you want to be absolutely positive all enterprise applications are using the same version of your common components, you could remove the common-components.jar file out of the root of the ear, and place the common components in the ws.ext directory, but I think that’s a pretty big trigger to pull as well.
What are the drawbacks to using the WebSphere extensions classloader for common files?
One thing that should be stated when talking about classloaders and the extensions directory is that any component that does a call back into a web module or an ejb module must be placed inside the ear and not on a classpath outside of the ear.
For example, say a Servlet calls on a helper class that uses an EJB. If that helper class were placed outside of the ear, the Servlet would be calling a component that exists outside of the application context. If the helper class then made a call back into the ear, to interact with an EJB for instance, that callback would be treated as a completely anonymous invocation from a component outside of the application’s context.
This poses a real problem, especially if we have security turned on or transactions running. By leaving the ear, the entire security context of the Servlet will be lost, and the resulting call will end up with an error to the effect that an unauthenticated user cannot access the EJB, despite the fact that the client was challenged when the Servlet was first invoked.
If you’re having problems with security, and you’re getting errors indicating that a secured resource is being invoked by an anonymous user, despite being challenged every time you try to access the component in question, then a component outside of the ear is making a callback.
This scenario can be especially frustrating due to the fact that when security is not on, everything works fine. Only when you turn security on does a problem like this rear its ugly head.
So, any components making a callback into the ear must be placed within the ear if we wish for those components to maintain their security or transactional contexts.
What should we place on the ws.ext classpath?
The extensions classpath is a great place to throw any commonly used components that do not make callbacks into a J2EE application. This includes elements such as common parsers and database drivers.
Every application in the enterprise should be connecting to the same version of db2. Everyone should be connecting to the same mail server. If the mail server changes, or the version of DB2 is updated, all of the applications will likely be updated at the same time. Placing database drivers for db2 in the ws.ext classpath will make updating the database drivers for all of your applications easy and manageable.
There is an art to effectively packaging your J2EE applications. With an understanding of how the various WebSphere and JVM classloaders work, you will make deployment easier, and avoid those tedious ClassNotFoundExceptions at runtime
31 Aug 2010