Best Way to Build a Plugin System with Java

Best way to build a Plugin system with Java

First you need an interface that all plugins need to implement, e.g.

public interface Plugin {
public void load(PluginConfiguration pluginConfiguration);
public void run();
public void unload();
public JComponent getConfigurationPage();
}

Plugin authors should then bundle their plugins into JAR files. Your applications opens the JAR file and could then use an attribute from JAR manifest or the list of all files in the JAR file to find the class that implements your Plugin interface. Instantiate that class, the plugin is ready to go.

Of course you may also want to implement some kind of sandboxing so that the plugin is restricted in what it can and can not do. I have created a small test application (and blogged about it) that consists of two plugins, one of which is denied access to local resources.

How to create a pluginable Java program?

I've done this for software I've written in the past, it's very handy. I did it by first creating an Interface that all my 'plugin' classes needed to implement. I then used the Java ClassLoader to load those classes and create instances of them.

One way you can go about it is this:

File dir = new File("put path to classes you want to load here");
URL loadPath = dir.toURI().toURL();
URL[] classUrl = new URL[]{loadPath};

ClassLoader cl = new URLClassLoader(classUrl);

Class loadedClass = cl.loadClass("classname"); // must be in package.class name format

That has loaded the class, now you need to create an instance of it, assuming the interface name is MyModule:

MyModule modInstance = (MyModule)loadedClass.newInstance();

Creating a Plugin System in Java

Writing your own plug-in infrastructure is fun, but totally unneccesary. It's a solved problem and you're not going to write a higher quality one than one that already exists and is proven in the field. I'd say choose your battles.

I've tried out JSPF before and found it incredibly easy to use. And this coming from someone who has done exactly what you're trying to do: I've made my own plug-in infrastructure (for basically the same purpose: to load mini-games dynamically) from scratch, writing the classloading and framework myself. And if I were to do it again, I would use a framework like JSPF without hesitation.

To load all classes from jars in a directory that adhere to a certain interface (say Game), it's as easy as:

PluginManager pm = PluginManagerFactory.createPluginManager();
pm.addPluginsFrom(new File("plugins/").toURI());

Collection<Game> games = new PluginManagerUtil(pm).getPlugins(Game.class);

IIRC the only requirement on implementers of Game is that it be tagged with the @PluginImplementation annotation.

Edit

And then:

for ( Game game : games ) {
game.someMethod();
}

How To Create a Flexible Plug-In Architecture?

This is not an answer as much as a bunch of potentially useful remarks/examples.

  • One effective way to make your application extensible is to expose its internals as a scripting language and write all the top level stuff in that language. This makes it quite modifiable and practically future proof (if your primitives are well chosen and implemented). A success story of this kind of thing is Emacs. I prefer this to the eclipse style plugin system because if I want to extend functionality, I don't have to learn the API and write/compile a separate plugin. I can write a 3 line snippet in the current buffer itself, evaluate it and use it. Very smooth learning curve and very pleasing results.

  • One application which I've extended a little is Trac. It has a component architecture which in this situation means that tasks are delegated to modules that advertise extension points. You can then implement other components which would fit into these points and change the flow. It's a little like Kalkie's suggestion above.

  • Another one that's good is py.test. It follows the "best API is no API" philosophy and relies purely on hooks being called at every level. You can override these hooks in files/functions named according to a convention and alter the behaviour. You can see the list of plugins on the site to see how quickly/easily they can be implemented.

A few general points.

  • Try to keep your non-extensible/non-user-modifiable core as small as possible. Delegate everything you can to a higher layer so that the extensibility increases. Less stuff to correct in the core then in case of bad choices.
  • Related to the above point is that you shouldn't make too many decisions about the direction of your project at the outset. Implement the smallest needed subset and then start writing plugins.
  • If you are embedding a scripting language, make sure it's a full one in which you can write general programs and not a toy language just for your application.
  • Reduce boilerplate as much as you can. Don't bother with subclassing, complex APIs, plugin registration and stuff like that. Try to keep it simple so that it's easy and not just possible to extend. This will let your plugin API be used more and will encourage end users to write plugins. Not just plugin developers. py.test does this well. Eclipse as far as I know, does not.

Java Web application plugin architecture

I will try to provide several possible solution. I did spent some time preparing small PoCs for the project I'm working on, so let's hope the options below are relevant.

Important note: it is really easy to define some extension point, do resolve and find available implementations. There are a lot of solutions available, for example good and simple one -- JSPF

