ClassLoader Issues - Loading a class when loaded in parent ClassLoader

Is there any good documention on ClassLoaders?

I want to understand ClassLoaders because of an issue I am having, but I cannot find any good documentation on the subject.

Here is my issue, but please understand I am somewhat ignorant regarding ClassLoaders.

I have a set of JARs that are frequently updated by a team of mine and I periodically need to update these classes so that I can call remote method calls.

I don't want to shut down my application every time they update their code, so as long as the API signatures do not change, I should be able to execute my code.

Right now I am using the netx library to dynamically load these classes into a URLClassLoader which I store in a Singleton, and periodically check the class versions and create a new ClassLoader when the versions change.

That seems to be working fine. Whenever I call Class.forName(className, true, [singleton classloader]) it is loaded into the the ClassLoader I expect.

The problem is that I need to call methods on another class that has already been loaded into the parent class loader. It cannot see the Singleton Class loader because it is at a lower level in the ClassLoader hierarchy and calls to the class throw ClassNotFoundExceptions because it cannot see the Classes loaded in my Singleton ClassLoader.

I tried to load the class into the sub ClassLoader, but apparently it checks the parent ClassLoader, and defers loading when it exists in the parent. (I am assuming here because when I specify the ClassLoader in the Class.forName() call, the class still only resides in the parent ClassLoader)

Is there any way I can either get this class to unload and reload into the sub classloader or else execute code from the other ClassLoader.

Again, I apologize if none of this makes sense, I just don't know enough about ClassLoaders to form my question intelligently.

Thanks...

[1934 byte] By [bearwiga] at [2007-11-27 10:33:33]
# 1

In this case, you need to ensure that the classes are not held onto, but used and dumped. Seems like you have a class there that is loaded and you can not get it to unload. That should not matter so long as your code is not trying to use that class.

If this class is loaded by a parent classloader, but references the class that is loaded by the URL classloader, then there is a problem with your classloading heirarchy because the class that is loaded by the URL class loader should not even be available to that class and instead you should get a NoClassDefFoundError. If you don't then it means you have not fully abstracted out your dynamic classes.

Simple point. The classes in question that need to be updated should appear in only 1 file. The rest of the program should be using interfaces for them.

also can you clarify what you mean by "it" when you say

"It cannot see the Singleton Class loader"

Any why does "it" need to see this classloader?

_dnoyeBa at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 2

> Is there any good documention on ClassLoaders?

>

> I want to understand ClassLoaders because of an issue

> I am having, but I cannot find any good documentation

> on the subject.

Here are some of my bookmarks:

http://blogs.sun.com/sundararajan/entry/understanding_java_class_loading

http://blogs.sun.com/sundararajan/entry/understanding_java_class_loading_part

http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=demystifying+class+loading+problems

http://www.ibm.com/developerworks/java/library/j-dyn0429/

And for even more in-depth information, see:

http://citeseer.ist.psu.edu/liang98dynamic.html

>

> Here is my issue, but please understand I am somewhat

> ignorant regarding ClassLoaders.

>

> [...]

>

> The problem is that I need to call methods on another

> class that has already been loaded into the parent

> class loader. It cannot see the Singleton Class

> loader because it is at a lower level in the

> ClassLoader hierarchy and calls to the class throw

> ClassNotFoundExceptions because it cannot see the

> Classes loaded in my Singleton ClassLoader.

>

> [...]

Also, from your description, I do not understand your setup/design very well; what exactly you are trying to achieve, and the point where you run into problems. For example, as the previous poster _dnoyeB already mentioned, how did you get the class loaded by the parent class loader (the one that throws ClassNotFoundException when trying to access your newly-loaded class) to compile and then load in the first place? But maybe you are able to find a way with the help of the links above, or can elaborate on the issue...

oeberta at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 3

Well, I believe I have a soution, but it is complex (to me).

First, let me clear up my architecture a bit. I have three sets of classes I am working with. The first set of classes are a client library to a remote service. These are the classes I am trying to update dynamically. The second set of classes is an API that communicates with the client library. The final set of classes is the application I am building. The API classes are the classes I am having problems with.

If I deploy the API classes with my application, then they are loaded into the parent class loader and cannot make calls to the remote service (I load the Remote Service client at runtime into a URLClassLoader).

The solution I have come up with is to remove the API from my application, and load the API along with the remote service classes into a URLClassLoader at runtime. This seems to work.

Only one problem... All calls to the API have to made through reflection.

bearwiga at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 4

Well, the crucial part are probably the hard-coded symbolic references in the class files. Assume you have a class org.example.Server with a field of type org.example.Client. When Server is loaded, the symbolic reference to Client has to be resolved, which means that ultimately, a definition for Client has to be found. The JVM then uses the defining loader of class Server to initiate loading of class Client. There's no way to interfere with this, only inside the ClassLoader that loadClass(String) (or something) is invoked on can first take control. Basically, a "Class" is identified not only by its name, but also by its defining class loader, that is, by a tuple like (org.example.Client, DefiningClassLoaderOfClient). This is described in more detail in the links I provided, I thought I just repeat it here.

