A lightweight plugin engine
Large applications very often provide plugin functionality. Stecker (which stands for “electrical plug” in Deutch) is a lightweight implementation of such plugin functionality that can be easily integrated to your project.
some/directory
)
<dependency>
<groupId>org.meridor.stecker</groupId>
<artifactId>stecker-plugin-loader</artifactId>
<version>${latest-version}</version>
</dependency>
This code will:
Path aDirectoryWithPlugins = Paths.get("some/directory");
PluginRegistry pluginRegistry = PluginLoader
.withPluginDirectory(aDirectoryWithPlugins)
.withExtensionPoints(ExtensionPoint1.class, ExtensionPoint2.class, ExtensionPoint3.class)
.withResourcesPatterns("glob:**/*.xml")
.load();
The plugin engine always throws PluginException. When dependency problems occur you can determine what went wrong using the following code:
try {
//Try to load plugins
} catch (PluginException e) {
Optional<DependencyProblem> problem = e.getDependencyProblem();
if (problem.isPresent()) {
List<Dependency> missingDependencies = problem.get().getMissingDependencies();
List<Dependency> conflictingDependencies = problem.get().getConflictingDependencies();
//Handle these lists somehow...
}
}
To easily create a plugin you need to use Maven plugin called plugin-generator:
<build>
<plugins>
<plugin>
<groupId>org.meridor.stecker</groupId>
<artifactId>stecker-plugin-generator</artifactId>
<version>${latest-plugin-version}</version>
<executions>
<execution>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
$ mvn clean package
A plugin is simply a jar file containing:
Plugin manifest fields are highly influenced by *.deb package description fields and are prefixed with Plugin-.
John Smith <john.smith@example.com>
The only required fields in manifest are Plugin-Name and Plugin-Version.
This section mainly relates to such plugin manifest fields as Plugin-Depends, Plugin-Conflicts and Plugin-Provides:
This notation means that current plugin depends on 3 plugins: some-plugin with version greater than or equal to 1.0; another-plugin 2.1.1 and one-more-plugin with version lower than 3.0. As you can see plugin name is separated from its version by
Plugin-Depends:some-plugin=[1.0,);another-plugin=2.1.1;one-more-plugin=(,3.0)
=
(equality) sign and different dependency requirements are delimited by ;
(semicolon). Dependency version is following Maven version specification rules and can be expressed in two ways:
Plugin-Provides:logger
1.1
[1.0,2.0)
[1.0, 1.2]
- between 1.0 and 1.2 including them, (,2.0)
- less than 2.0, [2.2, 3.0)
- greater than or equals to 2.2 but less than 3.0. An extension point can be an ordinary class (i.e. class or interface) or an annotation. In that case plugin loader returns classes that inherit from extension point class or are annotated by extension point annotation.
To build Stecker from source you need to have Java 8 and Maven 3.2+ installed. To run the build clone the source and type:
$ mvn clean install
Internally plugin engine is based on the following interfaces:
File aDirectoryWithPlugins = new File("some/directory");
PluginRegistry pluginRegistry = PluginLoader
.withPluginDirectory(aDirectoryWithPlugins)
.withPluginsProvider(new MyCustomPluginsProvider())
.withDependencyChecker(new MyCustomDependencyChecker())
.withClassesScanner(new MyCustomClassesScanner())
.withResourcesScanner(new MyCustomResourcesScanner())
.load();