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:
- Am I missing another idea that's better?
- 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)?
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:
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.
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.