de.velopmind | The Det about Programming

January 23, 2011

Guice 2.0 Multibinder + Java ServiceLoader = Plugin mechanism

Filed under: Java — Tags: — de.velopmind @ 12:47 am

Currently I have the task to extend an existing legacy Java Swing client with a plugin mechanism. As I hadn’t looked into this topic before, I tried to get an idea about the possibilities and what others do.

The first things to explore when talking about Java and Plugin are surely the IDEs like Eclipse and Netbeans, as for them the plugins are an elementary part, and so they should show how to do it right.

While Eclipse has an elaborated but quite obscure extension mechanism, Netbeans uses a proprietary lookup mechanism, which is an advanced version of the standard ServiceLoader feature.

This ServiceLoader, standard part of Java 6, is the simples approach to extensibility. Some part of the Software (the consumer) defines an Interface, which another part (the provider) must implement.

Then the consumer uses the ServiceLoader API to get all classes which implement that interface, and here we are.

The lookup is really simple: The provider contains in its Jar a directory named META-INF/services. In that there is a text file which is named like the fully qualified name of the interface (without any file extension). The file contains now the fully qualified name of the implementing class in this jar.

So you only have to throw that jar somewhere into the classpath of the application, and the consumer will get an instance of the implementing class.

And just here are the limitations of this approach:

  • Every lookup retrieves a new instance.
  • Every instance is constructed via its parameterless default constructor.

That’s it. As the name ServiceLoader may suggest, this mechanism is meant for loading services, not plugins.  And in its simplest form it is for stateless services. To overcome this last limitation, one would have to wrap the service loading into a singleton of a special service class which keeps the dependency to the implementing class hidden.

This seems not to be helpful so far.

Now, when we talk about plugins, we talk about Dependency Injection, and there are already nice solutions for that, for example Googles Guice, which is currently in its version 2.0,  but with 3.0 on the way, which will be based on the new JSR-330 specification,  now becoming the standard for DI in Javaland.

Guice is  a very nice thing.  DI is handled solely by annotations of classes and consuming constructors, methods or fields.

As injected instances can themselves depend on injections, and as we can declare Providers (say: factories), this system is very flexible and able to work with parameterised constructors, singleton instances and so on.

So even in complex situations Guice can provide  valid object trees.

Guice also  provides a nice Java API to declare the bindings from interface to implementation.  And here we have two problems :

  1. The standard approach is indeed, to inject the implementation for a required interface, not all of them.
  2. The configuration is provided in a Java class which inherits from Guice’s Module and implements the configure() method.  Reconfigure means: recompile.

The first problem is already addressed by the Multibinder, which was new in Guice 2.0.  With this binding approach, different providing jars can implement an interface, register the binding, and a consumer can get all the implementations. This allows for real extension points and extensions.

But Plugin is even more: It is the possibility to plug some extension in, so to configure the application on customer site, not at compile time.

As the Guice wiki states:

Finally we must register the plugins themselves. The simplest mechanism to do so is to list them programatically:

public class PrettyTweets {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(
        new GoogleMapsPluginModule(),
        new BitlyPluginModule(),
        new FlickrPluginModule()
        ...
    );

    injector.getInstance(Frontend.class).start();
  }
}

If it is infeasible to recompile each time the plugin set changes, the list of plugin modules can be loaded from a configuration file.

Unfortunately the wiki forgot to say how. This may be left as a task to the reader.

Well, to allow for extensibility on customer site, a plugin jar would have to be put into the classpath and explored on startup. And yes, there is some work for that on the way: Guice Automatic Injection , by Daniel Manzke, which will bring the big right solution.

But wait, in the meantime there can be a simpler solution. If we look at the main method code above, which depicts the standard Guice startup, we see that all Module classes are created by their parameterless default constructor.  But wasn’t that exactly for what ServerLoader was useful for?

