ClassLoader delegation and instanceof
Hi there !
I have a framework with an sdk allowing people to develop some kind of plugins.
I provide a generic framework in a package (A).
Independently, I provide an sdk which basically consist in a package (B) including the class CXXX.
The customer then can implement its own class CXXXImpl which inherits from CXXX (CXXX also implements IXXX).
At runtime:
- in the manifest of my app (A) I have an entry pointing to (B)
- so I'm using my default class loader to access CXXX
- one of my customer gives me an external jar including the class CXXXimpl_n which inherits from CXXX
- I can use an URLClassloader to load CXXXImpl_n
- all of this works fine
...BUT...
Then, I want to:
1) first ensure that CXXXImpl_n inherits properly from CXXX
but this does not work because CXXX and CXXXImpl_n are not loaded using the same classloader
2) cast CXXXImpl_n as CXXX and access the CXXX known methods (I could use also interfaces but the problem would be the same)
I do not really understand very well the delegation mecanism.
Here is my (pseudo)code:
File totoImplJarFile =new File("toto/" + jarName);
URL totoImplURL = totoImplJarFile.toURI().toURL();
URLClassLoader jarLoader = URLClassLoader.newInstance(new URL[]{totoImplURL});// should normally delegate first to the default classloader as far as I understood !?
String mainClassName = this.getMainClassName(totoImplURL);
Class launcherMainClass = jarLoader.loadClass(mainClassName);
Object launcherMainClassObject = totoMainClass.newInstance();
if (totoMainClassObjectinstanceof CXXX){
...
CXXX xxx = (CXXX)totoMainClassObject;
xxx.doThings();
xxx.doThings();
...
}
Currently, the instanceof returns false even if the class is a child of CXXX. This is because both are not loaded using the same classloder... but what would you recommend to get it working ? I though the delegation would have allowed to do so... but it sounds it's not the case :(
Could you explain please ?
Cheers,
Eg\\*
[2448 byte] By [
egavaldoa] at [2007-11-27 2:54:04]

