PluginClassLoader.java 4.42 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
/**
 * $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;

import org.jivesoftware.util.Log;

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>
 * <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.
 * </ul>
 *
 * @author Derek DeMoro
 */
class PluginClassLoader {

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


    /**
     * Constructs a plugin loader for the given plugin directory.
     *
     * @param pluginDir the plugin directory.
     * @throws java.lang.SecurityException if the created class loader violates
     *          existing security constraints.
     */
    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");
        if (classesDir.exists()) {
            list.add(classesDir.toURL());
        }
        File libDir = new File(directory, "lib");
        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());
                }
            }
        }
        }
        catch (MalformedURLException mue) {
            Log.error(mue);
        }
    }

    /**
     * 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.
     */
    public void addURL(URL url) {
        list.add(url);
    }

    /**
     * Initializes the class loader with all configured classpath URLs. This method
     * can be called multiple times if the list of URLs changes.
     */
    public void initialize(){
        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).
     * @throws SecurityException      if the custom class loader not allowed.
     */
    public Class loadClass(String name) throws ClassNotFoundException, IllegalAccessException,
            InstantiationException, SecurityException {
        return classLoader.loadClass(name);
    }

    /**
     * Destroys this class loader.
     */
    public void destroy() {
        classLoader = null;
    }

    /**
     * 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;
    }
}