views:

257

answers:

2

I am writing a library where I allow people to provide implementations of certain interfaces using a plugin framework (it's JPF if you're familiar). The plugins are not stored in the classpath. The framework gives me a ClassLoader for each plugin, so when implementation named "MyImpl" of interface "MyInterface" is requested, I can find the correct plugin, and then use that plugin's ClassLoader to load the class, from which I can make an instance if I know something about the constructor. So far so good.

However, now I have a case where I need to call a method that is only available on that particular implementation. So, there are two ways I could try to do this:

Method 1:

// Makes sure that MyImpl has been loaded, using the custom classloader
Plugins.getClass(MyInterface.class, "MyImpl");
// This line will not compile because MyImpl is not available at build time
MyImpl foo = new MyImpl();
// If I could get this far, this line would work:
foo.methodOnlyInMyImpl();

Method 2:

// This call will get an instance of MyImpl (already written and tested)
MyInterface foo = Plugins.getInstance(MyInterface.class, "MyImpl");
// Compiler error because there is no MyInterface.methodOnlyInMyImpl method.
foo.methodOnlyInMyImpl()

Method 1 is the cleaner of the two, as it is most similar to how you would write the code if the class were "normal" and not accessible via a plugin. However, neither compiles.

Options I've come up with so far:
A. Use Method 2, but use reflection to do the methodOnlyInMyImpl method call (please, no!)
B. Place the plugin classes in the build path and then use Method 1, which would compile. (my current favorite)
C. B + when plugins are installed, copy the classfiles to another directory that is in the classpath, so the system classloader can load them (causes other problems)

So, my questions are:

  1. Am I missing another idea that's better?
  2. If I do B, will I have problems at runtime? After all, the class using MyImpl will presumably have been loaded using the system classloader. So, as soon as it sees MyImpl foo, won't it try to load MyImpl using the system classloader, which will fail (even though the Plugins.newInstance call would provide an instance of MyImpl)?
+3  A: 

First, what advantage you get from the plugin mechanism, when you need to implement against the real implementation? The plugin should implement an interface and you can use the implementation via the interface.

I am not fimilar with JPF, but java classes are never compatible when loaded by different classloaders. But there are two possible ways:

  1. The interface is in your classloader, the plugin classloader has your classloader as parent, so its interface is the same as yours. The code 2 should work with this, when the method is decladed in the interface.

  2. You can use serialisation. This is a limited way more usefull transfering data objects between independent classloaders. I needed to use this for cross context dispatching with request parameter between two webapps.

Arne Burmeister
An example to illustrate the advantage: I have interfaces X and Y. There is an X.getY() method that returns a Y. I have four plugins that provide X implementations Xa and Xb and Y implementations Yc and Yd. Now, Xa and Xb both need to use implementation Yc internally, so this is where I need the specific implementation. The application can just interact with Xs and Ys, but the plugins need particular implementations. JPF helps handle the plugin dependencies.
Michael Rusch
+1 - the OP is attempting to ignore the contract implied by the use of interfaces. If the plugins are the only ones that care about implementation classes, then s/he will have to provide compile-time access to the implementation class, use a cast, and handle the possible ClassCastException.
kdgregory
Unfortunately, I can't anticipate all of the interfaces. Serialization (Arne), casting at runtime (kdgregory), and TransLoader (Carl) all require the class to be somehow available in the current classloader, which it is not in my case--it came down to using reflection or putting requirements on runtime classpath to include plugin dirs. I chose the latter. Accepting this answer because of the key phrase "java classes are never compatible when loaded by different classloaders". Thanks, I'd +1 if I had the rep.
Michael Rusch
+1  A: 

There's a library called TransLoader that was mentioned in a previous question. Here's the URL for the source: http://code.google.com/p/transloader/ .

Carl Smotricz
This was helpful and would help if my reqs were different. Thanks. I'd +1 if I had the rep.
Michael Rusch
+1 back atcha, and happily I do have the rep. I'm sorry I wasn't able to more closely discern and meet your requirements.
Carl Smotricz