Fork me on GitHub

It’s very simple to add PF4J in your application:

public static void main(String[] args) {
    ...

    PluginManager pluginManager = new DefaultPluginManager();
    pluginManager.loadPlugins();
    pluginManager.startPlugins();

    ...
}

In above code, I created a DefaultPluginManager (it’s the default implementation for PluginManager interface) that loads and starts all active(resolved) plugins.

Each available plugin is loaded using a different java class loader, PluginClassLoader.

The PluginClassLoader contains only classes found in PluginClasspath (default classes and lib folders) of plugin and runtime classes and libraries of the required/dependent plugins. This class loader is a Parent Last ClassLoader - it loads the classes from the plugin’s jars before delegating to the parent class loader.

The plugins are stored in a folder. You can specify the plugins folder in the constructor of DefaultPluginManager. If the plugins folder is not specified then the location is returned by System.getProperty("pf4j.pluginsDir", "plugins").

The structure of plugins folder is:

  • plugin1.zip (or plugin1 folder)
  • plugin2.zip (or plugin2 folder)

In plugins folder you can put a plugin as folder or archive file (zip). A plugin folder has this structure by default:

  • classes folder
  • lib folder (optional - if the plugin used third party libraries)

The plugin manager searches plugins metadata using a PluginDescriptorFinder.

DefaultPluginDescriptorFinder is a “link” to ManifestPluginDescriptorFinder that lookups plugins descriptors in MANIFEST.MF file.

In this case the classes/META-INF/MANIFEST.MF file looks like:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: decebal
Build-Jdk: 1.6.0_17
Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin
Plugin-Dependencies: x, y, z
Plugin-Id: welcome-plugin
Plugin-Provider: Decebal Suiu
Plugin-Version: 0.0.1

In above manifest I described a plugin with id welcome-plugin, with class org.pf4j.demo.welcome.WelcomePlugin, with version 0.0.1 and with dependencies to plugins x, y, z.

NOTE: The plugin version must be compliant with Semantic Versioning (PF4J uses jsemver as implementation for SemVer because it comes with support for comparing versions)

You can define an extension point in your application using ExtensionPoint interface marker.

public interface Greeting extends ExtensionPoint {

    String getGreeting();

}

Another important internal component is ExtensionFinder that describes how the plugin manager discovers extensions for the extensions points.

DefaultExtensionFinder looks up extensions using Extension annotation.

DefaultExtensionFinder looks up extensions in all extensions index files META-INF/extensions.idx. PF4J uses Java Annotation Processing to process at compile time all classes annotated with @Extension and to produce the extensions index file.

public class WelcomePlugin extends Plugin {

    public WelcomePlugin(PluginWrapper wrapper) {
        super(wrapper);
    }

    @Extension
    public static class WelcomeGreeting implements Greeting {

        public String getGreeting() {
            return "Welcome";
        }

    }

}

In above code I supply an extension for the Greeting extension point.

You can retrieve all extensions for an extension point with:

List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
for (Greeting greeting : greetings) {
    System.out.println(">>> " + greeting.getGreeting());
}

The output is:

>>> Welcome
>>> Hello

You can inject your custom component (for example PluginDescriptorFinder, ExtensionFinder, PluginClasspath, …) in DefaultPluginManager just override create... methods (factory method pattern).

Example:

protected PluginDescriptorFinder createPluginDescriptorFinder() {
    return new PropertiesPluginDescriptorFinder();
}

and in plugin repository you must have a plugin.properties file with the below content:

plugin.class=org.pf4j.demo.welcome.WelcomePlugin
plugin.dependencies=x, y, z
plugin.id=welcome-plugin
plugin.provider=Decebal Suiu
plugin.version=0.0.1

You can control extension instance creation overriding createExtensionFactory method from DefaultExtensionFinder. Also, you can control plugin instance creation overriding createPluginFactory method from DefaultExtensionFinder.

For more information please see the demo sources.

NOTE: If your application didn’t find extensions then make sure that you have a file with name extensions.idx generated by PF4J in the plugin jar. It’s most likely that they are some problems with the annotation processing mechanism from Java. One possible solution to resolve your problem is to add a configuration to your maven build. The maven-compiler-plugin can be configured to do this like so:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.5.1</version>
    <configuration>
        <annotationProcessors>
            <annotationProcessor>org.pf4j.processor.ExtensionAnnotationProcessor</annotationProcessor>
        </annotationProcessors>
    </configuration>
</plugin>