Developing A Simple Pluggable Java Application

Most of the applications we use on daily basis are pluggable. Popular applications like Firefox, Eclipse, NetBeans, JEdit, WordPress, Hudson are all pluggable. In fact, pluggability has played a major part in the success of most of these applications. Why not make the Java applications we develop pluggable as well? Yes, we get pluggability out of the box, if our applications are based on a rich client platform like NetBeans or Eclipse. But for some reasons if you decide not to use those platforms, it doesn’t mean that they should not be pluggable. In this article, we will learn how to write a simple pluggable application that will load it’s plugins dynamically.

The API
First, let us define a plugin interface that should be implemented by all the plugins of our application. We are going to keep it very simple. Create a project called “plugin-api” in your favorite IDE and create the interface “ApplicationPlugin”.


package com.pluggableapp.plugins.api;

public interface ApplicationPlugin
{
    String getName();
    void init();
}

The Plugins

Writing the plugins now is very easy. Our plugins need to implement the plugin interface and follow a simple convention to make it easy for our applications to find them later. Create a project called “plugin-a” and develop our first plugin.


package com.pluggableapp.plugins;

import com.pluggableapp.plugins.api.ApplicationPlugin;
import java.util.logging.Logger;

public class PluginA implements ApplicationPlugin
{
    private static Logger logger = Logger.getLogger(PluginA.class.getName());

    public String getName()
    {
        return "Plugin A";
    }

    public void init()
    {
        logger.info(getName() + " initialized!");
    }
}

Now create a directory called “META-INF/services” inside the source directory and create a file with the name “com.pluggableapp.plugins.api.ApplicationPlugin” (This is the fully qualified name of the plugin interface). The file should contain the name of the actual plugin inside it. In our case, the text is “com.pluggableapp.plugins.PluginA”.

Repeat these steps to create PluginB.


The Application

It’s time to create the application that will consume the plugins we created. First let us create a class to add the plugin jars to the classpath dynamically.


package com.pluggableapp;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.logging.Logger;

public class ClasspathUtils
{

 private static Logger logger = Logger.getLogger(ClasspathUtils.class.getName());
 // Parameters
 private static final Class[] parameters = new Class[]
 {
     URL.class
 };

 /**
 * Adds the jars in the given directory to classpath
 * @param directory
 * @throws IOException
 */
 public static void addDirToClasspath(File directory) throws IOException
 {
     if (directory.exists())
     {
         File[] files = directory.listFiles();
         for (int i = 0; i < files.length; i++)
         {
             File file = files[i];
             addURL(file.toURI().toURL());
         }
     }
     else
     {
         logger.warning("The directory \"" + directory + "\" does not exist!");
     }
}

 /**
 * Add URL to CLASSPATH
 * @param u URL
 * @throws IOException IOException
 */
 public static void addURL(URL u) throws IOException
 {
     URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
     URL urls[] = sysLoader.getURLs();
     for (int i = 0; i < urls.length; i++)
     {
         if (urls[i].toString().equalsIgnoreCase(u.toString()))
         {
             logger.info("URL " + u + " is already in the CLASSPATH");
             return;
         }
     }
     Class sysclass = URLClassLoader.class;
     try
     {
         Method method = sysclass.getDeclaredMethod("addURL", parameters);
         method.setAccessible(true);
         method.invoke(sysLoader, new Object[]
         {
             u
         });
     } catch (Throwable t)
     {
         t.printStackTrace();
         throw new IOException("Error, could not add URL to system classloader");
     }
  }
}

Now create an interface called “PluginService” to define the methods needed to load and initialize the plugins.


package com.pluggableapp;

import com.pluggableapp.plugins.api.ApplicationPlugin;
import java.util.Iterator;

public interface PluginService
{
    Iterator<ApplicationPlugin> getPlugins();
    void initPlugins();
}

As of version 1.6, the Java runtime ships with a class called “ServiceLoader” to easily find and load plugins. Let us now write a implementation called “StandardPluginService” and make use of the facilities built into the java runtime.


package com.pluggableapp;

import com.pluggableapp.PluginService;
import com.pluggableapp.plugins.api.ApplicationPlugin;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.logging.Logger;

public class StandardPluginService implements PluginService
{
    private static StandardPluginService pluginService;
    private ServiceLoader<ApplicationPlugin> serviceLoader;
    private Logger logger = Logger.getLogger(getClass().getName());

    private StandardPluginService()
    {
        //load all the classes in the classpath that have implemented the interface
        serviceLoader = ServiceLoader.load(ApplicationPlugin.class);
    }

