8

How would you implement an extendable web application? What I'm thinking about is a web application similar to Jenkins or Hudson which provides plug-in support. While it's obvious to me how plug-ins can be located and loaded, I don't know how they can be integrated within the overall architecture. I'm especially uncertain about the following points.

How can a plug-in change the view, e.g. add input elements to a form?

My first idea would be that a plug-in can register partials / fragments for a certain form.

Example: Newsletter plug-in which registers a typical newsletter checkbox fragment which will be rendered in the user registration view.

How can a plug-in react to incoming requests?

Again, a direct approach would be to provide listeners for certain requests or actions, e.g. POST request to /user.

How could a plug-in persist data?

I'm assuming this is a situation where NoSQL data storage solutions would be superior to relational databases.

I would appreciate any comments, ideas and experiences (maybe there is even a design pattern) that you have regarding extendable web applications.

TommyMason
  • 945
  • 5
  • 13

1 Answers1

3

Your question contains almost all the answers. You're right in the ballpark: it's all plug-ins and their extension points to set the context and make them do the things you want. There are numerou ways to design plug-in systems. For starters:

http://people.clarkson.edu/~dhou/courses/EE564-s07/plugin.pdf

http://blogs.atlassian.com/developer/2011/03/plugin_architecture_episode_i.html

Here is a trivial example to illustrate how a rudimentary plugin-aware system would work:

public interface Plugin {

  void setEntityManager(EntityManager manager); // this is very course grained and liberal! A plugin would have access to whatever Entity Manager the container gives it. A plugin would then have a carte blanche to do whatever it needs: create, drop, insert, select, delete.

  View renderView(ViewContext context); // a plugin would render or return a view (whatever it is, could be a string in the simplest case) based on the context that the container passed to the plugin

  Action readEvent(Event event); // a plugin performs some Action based on an event as notified by a container

}


public class PluginContainer {

  private List<Plugin> plugins = new ArrayList<Plugin>();

  public void registerPlugins() {
    // loop through plugin descriptors obtained by your preferred mechanism
    // like introspecting libraries (JARs) in a configurable location

    // for each descriptor, load a Plugin dynamically and "register" it with a container
    Plugin p = ClassLoader.getSystemClassLoader().loadClass("com.my.PluginA").newInstance(); 
    p.setEntityManager(entityManager);
    plugins.add(p);
  }

  public void readEvent(AppEvent appEvent) {
    Event e = this.constructPluginSpecificEventFromAppEvent(); // optional
    for (Plugin p : this.plugins) {
      p.readEvent(e); // disregarding Action here
    }
  }
}

public class Application {

  private PluginContainer pContainer;

  private void buttonClicked(AppEvent appEvent) {
    this.showCoolDialog("Thank you for clicking a button!");
    // now let my plugins handle this
    // they can do whatever they want for this event
    // since they have access to EntityManager, they can work with a persistence storage as well
    this.pcContainer.readEvent(appEvent);
  }

}
Yuriy Zubarev
  • 2,653
  • 1
  • 15
  • 15