What matters is that there is no way to install a reference to a newly-loaded class in the client field of Server, since you cannot change the type of the field. Suppose your new class is identified by (org.example.Client, Clientv2URLClassLoader). Although both have the same binary name, they are not interchangeable (given that DefiningClassLoaderOfClient and Clientv2URLClassLoader are different loaders); they are indeed different classes. And you also cannot re-load an already defined class so that it sort of "becomes" a new class (redefinition is possible with recent VMs I think, but it's not feasible with class loaders alone).

What's needed is an interface that both long-living classes and new, probably short-lived classes can agree on. One that is loaded by a common parent loader accessible by all sub-loaders, and that doesn't change. Then the actual classes implementing this interface are exchangeable, and the classes using the interface do not need to know about the actual types. So in your architecture, you have to devise a way how to load and then use a "new", updated class, that is, how to change instance creation from "old" to "new" classes (e.g. factory), and maybe even how to replace interface references to "old" classes in long-living objects of your application. But you can't change the type/behaviour of existing instances of a class! The only way is to have newly-created instances of a new class, implementing a common interface. This approach is what _dnoyeB already brought up in his post, I guess...

Naturally, this requires some arrangements on your part and cooperation from your team developing the client library, as it has to implement the respective interfaces. If you have a solution that works with reflection and promises to satisfy your requirements, I'd probably go this way as it is more flexible, and seems less of a headache.

oeberta at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 5

> Well, I believe I have a soution, but it is complex

> (to me).

>

These things are complex.

> First, let me clear up my architecture a bit. I have

> three sets of classes I am working with. The first

> set of classes are a client library to a remote

> service. These are the classes I am trying to update

> dynamically. The second set of classes is an API that

> communicates with the client library. The final set

> of classes is the application I am building. The API

> classes are the classes I am having problems with.

I have done this myself with a JDBC library I let the client provide themselves.

>

> If I deploy the API classes with my application, then

> they are loaded into the parent class loader and

> cannot make calls to the remote service (I load the

> Remote Service client at runtime into a

> URLClassLoader).

>

True. That is why the API should be expresses as a set of interfaces. These interfaces are deployed with the application. The classes that implement the API are not statically referenced by the application. Is this what you have done?

> The solution I have come up with is to remove the API

> from my application, and load the API along with the

> remote service classes into a URLClassLoader at

> runtime. This seems to work.

>

> Only one problem... All calls to the API have to made

> through reflection.

remove the api classes, in their place add API interfaces. Then you wont need any reflection.

_dnoyeBa at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 6

So there's no way other than using reflection or casts to interfaces for working with runtime loaded classes?

When a class is added to the classpath by its classloader, you should be able to work with the class definition itself? like:

classLoader.loadClass("RuntimeAdded");

RuntimeAdded r = new RuntimeAdded();

r.someMethod();

What does the systemclassloader do to make this possible?

bitshita at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 7

> So there's no way other than using reflection or

> casts to interfaces for working with runtime loaded

> classes?

>

> When a class is added to the classpath by its

> classloader, you should be able to work with the

> class definition itself? like:

>

> classLoader.loadClass("RuntimeAdded");

> RuntimeAdded r = new RuntimeAdded();

> r.someMethod();

>

So when you compile a class that has in it a type defined as

"RuntimeAdded" but this type does not exist, what should the compiler do?

That is why you put in its place a compatible interface that DOES exist.

Essentially like so.

IRuntimeAdded r=(IRuntimeAdded) classLoader.loadClass("RuntimeAdded");

r.someMethod();

> What does the systemclassloader do to make this

> possible?

Nothing it can do.

_dnoyeBa at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 8

> So when you compile a class that has in it a type

> defined as

> "RuntimeAdded" but this type does not exist, what

> should the compiler do?

>

> That is why you put in its place a compatible

> interface that DOES exist.

> Essentially like so.

> > IRuntimeAdded r=(IRuntimeAdded)

> classLoader.loadClass("RuntimeAdded");

> r.someMethod();

>

But what if the classes where available at compile time, but not at loading time... The JVM hapily executes any class it can resolve through its classpath. But what if some classes that where available at compile time are downloaded dynamically at runtime... can they be resolved in the same manner as the classes that where already available at boot time (and thus already are added to the classpath)?

bitshita at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...
# 9

It does not matter if the class is 'available' at compile time. What matters is that the programmer chose to 'use' the class at compile time. As a result information for that class is present in all the compiled classes that reference it. It can not then choose to not be available at runtime. That will throw an exception.

Sometimes classes that are referenced at compile time are located remotely at runtime. This is nasty and I do NOT like it. I suggest it be avoided at all costs. However, this is how java applets work. So it is done. If you look at RMI though, sun has tried hard to get further and further away from doing this. Which I agree with.

Again, if the class is not present you get a runtime exception and it can occur anywhere that class is attempted to be used. VS. using classForName or loadClass in which case a checked exception is only possible to be thrown exactly where you make the loadclass or classForName method call.

_dnoyeBa at 2007-7-28 18:23:27 > top of Java-index,Core,Core APIs...