C
Chris Head
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (MingW32)
iD8DBQFC6oCi6ZGQ8LKA8nwRAmEsAJ95vrpmYfUGNsN2l5ermfyd1BA05ACfbm5c
+tnpueT8GKQotZARxlX49Og=
=z8O2
-----END PGP SIGNATURE-----
Hash: SHA1
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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (MingW32)
iD8DBQFC6oCi6ZGQ8LKA8nwRAmEsAJ95vrpmYfUGNsN2l5ermfyd1BA05ACfbm5c
+tnpueT8GKQotZARxlX49Og=
=z8O2
-----END PGP SIGNATURE-----