The simplest solution to this problem is to use ServiceLoader
(doc here). It is included in Java, and is fairly simple to use:
- Load Jar file(s) at runtime
- Detect classes that implement a particular interface (e.g.:
your.package.YourService
).
- Instantiate objects from these classes.
Here is a pretty good post describing how to do so (Note: you should use the second proposal with URLCLassLoader
; not extend the classpath dynamically). Also, do not forget to declare your services inside the Jar's META-INF directory:
If com.example.impl.StandardCodecs
is an implementation of the
CodecSet
service then its jar file also contains a file named
META-INF/services/com.example.CodecSet
This file contains the single line:
com.example.impl.StandardCodecs # Standard codecs
By choosing this approach, your core program will naturally have a handle to your plugins, so will be able to communicate with them easily. To ensure the bi-directional communication (i.e. plugins calling your core program), I would suggest to create an interface that your core program will implement, and pass that interface to your plugins.
Plugin interface:
public interface Plugin {
public void doPluginStuff(Caller caller);
}
Core program Caller
interface:
public interface Caller {
public void sendBackResults(Object results);
}
Plugin implementation (in separate Jar file):
public class AParticularPlugin implements Plugin {
public void doPluginStuff(Caller caller){
caller.sendBackResults("Hello world");
}
}
Core program:
public class CoreProgram implements Caller {
public void callPlugin(URL[] urlToJarFiles){
URLClassLoader ucl = new URLClassLoader(urlToJarFiles);
ServiceLoader<Plugin> sl = ServiceLoader.load(Plugin.class, ucl);
Iterator<Plugin> plugins = sl.iterator();
while (plugins.hasNext())
plugins.next().doPluginStuff(this);
}
public void sendBackResults(Object results){
System.out.println(results.toString());
}
}