RSS Feed for This PostCurrent Article

Develop a Java Plugin Framework – Search for Plugin Dynamically

Download Source Code

This is part II of my previous article, Develop a Java Plugin Framework. In the previous article, I showed you the design pattern to develop a simple Java plugin framework. Now I am going to show you how to search for your plugin in jar files dynamically.

Let’s said now I develop a plugin and package it as a jar file. I put the jar file in a designated application folder. So right now I need to get my application to read and load the plugin. Provided the folders has many jar files, the application has to be smart enough to load search and load the plugin for the application only.

In our case, all our plugins implements the IPlugin interface. So what we need to do is to search for classes that implement this interface and load them accordingly.

I implemented the PluginFinder class, as shown below.


class JarFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return (name.endsWith(“.jar”));
}
}

public class PluginFinder {

// Parameters
private static final Class[] parameters = new Class[]{URL.class};

private List<IPlugin> pluginCollection;

public PluginFinder() {
pluginCollection = new ArrayList<IPlugin>(5);
}

public void search(String directory) throws Exception {
File dir = new File(directory);
if (dir.isFile()) {
return;
}
File[] files = dir.listFiles(new JarFilter());
for (File f : files) {
List<String> classNames = getClassNames(f.getAbsolutePath());
for (String className : classNames) {
// Remove the “.class” at the back
String name = className.substring(0, className.length() – 6);
Class clazz = getClass(f, name);
Class[] interfaces = clazz.getInterfaces();
for (Class c : interfaces) {
// Implement the IPlugin interface
if (c.getName().equals(“base.IPlugin”)) {
pluginCollection.add((IPlugin)clazz.newInstance());
}
}
}
}
}

protected List<String> getClassNames(String jarName) throws IOException {
ArrayList<String> classes = new ArrayList<String>(10);
JarInputStream jarFile = new JarInputStream(new FileInputStream(jarName));
JarEntry jarEntry;
while (true) {
jarEntry = jarFile.getNextJarEntry();
if (jarEntry == null) {
break;
}
if (jarEntry.getName().endsWith(“.class”)) {
classes.add(jarEntry.getName().replaceAll(“/”, “\\.”));
}
}

return classes;
}

public Class getClass(File file, String name) throws Exception {
addURL(file.toURL());

URLClassLoader clazzLoader;
Class clazz;
String filePath = file.getAbsolutePath();
filePath = “jar:file://” + filePath + “!/”;
URL url = new File(filePath).toURL();
clazzLoader = new URLClassLoader(new URL[]{url});
clazz = clazzLoader.loadClass(name);
return clazz;

}

public 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())) {
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”);
}
}

public List<IPlugin> getPluginCollection() {
return pluginCollection;
}

public void setPluginCollection(List<IPlugin> pluginCollection) {
this.pluginCollection = pluginCollection;
}
}

The search method

  1. searches for all jar files under a particular folder,
  2. retrieves all classes in the jar,
  3. check if the class implements IPlugin interface
  4. load the corresponding plugin

getClassNames retrieves all the names of the classes in the jar file.

getClass creates the class.

addURL adds the jar file to the existing CLASSPATH.

Below is the test stub that I used.

public class TestPluginFinder {
public static void main(String[] args){
try {
PluginFinder pluginFinder = new PluginFinder();
pluginFinder.search(“c:/temp/sample”);
List<IPlugin> pluginCollection = pluginFinder.getPluginCollection();
for (IPlugin plugin: pluginCollection){
System.out.println(“Found ” + plugin.getName());
}
} catch (Exception e) {
System.out.println(“Error: ” + e.getMessage());
}
}
}


Trackback URL


RSS Feed for This Post6 Comment(s)

  1. Steven Devijver | Oct 8, 2007 | Reply

    With Spring you can do this out of the box, without a single line of code.

  2. thoughtworks | Oct 8, 2007 | Reply

    Steven,
    This piece of code is not really meant for web application. Actually there are a lot of web application frameworks out there that can do this, not just Spring. What I see that there is a lack of framework meant for batch or application in Java

  3. Richard | Nov 30, 2007 | Reply

    Excellent post! I didn’t want bulky frameworks. Java already has too much frameworks. This is just some java code that works.

    Thanks

  4. James Fry | Jan 26, 2008 | Reply

    Spring isn’t just for web applications – it is great within alsorts of things from server side EJBs, webapps etc through to desktop rich clients (indeed there is a Spring Rich Client framework now).

    There is the “Java Plugin Framework” which is based on the Eclipse 2.0 plugin loader. Really powerful, especially when combined with other tools such as Spring. http://jpf.sourceforge.net/

  5. MikeW | Oct 30, 2008 | Reply

    Hi there,

    I am currently facing the same problem of building an extensible application. After considering JPF, looking at the Eclipse architecture, etc, I stumbled upon a nice little API in Java 1.6 called ServiceLoader.

    Serviceloader allows you to dynamically load and use classes that implement a certain interface (provide a certain service). The implementing classes are called `service providers’ and they can be added just by placing them on the classpath of the application.

    Contrary to many other platforms (OSGi, JPF) it is well documented and simple to use. See the tutorial on http://java.sun.com/developer/technicalArticles/javase/extensible/

    Worth checking out!!

    Cheers, MikeW

  6. jjMan | Dec 8, 2009 | Reply

    I know this is a pretty old post but thanks MikeW!
    I think that was exactly what I was looking for…
    Cheers!

Sorry, comments for this entry are closed at this time.