dough
dough

Reputation: 734

Are all dependencies of a class loaded by the same classloader?

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

Answers (3)

Aniket Thakur
Aniket Thakur

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 -

enter image description here

So for web apps class loading resource happens in following order -

  1. Bootstrap classes of your JVM (Core java classes)
  2. /WEB-INF/classes of your web application
  3. /WEB-INF/lib/*.jar of your web application
  4. System class loader classes (Tomcat / Classpath specific classes)
  5. Common class loader classes (classes common to all web apps)

But note if web application class loader is configured with delegate="true" then order is changed -

  1. Bootstrap classes of your JVM (Core java classes)
  2. System class loader classes (Tomcat / Classpath specific classes)
  3. Common class loader classes (classes common to all web apps)
  4. /WEB-INF/classes of your web application
  5. /WEB-INF/lib/*.jar of your web application

For more details you can check Apache Tomcat's Class Loader HOW-TO page.

Upvotes: 0

parsifal
parsifal

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

JB Nizet
JB Nizet

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

Related Questions