Resources are the main problem for WEB applications

OSGi

OSGi, is not that bad and can be useful. It seems to be heavy (and some implementations are heavy) but this is price of standardized platform. I would suggest to check Apache Felix. It can be used in a "lightweight" mode. By the way, it includes Web Console which is build as loosely coupled plugin-based application, could be helpful:

Sample Image

Some examples Extending the Apache Felix Web Console

The Web Console can be extended by registering an OSGi service for the
interface javax.servlet.Servlet with the service property
felix.webconsole.label set to the label (last segment in the URL) of
the page. The respective service is called a Web Console Plugin or a
plugin for short.

You can also check eie-manager which is clean and simple and uses OSGi to manage plugins. Could be a good example for you.

Custom plugin framework

I would suggest to review solution behind Jenkins/Hudson. I would say Jenkins plug-in system is quite mature and reliable. Can be used as a good example.

Sample Image

Please also check Hudson Plugin Architecture

Simple solution

For my project I've build plugin abstraction layer based on JSPF with custom dependency resolver.

PROS:

  • simple and small
  • clean concept
  • works good

CONS:

  • without proper plugin management can be slow (full classpath search)
  • provides very basic functionality
  • may require additional attention

I would suggest to use JSPF only if you really need some simplicity and want to control everything. JPF provides a lot of interesting features out of the box, for example:

Plug-ins can be "hot-registered" and even de-registered during
application execution. What's more, registered plug-ins can be
activated and deactivated "on the fly", minimizing runtime resource
usage.

The problem is JPF is dead.

Suggestion

Do spend some time with Apache Felix. It is mature enough, so your time investments may pay back a lot.

Java plugin framework choice

If you are planning to have just one (or only a few) not very complex 'extension points' than perhaps a well-defined SPI and a piece of configuration might be sufficient. No need to use a plugin framework.

By piece of configuration I mean some mechanism to find your plugins. For example something like META-INF/services/ or simply listing your plugins in a configuration file.

More details (upon request):

SPI = Service Provider Interface, an "implementer-side equivalent of an API". To learn more try searching for a difference between API and SPI. However in this context it is just a fancy term for an interface to be implemented by your plugins (i.e. defines the contract for your plugins).

A nice, short article "Creating a Service Provider Interface" by Ethan Nicholas describes how to create your own SPI in similar way as it is done in several part of the Java Platform itself.

META-INF/services/ can be seen as a more generalized approach to creating SPIs. More information can be found in the respective section of the JAR File Specification.

How to make a plugin-like architecture runnable?

After a long and tedious process of trial and error, I have found a solution which is working for me. So I decided to share the solution online, in case someone else runs into a similar problem. Here is a link to the final zip archive containing working example projects => File CustomerRunnable_RunningAssemblyPluginStackoverflowExample.zip https://drive.google.com/drive/u/0/folders/1ilJeTrOPgYgUTdOP0J4BQcBnPT5fls0k

My error was that I misunderstood how the assembly-plugin works. The approach that I executed the plugin inside my aggregator pom (CustommerRunnable) is wrong, as this maven project only exists as parent pom.

The CustommerRunnable pom.xml references all customer plugins as modules. Those modules have not the CustommerRunnable as parent, but a different pom. Then I created a separate maven project "distribution". The pom.xml of the distribution defines all the plugins (needed customer maven modules) as dependencies. It also has the CustommerRunnable pom.xml as parent. Hence when I run the project in NetBeans, all connected modules are also build(if necessary).

It also configures the assembly plugin. The assembly plugin is attached to the maven package-phase and thus executed with it. It also uses a custom assembly descriptor, which copies all the previously defined plugins into the right folders. This is done by using dependencySets with include and exclude patterns.
See https://maven.apache.org/plugins/maven-assembly-plugin/advanced-descriptor-topics.html for details on this.
So one dependencySet copies all jar files of all plugins to a /plugin folder by using an include pattern. Then this approach is inversed to copy the jar files of all external dependencies to a /lib folder.

The descriptor also defines some extra files to copy to a particular location. exec-maven-plugin, so I can comfortably start the customer software out of NetBeans. I didn't yet manage to configure the execute plugin correctly regarding the needed classpath arguments.

Endresult looks like this:
Assembly folder after building

It is also worth noting that the configurations of the "Build project", "Run project" and "Debug project" inside NetBeans need a tiny bit of modification. (Right Click Module "distribution" -> "Properties" -> point "Actions"



Related Topics



Leave a reply



Submit