Skip to main content

Plugin Architechture

If you have used IDE like Ecllipse you may have noticed that you can extend the functionality by adding plugins. Eclipse is an extensible platform. We can add new tools using pluggable components called Eclipse plug-ins. We can create our own plugin by using Eclipse plug-in model.

When we develop softwares we also want to create our applications extensible and modular. We can use plugin architecture to fulfill our needs.


What is a Plug-in?

A plug-in (or plugin, add-in, addin, add-on, or addon) is a software component that adds a specific feature to an existing computer program. (Wikipedia)

In plugin architecture, core modules may not know all existing features, instead we plug them at loading time. We are taking advantage of separation of concerns. Plugins is a fine example of open closed principle where we are extending existing functionality without changing main logic.

Advantages

  • Extensible - plugin can be developed outside the main program and extend the behaviour without touching it.
  • Independent - different people can develop plugins without knowing each other.
  • Decouple - main program doesn't depend on extensions.
  • Small - can be designed as small units.

How to design

Designing will depend on what you are building. As in eclipse example, it is an IDE where plugins are help develpment.
First we need to consider what the behaviours we allow to add as plugins. Then we need to create a clear abstractions around it which third parties should implement.
Not just that, we need to define our package structures, naming convenstions to follow, etc.

How to resolve plugins

Our system can be packaged as jar or web application or anything. Whatever it is, we need to have a way to register extra plugins into our system.
There can be two main ways to do that.
  • Configuration - this can be a configuration file like yml, database entry, service loader, or even Java class where we allow direct access.
  • Discovery - this can be done by imposing some rules, like you need copy your jar into plugins folder or you need annotate your plugin with custom annotation. Plugin loader then can automatically detect them and register with our system.

Working Example

What is important is to create plugin abstraction clearly. Let's create our dummy Plugin abstraction first.

package com.slmanju.plugins.api;

public interface Plugin {

  void run();

  String id();

  String name();

}
Now anyone can implement our Plugin.

package com.slmanju.plugins.menus;

import com.slmanju.plugins.api.Plugin;

public class SavePlugin implements Plugin {

  @Override
  public void run() {
    System.out.println("run save plugin");
  }

  @Override
  public String id() {
    return "com.slmanju.SavePlugin";
  }

  @Override
  public String name() {
    return "Save";
  }

}
I'm going keep my Plugins in a Map.

package com.slmanju.plugins.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class PluginRegistry {

  private Map plugins = new HashMap<>();

  private final static PluginRegistry INSTANCE = new PluginRegistry();

  private PluginRegistry() {}

  static PluginRegistry getInstance() {
    return INSTANCE;
  }

  void register(Plugin plugin) {
    plugins.put(plugin.id(), plugin);
  }

  Plugin resolve(String id) {
    return plugins.get(id);
  }

  List plugins() {
    return new ArrayList<>(plugins.values());
  }

}
Then we can use our Plugins as below. Full code can be found from my github account.

package com.slmanju.plugins;

import com.slmanju.plugins.api.Plugin;
import com.slmanju.plugins.api.PluginPlatform;

public class PluginApplication {

  public static void main(String[] args) {
    PluginPlatform.getInstance().bootstrap();

    PluginPlatform.getInstance().plugins().forEach(plugin -> {
      System.out.println("************************");
      System.out.println(plugin.id());
      System.out.println(plugin.name());
      plugin.run();
    });

    System.out.println("--------------------");
    Plugin plugin = PluginPlatform.getInstance().plugin("com.slmanju.SavePlugin");
    plugin.run();
  }

}

Full Code

References