PluginClassLoader.java 4.42 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5 6 7 8 9 10 11 12 13
/**
 * $RCSfile$
 * $Revision$
 * $Date$
 *
 * Copyright (C) 2004 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

package org.jivesoftware.messenger.container;

14 15
import org.jivesoftware.util.Log;

Matt Tucker's avatar
Matt Tucker committed
16 17 18 19 20 21 22 23 24 25 26 27 28
import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * ClassLoader for plugins. It searches the plugin directory for classes
 * and JAR files, then constructs a class loader for the resources found.
 * Resources are loaded as follows:<ul>
29 30 31
 * <p/>
 * <li>Any JAR files in the <tt>lib</tt> will be added to the classpath.
 * <li>Any files in the classes directory will be added to the classpath.
Matt Tucker's avatar
Matt Tucker committed
32 33 34 35 36 37 38
 * </ul>
 *
 * @author Derek DeMoro
 */
class PluginClassLoader {

    private URLClassLoader classLoader;
39
    private final List<URL> list = new ArrayList<URL>();
40

Matt Tucker's avatar
Matt Tucker committed
41 42 43 44 45

    /**
     * Constructs a plugin loader for the given plugin directory.
     *
     * @param pluginDir the plugin directory.
46 47
     * @throws java.lang.SecurityException if the created class loader violates
     *          existing security constraints.
Matt Tucker's avatar
Matt Tucker committed
48
     */
49 50 51 52 53 54 55 56 57 58 59 60 61
    public PluginClassLoader(File pluginDir) throws SecurityException {
        addDirectory(pluginDir);
    }

    /**
     * Adds a directory to the class loader. The {@link #initialize()} method should be called
     * after adding the directory to make the change take effect.
     *
     * @param directory the directory.
     */
    public void addDirectory(File directory) {
        try {
        File classesDir = new File(directory, "classes");
Matt Tucker's avatar
Matt Tucker committed
62 63 64
        if (classesDir.exists()) {
            list.add(classesDir.toURL());
        }
65
        File libDir = new File(directory, "lib");
Matt Tucker's avatar
Matt Tucker committed
66 67 68 69 70 71 72 73 74 75 76 77
        File[] jars = libDir.listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".jar") || name.endsWith(".zip");
            }
        });
        if (jars != null) {
            for (int i = 0; i < jars.length; i++) {
                if (jars[i] != null && jars[i].isFile()) {
                    list.add(jars[i].toURL());
                }
            }
        }
78 79 80 81
        }
        catch (MalformedURLException mue) {
            Log.error(mue);
        }
82 83
    }

84 85 86 87 88 89
    /**
     * Adds a URL to the class loader. The {@link #initialize()} method should be called
     * after adding the URL to make the change take effect.
     *
     * @param url the url.
     */
90 91 92 93
    public void addURL(URL url) {
        list.add(url);
    }

94 95 96 97
    /**
     * Initializes the class loader with all configured classpath URLs. This method
     * can be called multiple times if the list of URLs changes.
     */
98
    public void initialize(){
Matt Tucker's avatar
Matt Tucker committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
        Iterator urls = list.iterator();
        URL[] urlArray = new URL[list.size()];
        for (int i = 0; urls.hasNext(); i++) {
            urlArray[i] = (URL)urls.next();
        }
        classLoader = new URLClassLoader(urlArray, findParentClassLoader());
    }

    /**
     * Load a class using this plugin class loader.
     *
     * @param name the fully qualified name of the class to load.
     * @return The module object loaded
     * @throws ClassNotFoundException if the class could not be loaded by this class loader.
     * @throws IllegalAccessException if the class constructor was private or protected.
     * @throws InstantiationException if the class could not be instantiated (initialization error).
115
     * @throws SecurityException      if the custom class loader not allowed.
Matt Tucker's avatar
Matt Tucker committed
116 117
     */
    public Class loadClass(String name) throws ClassNotFoundException, IllegalAccessException,
118
            InstantiationException, SecurityException {
Matt Tucker's avatar
Matt Tucker committed
119 120 121
        return classLoader.loadClass(name);
    }

122 123 124 125 126 127 128
    /**
     * Destroys this class loader.
     */
    public void destroy() {
        classLoader = null;
    }

Matt Tucker's avatar
Matt Tucker committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    /**
     * Locates the best parent class loader based on context.
     *
     * @return the best parent classloader to use.
     */
    private ClassLoader findParentClassLoader() {
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        if (parent == null) {
            parent = this.getClass().getClassLoader();
        }
        if (parent == null) {
            parent = ClassLoader.getSystemClassLoader();
        }
        return parent;
    }
}