:::: MENU ::::

Simple Plug-in Example

The long-term vision for a project I’m working on now is using the word ‘platform’. “We want to be a platform.” But what does that mean? Windows is a platform, Android is a platform, heck Firefox is a platform. All of these platforms provide a way to run 3rd party code, in the form of apps, programs or plug-ins. When exploring some options for pulling in and running 3rd party code within a rest-service, I realized I’ve never had to write a plug-in. Here’s a simple walk through of a simple plug-in example in Java.

For this example we have a simple base interface that implements a set of methods . (here, just one)

public interface PlugIn {
   public String helloWorld();
}

This is the interface provided to the 3rd-party developer. They might provide an implementation something like this.

public class SpiImplOne implements PlugIn {
   private static String hello = “Hello, I’m number one.”;

   public SpiImplOne() {}

   @Override
   public String helloWorld() {
      return hello;
   }
}

So, they give us a jar with this guy in it… now what? I’m sure there’s a brute-force way to go through all the class files in the jar, and find what we’re looking for. But for the sake of simplicity, I suggest adding one more requirement to the plug-in developer: tell us where the class is. That is, each plug-in jar must fill out a properties file (always the same name, in the same place), where one of properties tells us which class we’re looking for. It might be easiest to just put it in the root of the jar, but you do what you want. Here’s our example “spiimpl.properties”.

implName=spi_impl_one.SpiImplOne

Notice, there’s not a whole lot there. Just the package and class name of the implementation.

That’s it, we have a plug-in. Implement our interface(s) and tell us where it’s at.

So how do I run it. Here’s some code to take a list of jar files, read the properties files, and load each class.

//exercise for the reader
List<URL> jars = getAllTheFilesInADirectory(“../plugins”);

//adding a second argument (null) to the URLClassLoader constructor will prevent
// … searching this classes parent class loader
URLClassLoader loader = new URLClassLoader(jars.toArray(new URL[jars.size()]));


//grab all the properties files
Enumeration<URL> en = loader.getResources(“spiimpl.properties”);


//a place to stick the plug-in classes when they’re loaded
List<PlugIn> plugIns = new ArrayList<PlugIn>();


while (en.hasMoreElements()) {
   URL url = en.nextElement();
   JarURLConnection urlcon = (JarURLConnection) (url.openConnection());

   //load the properties file
   Properties p = new Properties();
   p.load(url.openStream());

   //load the class listed in the properties file.
   Class tempClass = loader.loadClass(p.getProperty(“implName”));
   plugIns.add((PlugIn) tempClass.newInstance());

}

//Now that we have a list of loaded plug-in, we can start using them.
for (PlugIn bc : plugIns) {
   System.out.println(bc.helloWorld());
}

If you’d like to see all my dirty-laundry, here’s a far-from-polished public github repo with the same code.

https://github.com/shostler/ClassLoaderExample

Next steps: If you plan to run other peoples code on your machine, you’ll want to lock-down what they can do. Look into java security http://docs.oracle.com/javase/7/docs/api/java/security/package-summary.html , it provides fine-grained permissions so you can prevent their code from calling System.exit(), (except when you want it to).


2 Comments

    • Reply shostler@gmail.com |

      Great point. ServiceLoader is great option, and the code is a little easier to understand. It also saves us from having to safely call newInstance() on each class.(which you’ll notice isn’t happening in my example)

      The tradeoff, however, is with calling newInstance() on each class will let us only instantiate the one(s) we want. And as a bonus, we can get access to the classes constructors. (if we want to pass in constructor args)

So, what do you think ?