Sunday, December 9, 2007

Java: How to load class dynamically (on fly)?

I face a problem of loading class dynamically in JAVA. How to do this?
After do research in internet with many site, i finally find out the way to do this.
In order to save your time on researching problem, i will show you how to do this.

To understand the framework, we have to know following knowledge:
1. Class loader and how does it work

So how does class loader work:



As shown in Figure 1, the bootstrap class loader (BS) -- Native implemented into JVM -- loads the classes from the JVM, as well as extensions to the JDK. The system class loader (CP) loads all of the classes provided by the CLASSPATH environment variable or passed using the -classpath argument to the java command. Finally we have several additional class loaders, where A1-3 are children of the CP, and B1-2 are children of A3. Every class loader (except BS) has a parent class loader, even if no parent is provided explicitly; in the latter case, the CP is automatically set as the parent.

Rule for class loader:

1. lass loaders are hierarchically organized, where each one has a parent class loader, except the bootstrap class loader (the root).

2. Class loaders should (practically: must) delegate the loading of a class to the parent, but a custom class loader can define for itself when it should do so.

3. A class is defined by its class type and the effective class loader.

4. A class is only loaded once and then cached in the class loader to ensure that the byte code cannot change.

5. Any symbolic links are loaded by the effective class loader (or one of its ancestors), if this is not already done. The JVM can defer this resolution until the class is actually used.

6. An upcast of an instance to another class fails when the class of the instance and the class of the symbolic link do not match (meaning their class loaders do not match).

But another problem is: Classes are loaded by many class loader. How can it distinguish them? JAVA do it by combine qualified name: (Class name, Package name, Class loader) --> With the same classes, but loaded by different class loader, it can not be used together. We can check it easily by using URLClassLoader and sun.misc.Launcher$AppClassLoader(class loader which load main function).

When i work with JAX-WS, i see that we can dynamically add custom handler to WS, and JAX-WS will call our handler. It make me think a lot. How can it do this?
The answer is: JAXWS has its own class loader. And the class loader will find and call our handler.

Now we will make a framework which can allow us load custom handler like JAX-WS at run time.

1. We define a interface for handler. This handler is packaged in jar file.
2. User will define class which implemented the interface handler. The implement class is packaged in other jar file.
3. Our framework will scan those jar file and load all custom handlers, and call it sequentially.

The most difficult part is load all custom handlers and call it at run time.
To solve the problem, we will make our own class loader:


public class CustomLoader extends URLClassLoader
{
static public ClassLoader MainClassLoader = null;

protected CustomLoader (URL[] urls, ClassLoader parent) {
super(urls, parent);
}

public Class loadClass(String name) throws ClassNotFoundException {
//Get system class loader
try {
if (MainClassLoader != null) {
return MainClassLoader.loadClass(name);
} else {
return super.loadClass(name);
}
} catch (ClassNotFoundException e) {
return super.loadClass(name);
}
}

protected Class findClass(String name) throws ClassNotFoundException {
Class clas = super.findClass(name);
return clas;
}
}


The variable MainClassLoader point to sun.misc.Launcher$AppClassLoader( the class loader which call our main function)

How to used this custom class loader?


void main() {
ClassLoader base = ClassLoader.getSystemClassLoader(); //get the boot trap class loader
CustomLoader loader = new CustomLoader(urls, base.getParent()); //create our class loader
loader.MainClassLoader = Thread.currentThread().getContextClassLoader(); //set the //application class loader to our class loader. We have to do this because when java //compile and link our code, it used class loader: sun.misc.Launcher$AppClassLoader.

Class handlerClass = loader.loadClass("CustomHandler");
Object handler = handlerClass.newInstance(); (1)
Handler customHandler = (Handler)handler; (2)
}


(1) --> implement class is loaded by our custom class loader.
(2) --> Handler is loaded by sun.misc.Launcher$AppClassLoader

Thank to this code:

public Class loadClass(String name) throws ClassNotFoundException {
//Get system class loader
try {
if (MainClassLoader != null) {
return MainClassLoader.loadClass(name);
} else {
return super.loadClass(name);
}
} catch (ClassNotFoundException e) {
return super.loadClass(name);
}
}

We delegate loading class to MainClassLoader first: because in linkage phase, MainClassLoader load all symbols in our source code. If we do not do this, we will violate the rule 6. If MainClassLoader does not load them, we will delegate to parent or we violate rule 2. If both do not load class, we can do it by ourselves. But in this case we do not implement this(for simple).

So with our custom class loader, (2) can be used without ClassCastException.

So i hope , you can make a framework easily with the custom class loader as i explain.

2 comments:

Anonymous said...

This article is of great value.
Its very usefull too..
Thanks very much.

One question. I have a requirement where i should load the classes which should be encrypted, and when the request comes for that particular servlet or java files the jvm should automatically call CUSTOM CLASS LOADER and this loader will decrypt and execute the class.

In the sample provided by you the user has to manually call the CUSTOM CLASS LOADER, but how to make JVM call CUSTOM CLASS LOADER automatically from an application server (jboss) automatically, without we calling the main class and that too we have to pass our class file name to the CUSTOM CLASS LOADER to perform execution. But how to make jvm call automatically CUSTOM CLASS LOADER which does decryption of class files and loads the class and exectue.. I mean to say by just modifying some parameters in Application server or by some code in application server that tells jvm Which classloader to be called automatically that loads the classes when ever its needed and not by us.

Can this be done..
Waiting for ur Post.

CABA LAM said...

Application server class and library first loaded by bootstrap class loader. Then application with their own class loaders will load class of our application. So how to do it depends on how application server is implemented. If you want your class loader is call automatically when load class, you can see the way JAXB, JAXWS is loaded without endorse. JRE also has JAXWS, JAXB. Therefor if you want to use other version of JAXB, JAXWS you have 2 way: endorse or use custom class loader. As i remember we have to make a mask class loader and custom class loader. I think your requirement can be done. You can refer to artical of Koshuke, author of jaxws, jaxb in java.net.

Google