I have been working on a software with a plugin based system, where users can write their own plugins. I am very new to JMPS, but I would like to make this using JMPS and not OSGi. Made a separate API module and even created a Test Plugin.
The plugins are stored with the filename "someplugin.jar" in a directory.
How do I load all these jars (none of them are automodules but well-defined modules with module-info.class) during runtime? The reason I want to load them dynamically during runtime is that the user will be having an option to change the directory to search for plugins, and change it without having to restart the application.
To load modules dynamically, you need to define a new ModuleLayer
. The new module layer will inherit the boot layer:
This means that in your boot layer (where your main module is), you cannot directly refer to classes in the plugins layer. However, you can use your plugins layer through services.
Here is the code that you can use as a starting point:
Path pluginsDir = Paths.get("plugins"); // Directory with plugins JARs
// Search for plugins in the plugins directory
ModuleFinder pluginsFinder = ModuleFinder.of(pluginsDir);
// Find all names of all found plugin modules
List<String> plugins = pluginsFinder
.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.collect(Collectors.toList());
// Create configuration that will resolve plugin modules
// (verify that the graph of modules is correct)
Configuration pluginsConfiguration = ModuleLayer
.boot()
.configuration()
.resolve(pluginsFinder, ModuleFinder.of(), plugins);
// Create a module layer for plugins
ModuleLayer layer = ModuleLayer
.boot()
.defineModulesWithOneLoader(pluginsConfiguration, ClassLoader.getSystemClassLoader());
// Now you can use the new module layer to find service implementations in it
List<Your Service Interface> services = ServiceLoader
.load(layer, <Your Service Interface>.class)
.stream()
.map(Provider::get)
.collect(Collectors.toList());
// Do something with `services`
...
Module layers are considered an advanced topic but I don't find it really difficult. The only key point you need to understand is that module layers are inherited. This means that from a child layer, you can only refer to classes of the parent layer but not vice versa. To do the opposite, you have to use the inversion of control which is implemented in the Java module system by ServiceLoader
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With