Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / July 2005

Tip: Looking for answers? Try searching our database.

ClassLoaders, delegation, and a plugin system

Thread view: 
Chris Head - 29 Jul 2005 20:16 GMT
Hello everyone,
I'm currently trying to figure out how to write an application that uses
a plugin-type architecture with a custom-built instance of
URLClassLoader to load the plugins. I have run into a few problems.

First, as a minimal example, consider file a.jar containing classes A
and Launcher, and file b.jar containing class B. I launch the virtual
machine with only a.jar in the classpath, invoking the Launcher class.
The Launcher class's main() method instantiates a URLClassLoader, giving
it b.jar as a classpath.

The Launcher class then uses ClassLoader.loadClass() to reflectively
load class B and call a static method on it. This works fine. Class B
can then use Class.forName() (2 args) to reflectively load class A and
call a method on it.

The first problem arises if I try to make this work in the opposite
direction. If I use ClassLoader.loadClass() to reflectively load A
instead and call a static method on it, and that static method uses
Class.forName() (2 args) to load B, the load fails. I believe this is
because ClassLoader.loadClass() is delegating to the system ClassLoader
(as it should), so A is getting loaded by the system ClassLoader, which
cannot find B. Class.forName() (2 args) is defined as using the same
ClassLoader which loaded the calling class (in this case, the system
ClassLoader). I understand that I could solve this by setting my custom
ClassLoader as the thread's context ClassLoader and using it directly in
A, but I don't know if this is the "right" solution, and it doesn't
solve my other problems, described below.

My second problem is what to do if A refers to B statically, rather than
reflectively. In this case, there is no chance for A to use whatever
ClassLoader it wishes. Setting the context ClassLoader has no effect
here. I cannot figure out how to solve this problem. No matter what
happens, A cannot see B, because A is loaded (even if only by
delegation) by the system ClassLoader.

I have a third problem which I haven't really explored very much yet,
but which is probably going to hit me really hard sometime: what the
heck does serialization do to all this? What if I want to load a
serialized version of a B object? How do I make ObjectInputStream use my
custom ClassLoader?

The only solution I can think of so far is to use a kind of proxy
launcher. I have a lightweight launcher.jar file with Launcher in it and
is alone on the command-line classpath. The Launcher in here creates a
custom ClassLoader pointing at both the main application jar and the
plugin jars, then calls the main application's Launcher. This way, the
main application's Launcher is loaded by the custom ClassLoader (the
system ClassLoader can't see it), and my problems "go away". It feels
like jumping through a lot of hoops though. Is there an easier way?

If my explanations are not good enough, I can provide compilable code
samples.

One final note: Including a.jar in my custom URLClassLoader's classpath
changes nothing. URLClassLoader delegates (as it's documented to) to the
system ClassLoader FIRST, so only if the system ClassLoader cannot find
a class does the custom loader start working.

Thank you in advance,
Chris
Chris Smith - 29 Jul 2005 21:24 GMT
> My second problem is what to do if A refers to B statically, rather than
> reflectively. In this case, there is no chance for A to use whatever
> ClassLoader it wishes. Setting the context ClassLoader has no effect
> here. I cannot figure out how to solve this problem. No matter what
> happens, A cannot see B, because A is loaded (even if only by
> delegation) by the system ClassLoader.

I'm not sure what you're trying to do here.  If A refers to B
statically, there is no point whatsoever in making A available to the
system classloader without making B available to the system classloader
as well.  It will be impossible to ever load the class A, because of
that static reference.  Is A part of the plugin, or part of the
framework?  If it's part of the plugin, then it should be packaged with
the plugin.  If it's part of the framework, then it doesn't make sense
to want to statically reference a class from the plugin.  That static
references makes your plugin no longer a plugin at all, and you may as
well not bother with the custom classloader.

I'm racking my brain trying to figure out what you might be trying to do
that would break this.  The only thing that comes to mind is this.  Are
you requiring that all your plugins define classes with the same fully
qualified name (B)?  If so, then you'd be well-advised to abandon that
approach (the dlopen approach to plugins) in favor of letting the plugin
JAR file specify the name of the main plugin class using either a
resource in the JAR file or in the manifest.  Then your framework would
never statically reference any plugin class, but would instead call
ClassLoader.loadClass, then Class.newInstance, and then cast the result
to a known superinterface.

> I have a third problem which I haven't really explored very much yet,
> but which is probably going to hit me really hard sometime: what the
> heck does serialization do to all this? What if I want to load a
> serialized version of a B object? How do I make ObjectInputStream use my
> custom ClassLoader?

Good question!  A quick Google search suggests that you should extend
ObjectInputStream and override the resolveClass method... but I don't
really know anything more about this than you do.  Try that out, and ask
again if it doesn't work.

> The only solution I can think of so far is to use a kind of proxy
> launcher.

You should not need to do this.  The question is why your framework
(application) code has a static reference to the plugin code.  Once
that's settled, the rest will fall into place.

Signature

www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation

Chris Head - 29 Jul 2005 21:46 GMT
[snip]

> I'm not sure what you're trying to do here.  If A refers to B
> statically, there is no point whatsoever in making A available to the
[quoted text clipped - 6 lines]
> references makes your plugin no longer a plugin at all, and you may as
> well not bother with the custom classloader.

Well, the static reference thing was a bit of a tangent actually; I
*don't* plan to have a static reference to a plugin class in my main
app. I agree, that would be silly in just about every case.

[snip]

> Good question!  A quick Google search suggests that you should extend
> ObjectInputStream and override the resolveClass method... but I don't
> really know anything more about this than you do.  Try that out, and ask
> again if it doesn't work.

Thanks. I see that it should be quite straightforward. I hadn't done any
Googling because my code is not yet at the point of serialization. I
just wondered if anyone knew offhand whether it would come and hit me or
if the solution is easy.

>>The only solution I can think of so far is to use a kind of proxy
>>launcher.
>
> You should not need to do this.  The question is why your framework
> (application) code has a static reference to the plugin code.  Once
> that's settled, the rest will fall into place.

I'm not 100% confident with my knowledge of ClassLoaders yet. Somehow it
feels "unnatural" passing instances of classes that come from different
loaders to each other, but that's just me. Once I get things organized I
guess it'll all work out fine, using the custom ClassLoader to load all
plugin classes and making sure than any reference from the application
to a plugin uses the appropriate custom ClassLoader.

Thanks again,
Chris


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.