Hi Eg,
Instead of trying to get the default class loader like this:
URLClassLoader.newInstance(new URL[] {totoImplURL});
What about this:
this.getClass().getClassLoader();
I am just guessing - but when I read URLClassLoader.newInstance
my first thought was that newInstance might make a new loader rather than give you the default one..
Hope you work it out..
Rob
:)
On the face of it it should work, because your CXXX class should be loaded by the default loader, and that should be the parent of the URLClassLoader.
Try making that explicit i.e.
URLClassLoader.newIntance(new URL[]{totoImplURL}, CXXX.class.getClassLoader());
Thx for your replies !
Hum... I tried this:
jarLoader = new URLClassLoader(new URL[] {totoImplURL});
instead of this:
jarLoader = URLClassLoader.newInstance(new URL[] {totoImplURL});
but there is no progress :(
If I display the 2 classloaders like this:
System.out.println("-classloader 1 = " + launcherMainClassObject.getClass().getClassLoader());
System.out.println("-classloader 2 = " + CLauncher.class.getClassLoader());
if (launcherMainClassObject instanceof CLauncher) {
...
}
I get this:
-classloader 1 = java.net.URLClassLoader@6295eb
-classloader 2 = sun.misc.Launcher$AppClassLoader@df6ccd
and the instanceof returns false (of couse !).
I also tried to give explicitely the default class loader as parent while instanciating the URLClassLoader: no change :(
Any other Ideas ?
Eg\\*
You'd expect the plugin to have a different class loader, but still be connected to the superclass loaded by the default ClassLoader. Try laucherMainClass.getSuperclass().getClassLoader();
There is something wrong with your code, instanceof will work as expected regardless of whether the class was loaded with a different classloader.
> There is something wrong with your code, instanceof
> will work as expected regardless of whether the class
> was loaded with a different classloader.
Not if the superclass which the instanceof operator refers to is also from a separate ClassLoader. Normally when a plugin is loaded from a URLClassLoader the superclass load is delegated to the default ClassLoader, and so instanceof works.
> Not if the superclass which the instanceof operator
> refers to is also from a separate ClassLoader.
> Normally when a plugin is loaded from a
> URLClassLoader the superclass load is delegated to
> the default ClassLoader, and so instanceof works.
If you are doing: x instanceof y
I think it is only if the x objects class was loaded by the boot classloader and y has not been loaded by the boot classloader that it will return false.
Classes actually loaded by different class loaders are considered distinct, even if they have the same FQN. So if you've loaded your plugin and it's superclass pointer points to a class loaded from the URLClassLoader then the instanceof will fail.
If the URLClassLoader is delegating correctly this shouldn't happen because the superclass reference in the plugin should be delegated to the default ClassLoader.
> If the URLClassLoader is delegating correctly this> shouldn't happen because the superclass reference in> the plugin should be delegated to the default> ClassLoader.Right, so it will work as expected.
Thanks again !
Hum... I don't follow you here.
let's resume the statements:
- launcherMainClass = the class of the plugin as developped by the customer (provided in an external jar)
- CLauncher = the superclass inherited (loaded by the default class loader)
the sniplet you're suggesting...
launcherMainClass.getSuperclass().getClassLoader();
...will return the classloader that loaded the superclass of the plugin (so basically the default class loader right ?) but what can I do with that ?
Are you suggesting that I use this like this:
jarLoader = new URLClassLoader(new URL[] {launcherImplURL}, launcherMainClass.getSuperclass().getClassLoader());
I can understand the idea to connect the URLClassLoder to the specific classloader that loaded the superclass... but the problem is that at that time I don't know launcherMainClass ! for the good reason that this is the jarLoder that allow me to find it ;)
I may have missed something around... can you clarify what you meant ? Thanks again...
Eg\\*
>..will return the classloader that loaded the superclass of the plugin (so
>basically the default class loader right ?) but what can I do with that ?
Check it, and see if it's the same as the classloader that loaded your main class. Print it and check for equality.
Is it possible that you've changed the package name of the superclass or something, resulting in either the plugin or the framework using another version (maybe still in the .class tree)? Print launcherMainClass.getSuperclass().getName() too.
No idea about a possible change in the package structure...
I added some traces with the following code:
System.out.println("- - - classloader plugin= " + launcherMainClassObject.getClass().getClassLoader());
System.out.println("- - - classloader plugin's parent = " + launcherMainClass.getSuperclass().getClassLoader());
System.out.println("- - - classloader CLauncher= " + CLauncher.class.getClassLoader());
System.out.println("- - - classloader this = " + this.getClass().getClassLoader());
/********** Double-check that it is really a launcher *********/
if (launcherMainClassObject instanceof CLauncher) {
System.out.println("- - - - - thread good instance");
CLauncher launcher = (CLauncher)launcherMainClassObject;
...
} else {
System.out.println("- - - - - thread incorrect cast !!!!! " + launcherMainClassObject.getClass());
return;
}
This gives the following result:
- - - classloader plugin= java.net.URLClassLoader@1982fc1
- - - classloader plugin's parent = sun.misc.Launcher$AppClassLoader@df6ccd
- - - classloader CLauncher= sun.misc.Launcher$AppClassLoader@df6ccd
- - - classloader this = sun.misc.Launcher$AppClassLoader@df6ccd
- - - - - thread incorrect cast !!!!! class com.toto.titi.tutu.CLauncherImpl
So it is verified that the superclass is well loaded using the default class loader. Only the plugin is loaded through the URLClassLoader using:
jarLoader = new URLClassLoader(new URL[] {launcherImplURL}, CLauncher.class.getClassLoader());
Hum... it sounds like delegation does not work or I'm using something incorrectly. Any experience with classloader delegation with Jdk 1.6.0 ?
Eg\\*
But I am thinking about something:
when the customer compile it's implementation, he uses the library provided in the sdk. Am I right in saying that if for any reason the class is not exactely the same at runtime this may be a problem ?
So the solution would be to have the customer embedding CLauncher in its jar. So I could use its implementation AND CLauncher from its jar.
It sounds a good idea to me... any objection ?
Then the code would have some additional lines like:
Class cLauncherClass= jarLoader.loadClass("CLauncher");
Object cLauncherClassObject = cLauncherClass.newInstance();
// instanceof replaced by isInstance
if (cLauncherClass.isInstance(launcherMainClassObject)) {
cLauncherClassObject = cLauncherClass.cast(launcherMainClassObject);
LauncherClassObject.method1();
LauncherClassObject.method2();
LauncherClassObject.method3();
...
}
I'm just wondering how to call the methods method1() this should be trivial but I didn't find it anywhere...
Eg\\*
I just found it:
/********** Double-check that it is really a launcher *********/
if (cLauncherClass.isInstance(launcherMainClassObject)) {
System.out.println("- - - - - thread good instance");
//CLauncher launcher = (CLauncher)launcherMainClassObject;
cLauncherClassObject = cLauncherClass.cast(launcherMainClassObject);
System.out.println("- - - - - thread xxx 1");
try {
Method method = cLauncherClass.getMethod("setConfiguration", new Class[]{Class.forName("org.w3c.dom.Node")});
Object[] params = {categorySessionData.getCategorySessionConf()};
method.invoke(cLauncherClassObject,params);
} catch (Exception e) {
CTraceUtils.trace(LOG_PRIORITY_SEVERE, LOG_PROPERTIES, LOG_PROPERTIES_LABEL, "couldn't execute setConfiguration() on the launcher !");
}
etc.
But it may be a bit boring if I have alot of methods to call :(
Eg\\*
That's a nasty work-arround which definitely shouldn't be necessaray - this use of plugin objects being cast to superclass or interface is quite routine.
ok.. I found the problem !!!
Very simple: the superclass was present, as expected, in the lib but also in the plugin - due to a problem in the ant script that compile the plugin against the sdk lib.
If I remove the superclass from the plugin (there is no reason for it to be there!), then it works fine:
- customer's implementation class is loaded for the URLClassLoader
- all classes from the lib are loaded from the default loader
Not sure about why the fact to have a duplicate of the superclass in the plugin makes a problem... anyway... thanks for your feedbacks it definitely helped !
Cheers,
Eg\\*
PS: This makes me wondering how to deal with plugin versionning. How to warn the user at runtime that his plugin is not in sync with a newer lib ?