Indeed now we see a feasible architecture for our plugin mechanism.

  1. Create extension points by defining interfaces on consumer site.
  2. Expect to get all implementations injected by Guice’s multi mechanism
  3. Create a markup interface to mark all Module classes that configure plugin implementations.
  4. Let different parties implement the interface from 1 and register them per Multibinder in a special Module subclass which implements the markup interface from 3.
  5. Let these parties declare the Module subclass as implementation for the markup interface in the META-INF/services folder of their jar.
  6. In your application startup code (main method) use ServiceLoader to get all Module instances  that implement that markup interface and use them to create the injector.

Here are some code examples from a little test workspace:

Let’s start with the thing that causes all this trouble: The component containing the extension point:

package modone;

import com.google.inject.Inject;
import java.util.Set;

public class ModOneImpl implements ModOne {
    @Inject(optional=true) Set<ModOneExtension> mPlugins = new HashSet<ModOneExtension>();

    public String getSomeval() {
        String r = "Someval from ModOneImpl ";
        for (ModOneExtension p : mPlugins)  {
            r = p.extend(r);
        }
        return r;
    }
}

So this class has a method getSomeval which returns a String value, but gives some extensions/plugins the chance to do some processing on the string before it is returned.  The class ModOneImpl defines the extension point by expecting an injected Set of implementors of  ModOneExtension, the interface that declares  the called extend method.

As there may be no plugin at all on the classpath, the injection is marked optional with an empty set as default initialisation.

Let’s assume that our application has more than one of such extension points, so we declare the application as a whole to be extensible by plugins by creating a markup interface:

package myapp;

import com.google.inject.Module;

public interface PluginModule extends Module {}

Each plugin now creates its specific implementation of the extension interface, e.g. like this:

package pluginone;

import modone.ModOneExtension;

public class PluginOne implements ModOneExtension {
     public String extend(String x) {
          return  "extended(one): "+x;
     }
}

Additionally, this implementation is bound to that interface in the  module configuration class:

package pluginone;

import modone.ModOneExtension;
import myapp.PluginModule;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;

public class PluginOneModule extends AbstractModule implements PluginModule {
   public void configure() {
     Multibinder<ModOneExtension> mbinder = Multibinder.newSetBinder( binder(), ModOneExtension.class);
     mbinder.addBinding().to(PluginOne.class);
   }
}

That was the Guice part, and now the ServiceLoader part.

Create a file in the directory META-INF/services named:  myapp.PluginModule .

This file only contains one line:   pluginone.PluginOneModule

Now what is left is the application’s startup code:

package myapp;

import modone.ModOneModule;
import modrep.ReportModule;

import com.google.inject.Guice;
import com.google.inject.Module;
import com.google.inject.Injector;

import myapp.PluginModule;
import java.util.ServiceLoader;

import java.util.List;
import java.util.ArrayList;

public class Main {
   public static void main(String[] args) {
       List<Module> modlist = new ArrayList<Module>();
       modlist.add( new ModOneModule() ); // declare app modules the standard way
       modlist.add( new ReportModule() ); 

      ServiceLoader<PluginModule> extensions = ServiceLoader.load( PluginModule.class );
      for(PluginModule ext : extensions) {
          modlist.add(ext);         // add each found plugin module
      }

      Injector inj = Guice.createInjector( modlist );
      ReportApp appl = inj.getInstance( ReportApp.class );
      appl.report();
   }
}

In this main method we see how the list of all modules is created and filled with the standard application modules.   The plugin modules then are looked up by the markup interface PluginModule via the ServiceLoader mechanism and added to that list.

In the end the injector is created and the application started by getting the first managed instance from the injector, like usual.

To add more plugins, simply put their jar files on the classpath and they will be configured and managed by Guice automatically.

I hope that you enjoyed this little experiment.

PS: If you want to configure only loaded plugin modules with your injector, you can directly put the ServiceLoader instance into the createInjector() method, as the latter requires an Iterable instance, and the ServiceLoader implements this interface.

Edit: After I finished this article, I found a similar approach on Stackoverflow, with a nice alternative for module loading.

Advertisements

Create a free website or blog at WordPress.com.