Plugin is a set of functionality, i.e. Java classes packed into one or multiple JAR files. The Server loads and initializes such JARs.
Loading and initializing are two separate steps of the process.
At the first step a part of the server, the PluginLoader scans the JAR files for implementations of IPlugin. It uses PluginCreator to instantiate the Plugin by passing the instance of IBootstrap to it’s constructor. The load() method of each Plugin is called here.
In it’s load() method the Plugin adds it’s own IBootstrapItem into the IBootstrap known to him. The IBootstrapItem gives the name of the part of the functionality provided by the plugin and allows to declare dependencies from the other bootstrap items using the after() and before() methods. Finally the sequence of the bootstrap items according to the dependencies is constructed and their executeProcess() is called. This is the second step of the initialization.
Let create a sample plugin.
public class SamplePlugin implements IPlugin {
It must contain a reference to Bootstrap provided during initialization process.
private final IBootstrap<IBootstrapItem<String>> bootstrap;
The Bootstrap must be injected into the plugin’s constructor.
public SamplePlugin(final IBootstrap<IBootstrapItem<String>> bootstrap) { this.bootstrap = bootstrap; }
You have to define the load() method.
@Override public void load() throws PluginException {
In the method at the first you should declare the BootstrapItem provided by your plugin.
IBootstrapItem<String> item = new BootstrapItem("SamplePlugin");
Here you declare that your item must be initialized after initialization of ‘IOC’ item.
item.after("IOC");
Then you define your plugin initialization code. For example, let register some new strategy in IOC.
item.process(() -> { try { IKey key = Keys.getOrAdd("new SampleClass"); IOC.register( key, // it's the initialization action of our plugin new CreateNewInstanceStrategy( (args) -> new SampleClass((String) args[0]) ) ); } catch (Exception e) { throw new RuntimeException(e); } System.out.println("SamplePlugin initialized"); });
On the server side the loading of the plugin may looks as the following.
The server initializes the Bootstrap to handle the initialization sequence.
IBootstrap bootstrap = new Bootstrap();
Also it uses PluginCreator to find and call the correct constructor of IPlugin.
IPluginCreator creator = new PluginCreator();
A IPluginLoaderVisitor is necessary to track plugins loading process.
IPluginLoaderVisitor<String> visitor = new SamplePluginVisitor();
Because the plugin jars can be located in different directories and can be added dynamically, the special kind of ClassLoader is required.
ClassLoader urlClassLoader = new ExpansibleURLClassLoader(new URL[]{}, ClassLoader.getSystemClassLoader());
Then the PluginLoader is created. It’s second constructor parameter is the code to load each plugin.
IPluginLoader<Collection<IPath>> pluginLoader = new PluginLoader( urlClassLoader, (t) -> { try { IPlugin plugin = creator.create(t, bootstrap); plugin.load(); } catch (Exception e) { throw new RuntimeException("Could not create instance of IPlugin", e); } }, visitor );
In this example, when we have two plugins: ‘SamplePlugin’ following after ‘IOC’ — the loading and initialization sequence is like this:
Note the order of first two steps is not defined, while the order of execution of bootstrap items is defined by their dependencies.
You can check the full source codes of this example here.