    public static StandardPluginService getInstance()
    {
        if(pluginService == null)
        {
            pluginService = new StandardPluginService();
        }
        return pluginService;
    }

    public Iterator<ApplicationPlugin> getPlugins()
    {
        return serviceLoader.iterator();
    }

    public void initPlugins()
    {
        Iterator<ApplicationPlugin> iterator = getPlugins();
        if(!iterator.hasNext())
        {
            logger.info("No plugins were found!");
        }
        while(iterator.hasNext())
        {
            ApplicationPlugin plugin = iterator.next();
            logger.info("Initializing the plugin " + plugin.getName());
            plugin.init();
        }
    }
}

Feel free to write your own implementation if needed. For example you can easily write an implementation based on the NetBeans API.

Write a factory class named “PluginServiceFactory” to create “PluginService” objects.


package com.pluggableapp;

import com.pluggableapp.*;
import com.pluggableapp.StandardPluginService;
import com.pluggableapp.PluginService;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PluginServiceFactory
{
    public static PluginService createPluginService()
    {
        addPluginJarsToClasspath();
        return StandardPluginService.getInstance();
    }

    private static void addPluginJarsToClasspath()
    {
        try
        {
            //add the plugin directory to classpath
            ClasspathUtils.addDirToClasspath(new File("plugins"));
        } catch (IOException ex)
        {
            Logger.getLogger(PluginServiceFactory.class.getName()).log(
                Level.SEVERE, null, ex);
        }
    }
}

That’s it. All we need to do now is to write a class that will invoke the “PluginServiceFactory”.


package com.pluggableapp;

import com.pluggableapp.plugins.api.ApplicationPlugin;
import java.util.Iterator;
import java.util.logging.Logger;

public class Main
{

    private static Logger logger = Logger.getLogger(Main.class.getName());

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args)
    {
        loadPlugins();
    }

    private static void loadPlugins()
    {
        PluginService pluginService = PluginServiceFactory.createPluginService();
        pluginService.initPlugins();
    }
}

Before we build and see the application in action, create a directory called “plugins” and put all plugin jars there.

And this is what you might see when you run our pluggable application.

Now feel free to create as many plugins you want to write and just drop them in the “plugins” directory and restart the “pluggable” application to see your “home grown” plugins in action!

Resources

Creating Extensible Applications With the Java PlatformJohn O’Conner

Developing a Java Plugin Framework

How to Create a Pluggable Photo Album in JavaGeertjan


