Reputation: 734
/tomcat/lib
dir I have class SharedClass
(which is thus shared with all web apps).WEB-INF/lib
I have class LocalClass
. SharedClass
has a reference to LocalClass
.In my web app I try to create an instance of SharedClass
but it fails with the message:
NoClassDefFoundError: LocalClass.
Since SharedClass
is shared and LocalClass
is local to my web app I was hoping it would work, but it doesn't.
My suspicion is that SharedClass
is loaded by the Tomcat parent classloader and LocalClass
is loaded by the Web App classloader. Since SharedClass
was loaded by the parent, I assume that all of its dependencies must also be loaded by the parent. Thus, the parent can't find LocalClass
and it throws the Error.
Does this make sense? Is there any way around this (without writing my own classloader)?
Upvotes: 6
Views: 3070
Reputation: 68905
In normal Java applications when a classloader is asked to load a class it forst delegates the request to it's parent class loader and then loads it if parent class loaders cannot find the requested class.
For web application servers this slightly differs. There are generally different class loader for each web app deployed in a web application server like tomcat. For Tomcat it looks like below -
So for web apps class loading resource happens in following order -
But note if web application class loader is configured with delegate="true"
then order is changed -
For more details you can check Apache Tomcat's Class Loader HOW-TO page.
Upvotes: 0
Reputation: 1663
This does make sense, and it's the way that Tomcat is able to isolate applications. More information can be found here: http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html
There are very few valid reasons for a shared class to load an application class or create an instance of an application class. For the rare cases where you need to do this, you can call Thread.getCurrentThread().getContextClassloader()
.
Edit: I feel like I'm handing a LOX-soaked briquette to someone who wants to start a campfire, so here are the reasons why it's a bad idea to load an application class from the shared classloader.
The immediate problem is that you haven't actually loaded the class into the shared classloader, so you can't easily manipulate the objects that you create. Instead, you need to use reflection to invoke methods, which is going to make your code a mess.
But the worse problem is that, if your SharedClass
instances maintain a reference to instances of LocalClass
, those references will prevent Tomcat from undeploying the application. Actually, it will claim to undeploy, but there will still be pieces of the old application living in the permgen until those references are collected, and that often leads to permgen exhaustion.
So, while the context classloader is a useful tool -- and should be the only way that you access classpath resources -- it's a tool that can easily cause things to blow up.
Upvotes: 3
Reputation: 691625
ClassLoaders are hierarchical. A class loader has a parent, and sees the classes of its parent. The reverse is not true. So a class loaded by the webapp's classloader has access to the classes loaded by the common Tomcat classloader (its parent), which has access to the JRE classes (the parent of Tomcat's classloader).
See the Tomcat documentation and the ClassLoader javadoc for more details.
Upvotes: 7