Announcement Announcement Module
Collapse
No announcement yet.
Plugins Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Plugins

    I thought I'd share a recipe we are using to implement Plugins. If it's a good idea, then it might end up being useful to someone - if it's a bad idea, then a little peer review is always a good thing.
    We are developing a rather large rich client application and have created a plugin system of sorts using Spring. The idea is pretty simple: we would like to package a plugin into a single jar. If we include the jar in the classpath, then it automatically becomes a part of the app. To accomplish this we created a set of interfaces that define the various plugin points for the application (I believe Eclipse would call these extension points, but I have never used Eclipse in any way, so have no familiarity with their system except what I've heard other people mention in passing). For example, we have a simple "Plugin" interface that provides getters for general information on the Plugin itself (id, name, description, etc). Another example: if you have an application that can work with various file types, you would define an interface for a file type handler (or whatever). If a plugin wanted to handle a new filetype, it would simply create an implementation of this interface.
    Next, the Plugin needs a way to tell the application about itself (sure, it defines a class that implements the file handler interface, but how does the app know this?). This is easy: each plugin has its own Spring config. In our two examples above, you would define a bean in the Spring config that implements the Plugin interface and another bean that implements the file handler interface.
    Our application uses central managers for each application interface (such as the PluginManager). The PluginManager provides simple methods for registering and unregistering plugins:
    Code:
      void registerPlugin(Plugin plugin);
      void unregisterPlugin(Plugin plugin);
    The file manager would also provide similar methods.
    Now there are just two ingredients remaining: 1) A way to pick up the plugin's Spring config file, and 2) a way to automatically register the beans defined in the config file with their proper manager.
    For #1, we have been manually building the list of spring config files at deploy time. This doesn't quite attain to our dream of simply dropping in a new jar and having a new plugin. In this case, if someone added a plugin, they would also have to modify the list of .xml files to include the .xml file for their plugin. However, we just recently ran across a comment by Juergen in a blog (and maybe this is documented somewhere else too) that you can use this path when loading your application context:
    Code:
    classpath*:/META-INF/beans.xml
    and it will automatically pickup all 'META-INF/beans.xml' files defined in all the .jars on your classpath. While we haven't tested this yet, if it works it would provide an easy solution to our dream. We would simply put each plugin's .xml config file in the jar and use a similar path to the one above.
    For #2 there are multiple solutions. Our solution has been to create a BeanPostProcessor that automatically detects when a bean has been instantiated in the application context with the desired interface and then register that bean with the appropriate manager. Here is a small example:
    Code:
    /**
     * Automatically detects all beans in a Spring ApplicationContext that
     * implement the Plugin interface and registers them with the PluginManager.
     * Note that this only detects a Plugin once it is instantiated, which means
     * that Plugins cannot be prototypes and cannot be set to lazy initialization
     * for this to be effective.
     */
    public class AutoPluginDiscoveryAndRegistration implements BeanPostProcessor
    {
      private PluginManager pluginManager;
    
      public PluginManager getPluginManager()
      {
        return this.pluginManager;
      }
    
      public void setPluginManager(final PluginManager pluginManager)
      {
        this.pluginManager = pluginManager;
      }
    
      //
      // METHODS FROM INTERFACE BeanPostProcessor
      //
    
      public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
      {
        return bean;
      }
    
      public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
      {
        if(bean instanceof Plugin) {
          if(bean instanceof AutoPluginDiscoveryAware) {
            final AutoPluginDiscoveryAware apda = (AutoPluginDiscoveryAware)bean;
            if(apda.getAutoRegister()) {
              final Plugin plugin = apda.getPlugin();
              getPluginManager().registerPlugin(plugin == null ? apda : plugin);
            }
          } else {
            getPluginManager().registerPlugin((Plugin)bean);
          }
        }
    
        return bean;
      }
    }
    You can see in the code above that we have also defined an optional interface that can give the plugin more control over the automatic registration process. At this point, all we have to do to make this BeanPostProcessor active in our application context is to define it in our main application context config:
    Code:
      <!-- This bean will automatically register all Plugins in the application context. -->
      <bean class="somepackage.AutoPluginDiscoveryAndRegistration">
        <property name="pluginManager"><ref local="pluginManager"/></property>
      </bean>
    Note that another way to approach this might be to have your manager implement InitializingBean and ApplicationContextAware. When afterPropertiesSet is invoked the manager could query the application context for all the beans implementing your interface and automatically register them with itself. Yet another approach would be to implement a BeanFactoryPostProcessor, though these are usually for tweaking the bean definitions in an application context.
    As we define more plugin points we might refactor this recipe a little to make it more generic and adaptable. For example, the process of automatically finding and registering beans implementing certain interfaces could be made generic for any type of interface + manager combination. For now, it is working quite well.
    Any comments or ideas are welcome.

    - Andy

  • #2
    osgi

    I'm researching client application frameworks, and briefly looked at Spring RCP and Eclipse RCP (which the eclipse IDE is now based on, as well as many of IBM's developer tools, I hear). It turns out that Eclipse RCP is, among other things, an implemention of OSGi, a plugin architecture for rich clients initially developed for set-top boxes and automotive use. Eclipse RCP offers much more than that (built-in help system, software update mechanism, perspective-and-view architecture, SWT, etc), but the OSGi part is really interesting. I don't know if it supports auto-discovery as you describe it, but it is intended to be a solution to a problem similar to yours.

    Is anyone familiar enough with OSGi to comment on the usefulness of incorporating OSGi into the Spring RCP?

    Comment


    • #3
      I don't know if its OSGi or just Eclipse, but Eclipse uses XML manifests (along with directory structure) to decide what is a plug-in. I am not super familiar with it; but my current impression is that it (the Eclipse RCP manifest format) is ok, but could use some simplifications: like allowing all code + manifest to be packaged into a single JAR.

      Comment


      • #4
        Hi Andy,

        what exactly does the PluginManager do, when registering a plugin? What kind of methods does the plugin provide?

        Do I get it right: when the user chooses a file of a certain type, the FileManager asks the PluginManager for the plugin, which would handle this file type and then calls some method of the handler. This could e.g. result in showing an editor for that file type?

        Is that a possible use case?

        Thanks

        Kambiz

        Comment


        • #5
          Andy,

          Hope you are still there and read my reply

          I do appreciate your solution on making "plugin" work. But for me, I think we can not afford to implement our own "plugin" framework( there are too few developers in the company I am working for . On my previous investigation of comparation of Eclipse RCP and Spring RCP, I feel that eclipse rcp is more mature than eclipse rcp in ways( sorry spring rcp team ), especially how nice the eclipse rcp support plugins.

          For me, I'd better wait and watch -- I believe that spring team will support the plugins better in some time.

          Anyway, spring rcp is great in many ways -- I am continueing to find them out

          Regards,

          Comment


          • #6
            Good idea!!!

            Good idea!!!

            Comment


            • #7
              Could you help for this thread
              http://forum.springframework.org/showthread.php?t=47940

              Comment

              Working...
              X