ClassLoaders, delegation, and a plugin system

Discussion in 'Java' started by Chris Head, Jul 29, 2005.

  1. Chris Head

    Chris Head Guest

    -----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-----
     
    Chris Head, Jul 29, 2005
    #1
    1. Advertising

  2. Chris Head

    Chris Smith Guest

    Chris Head <> wrote:
    > 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.

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

    Chris Smith - Lead Software Developer/Technical Trainer
    MindIQ Corporation
     
    Chris Smith, Jul 29, 2005
    #2
    1. Advertising

  3. Chris Head

    Chris Head Guest

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Chris Smith wrote:
    > Chris Head <> wrote:
    >


    [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
    > 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.


    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
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.2.1 (MingW32)

    iD8DBQFC6pWg6ZGQ8LKA8nwRAkENAJ40Bfkq9b929SzGlDD5StK2GeKHkwCdH4hA
    /LMvZtOGQAQgphf56D5VCmQ=
    =7zU/
    -----END PGP SIGNATURE-----
     
    Chris Head, Jul 29, 2005
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Luther Baker

    ClassCastException and ClassLoaders

    Luther Baker, Sep 9, 2003, in forum: Java
    Replies:
    3
    Views:
    994
    Luther Baker
    Sep 10, 2003
  2. Michael Brown
    Replies:
    3
    Views:
    4,108
    John C. Bollinger
    Aug 9, 2004
  3. Erik Berglund

    Tomcat and classloaders

    Erik Berglund, Sep 6, 2004, in forum: Java
    Replies:
    7
    Views:
    3,453
    Albretch
    Sep 18, 2004
  4. Replies:
    3
    Views:
    351
    John C. Bollinger
    Feb 7, 2005
  5. Sam Roberts
    Replies:
    4
    Views:
    346
    Sam Roberts
    May 7, 2008
Loading...

Share This Page