27 comments to Developing A Simple Pluggable Java Application

  • AudriusK

    Very useful post. It doesnt do many importance to me now, but I remember I had dificulties searching smth like this few yers ago :)

  • Very good post and quite clear too. I will no doubt be using this in my app :)

  • Steve

    Great post James. I’ll find this very useful on future projects. I wondered how applications got pluggins to tie in to its all clear to me now :)

  • Good example, thx!
    I knew about the service api but your example is very clear.
    Didn’t wan’t to use osgi or the netbeans platform for my future javafx application.

  • Pad

    Nice post !! Don’t know if it’s gonna help me for my next development but I didn’t have any clue about how such a system could be implemented. Thanks for this article :)

  • That is really really a superb stuff, that i ever want to know it. In past, I have experienced the many PHP based CMS & systems, and they have extensive usage of real time plug-ins, add-ons and themes and at that time I wish how to achieve this in Java.

    Thanks a lot once again, sharing such a great concept.

  • Nice! So, this article is about ServiceLoader (in java > 6) the other one mentioned above http://java.dzone.com/news/how-create-pluggable-photo-alb is about openide Lookup (NetBeans) and another candidate is osgi: http://karussell.wordpress.com/2009/09/16/plugable-swing-a-hello-world-osgi-example/ ;-)

  • Lex

    Hi all! Give please worked source code, in archive..And that is impossible :(

  • Hi,
    Great article. I’m probably gonna use this for JBJF…

    adym

  • Anonymous

    Extremely complicated example. Try doing a Google search for more simple examples. 215 lines of code says it all, it can be done in less than half that number of lines.

  • Suchitto Palit

    Great Post. And very clear. Helped me a lot. Thank you very much James. :)

  • Suchitto Palit

    Hi James.
    1> The class StandardPluginService.java is a Singleton, isn’t it ?

    2> In the Main.java line 10 we do not need that line of code
    “private static Logger logger = Logger.getLogger(Main.class.getName());”

    Isn’t it?

    3> In ClasspathUtils.java line 59 I changed “Class sysclass = URLClassLoader.class;” to “Class sysclass = URLClassLoader.class;” .

    That eleminated unchecked error “[unchecked] unchecked call to getDeclaredMethod(String name, Class… parameterTypes) as a member of the raw type java.lang.Class ” in line 62
    “Method method = sysclass.getDeclaredMethod(“addURL”, parameters);”.

    I learned it from the link below.

    http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ002
    :)

  • - I takes to much time to configure for every program man;
    - I am using javascript to “plug in” some code in my program, it basicly changes whole program into another.
    - I know it highly risky, but highly configurable. with one exe(jar) file and directing “target directory” to some other directory with “plugins js files” in it, i solved great deal. I am searching same style plug in framework with java-class files now.

  • Kevin

    Hi, very useful indead, thanks a lot;
    but don’t you prefer this version:

    public static ClassLoader addDirToClasspath(File directory) throws IOException {
    MyURLClassLoader cl = new MyURLClassLoader() ;
    if (directory.exists())
    for (File file : directory.listFiles())
    if (file.getName().endsWith(".jar")) cl.addURL(file.toURI().toURL());
    return cl ;
    }

    private static class MyURLClassLoader extends URLClassLoader {
    public MyURLClassLoader() {
    super(new URL[]{}) ;
    }
    @Override
    public void addURL(URL url) {
    super.addURL(url);
    }
    }

    which doesn’t rely on the System classloader to to an URLClassLoader, doesn’t use reflection to access illegaly addURL, and also doesn’t create a class leak when you unload you app? (please let me know if you see any drawback, it’s just an open suggestion)

  • Bruce

    James,

    I have followed your example to the T, but get this error when I run it:
    Nov 16, 2010 2:03:29 PM pluggableapp.StandardPluginService initPlugins
    INFO: No plugins were found!

    Any ideas?

    TIA,

    Bruce

  • Sathish

    Hi I got the following output. But I placed the plugin-jar files in proper folder. I don’t know where the mistake is..

    16 Dec, 2010 12:41:18 PM com.pluggableapp.StandardPluginService initPlugins
    INFO: No plugins were found!

    Regards,
    Sathish

  • James

    @Satish Did you create the respective files in “META-INF” folder?

  • James

    @Kevin Thanks for the nice suggestion. Yes, I’m aware of the issues associated with the system class loader.

  • James

    @Suchitto Palit

    1> The class StandardPluginService.java is a Singleton, isn’t it ?

    – Yes the StandardPluginService is a singleton.

    2> In the Main.java line 10 we do not need that line of code
    “private static Logger logger = Logger.getLogger(Main.class.getName());”Isn’t it?

    – Logging is upto you and is not mandatory but it’s a good practice to log.

    3> In ClasspathUtils.java line 59 I changed “Class sysclass = URLClassLoader.class;” to “Class sysclass = URLClassLoader.class;” .

    – Sorry, I don’t understand what do you mean here.

  • Adam

    James,
    I know this is quite an old post, but I am having the same problem as Sathish. I get “No plugins were found” when I run Main. I have all the files set up the way mentioned in this article, but it would not compile so I moved in dependancies and built the plugin jar, copied it to the plugins folder, I have the META/services file in place (in fact I copied it to the main app project as well to test that as well). I copied all the code and pasted directly from this webpage. Nothing works. Any ideas?

    Here is a screenshot of my project files and the Files list:
    http://i.imgur.com/4BPFi.jpg

  • uyalap

    Thanks James. Very useful post.

  • Hi, very usefull post. I use it in my java simple executable app, it work so good. But when I want to use it in servlet in Tomcat, ClassCastException is throw by ServiceLoader when getting iterator (getPlugins). Have you heared something that ?
    I thought it’s same thing that a tomcat Classloader bord effect… Please help me !

    Sorry for my frenlish :)

    Xavier

  • Sahand

    Thanks a lot. It really helped me.
    But there’s something I don’t get. We have to list every subclass of the ApplicationPlugin in that manifest. What if later I wish to add a new plugin. just adding it to the plugins directory won’t help. What would you do in such a this situation?

  • angel figueroa cruz

    Hi, very usefull post. I would like to implement a more that one plug-in interface.

    First The onw you define.
    public interface ApplicationPlugin
    {
    // Init plugin
    void init();

    // Plugin method for management the method
    String getId();
    String getName();
    String getVersion ();

    }

    Second
    public interface ApplicationPluginData
    {
    //
    void getBitDataFromBuffer (String Data);

    }

    My Question is how changes need to make in order to use differences plug-in architecture. but be transparent to the developer of the application.

    thanks,
    angel figueroa

  • An Co

    Thank you for this tutorial, helped a lot and worked as a charm

  • A C

    Very useful post, thank you.
    How can i access from plugin references to classes created in main application

Leave a Reply

  

  

  

*

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>