Home > Java, NetBeans > Developing A Simple Pluggable Java Application

Developing A Simple Pluggable Java Application

September 20th, 2009 James Leave a comment Go to comments

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


 

Related posts:

  1. Developing A Simple Java Application With Spring
  2. Subversion and NetBeans – A quick start guide
  3. Must have tools for a Java Developer
  4. Ubuntu 8.10 – A Productive Java Development Environment
  5. Connecting to a database from a java web application

  1. AudriusK
    September 28th, 2009 at 01:00 | #1

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

  2. Tom
  3. September 28th, 2009 at 23:29 | #3

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

  4. Steve
    September 29th, 2009 at 17:22 | #4

    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 :)

  5. September 29th, 2009 at 20:08 | #5

    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.

  6. September 30th, 2009 at 11:36 | #6

    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 :)

  7. September 30th, 2009 at 16:32 | #7

    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.

  8. October 2nd, 2009 at 14:57 | #8

    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/ ;-)

  9. Lex
    October 3rd, 2009 at 18:29 | #9

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

  10. October 31st, 2009 at 02:53 | #10

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

    adym

  11. Anonymous
    November 10th, 2009 at 22:52 | #11

    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.

  12. Suchitto Palit
    February 14th, 2010 at 02:56 | #12

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

  13. Suchitto Palit
    February 18th, 2010 at 23:50 | #13

    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

    :)

  1. No trackbacks yet.