Mischiefblog
I make apps for other people

Plug-in architectures in Java

Posted by Chris Jones
On March 14th, 2010 at 17:56

Permalink | Trackback | Links In |

Comments (2) |
Posted in Java

An off-hand discussion at work (“This application is being used by more groups now . . . wouldn’t it be cool to make it so they can develop independently?”) led to me looking into Java classloaders this weekend. Here’s the core for an application that’s treated like a bus in that it looks to see what’s plugged into it and provides communications (context) to each plug-in.





    private void audit() {
        // get the plugin JARs
        File basedir = new File(System.getProperty("user.dir"+ File.separator + "dynamic");

        System.err.println("Searching " + basedir + " for pluggable JARs");

        if (basedir.isDirectory() && basedir.canRead()) {
            // get a list of jar files
            String [] jars = basedir.list(new FilenameFilter() {
                public boolean accept(File file, String s) {
                    return s.endsWith(".jar");
                }
            });

            URL [] urlJars = new URL[jars.length];
            List<String> initializers = new ArrayList<String>(jars.length);
            int i = 0;
            for (String jar : jars) {
                String jarpath = basedir.getAbsolutePath() + File.separator + jar;

                // get the plug-in entry point from the jar
                File jarfile = new File(jarpath);
                if (jarfile.canRead()) {
                    // read the pluggable manifest
                    JarFile jf = null;

                    try {
                        jf = new JarFile(jarpath);

                        JarEntry entry = jf.getJarEntry("pluggable.txt");
                        if (entry != null) {
                            // manifest file found
                            BufferedInputStream bis = null;
                            String plugin = null;

                            try {
                                bis = new BufferedInputStream(jf.getInputStream(jf.getEntry("pluggable.txt")));
                                // todo should make sure bytes are available...
                                byte [] inBuf = new byte[1024];

                                // should be short:  one line, pointing to a single class
                                bis.read(inBuf);

                                // todo validate plug-in class
                                plugin = new String(inBuf).trim();

                                if (plugin.length() 1) {
                                    System.err.println("Plug-in class name not found");
                                    plugin = null;
                                else if (initializers.contains(plugin)) {
                                    System.err.println("Duplicate plug-in class name");
                                    plugin = null;
                                }
                            catch (IOException e) {
                                // couldn't open plug-in manifest
                                e.printStackTrace();
                            finally {
                                if (bis != null) {
                                    try {
                                        bis.close();
                                    catch (IOException e) {
                                        // do nothing
                                    }
                                }
                            }

                            // only use the jar if the plug-in has a manifest class
                            if (plugin != null) {
                                try {
                                    urlJars[i++new URL("file:///" + jarpath);
                                    initializers.add(plugin);
                                catch (MalformedURLException e) {
                                    e.printStackTrace();
                                }
                            }
                        }

                    catch (IOException e) {
                        // couldn't open jar file
                        e.printStackTrace();
                    finally {
                        if (jf != null) {
                            try {
                                jf.close();
                            catch (IOException e) {
                                // do nothing
                            }
                        }
                    }

                }
            }

            // load the pluggables
            ClassLoader loader = URLClassLoader.newInstance(urlJars);

            // load each pluggable initializer
            for (String pluggableClass : initializers) {
                try {
                    // load the pluggable class
                    Class clz = loader.loadClass(pluggableClass);
                    Pluggable pluggable = (Pluggable)clz.newInstance();

                    // initialize the plug-in
                    Method method = clz.getMethod("initializePlugin", PluggableContext.class);
                    method.invoke(pluggable, context);
                catch (ClassNotFoundException e) {
                    e.printStackTrace();
                catch (NoSuchMethodException e) {
                    e.printStackTrace();
                catch (InstantiationException e) {
                    e.printStackTrace();
                catch (IllegalAccessException e) {
                    e.printStackTrace();
                catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }


Java2html

2 Responses to “Plug-in architectures in Java”

  1. Josh Says:

    Isn’t this something that you can use osgi for? I am no osgi guy, but from the little I’ve read, anytime I want to have a pluggable classloader, osgi is the way to go.

    You back in cincy, or still out in the NW US?

  2. Chris Says:

    Surprisingly, I hadn’t run across OSGI before Amazon or after. I thought of this code example more like the dynamic WAR loader in Tomcat or another container without the directory monitoring implemented.

    OSGI looks like it provides a good set of high-level service calls if implemented completely and on top of standards-based libraries.