PluginClassLoader.java 6.35 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/**
 * $RCSfile$
 * $Revision: 2993 $
 * $Date: 2005-10-24 18:11:33 -0300 (Mon, 24 Oct 2005) $
 *
 * 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.wildfire.container;

import org.jivesoftware.util.Log;
15
import org.jivesoftware.wildfire.XMPPServer;
16 17 18 19 20 21 22

import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
23
import java.util.Collection;
24 25 26 27 28 29 30 31 32 33 34 35 36 37
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
 */
38
public class PluginClassLoader {
39 40 41 42 43 44 45 46

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

    /**
     * Constructs a plugin loader for the given plugin directory.
     *
     * @throws SecurityException if the created class loader violates
47
     *                           existing security constraints.
48
     */
49
    public PluginClassLoader() throws SecurityException {
50 51 52 53 54 55 56
    }

    /**
     * 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.
57 58
     * @param developmentMode true if the plugin is running in development mode. This
     *      resolves classloader conflicts between the deployed plugin
59
     * and development classes.
60
     */
61
    public void addDirectory(File directory, boolean developmentMode) {
62
        try {
63
            // Add classes directory to classpath.
64 65 66 67
            File classesDir = new File(directory, "classes");
            if (classesDir.exists()) {
                list.add(classesDir.toURL());
            }
68

69 70 71 72 73 74
            // Add i18n directory to classpath.
            File databaseDir = new File(directory, "database");
            if(databaseDir.exists()){
                list.add(databaseDir.toURL());
            }

75 76 77 78 79 80 81
            // Add i18n directory to classpath.
            File i18nDir = new File(directory, "i18n");
            if(i18nDir.exists()){
                list.add(i18nDir.toURL());
            }

            // Add lib directory to classpath.
82 83 84 85 86 87 88 89 90
            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()) {
91 92 93 94 95 96 97 98 99
                        if (developmentMode) {
                            // Do not add plugin-pluginName.jar to classpath.
                            if (!jars[i].getName().equals("plugin-" + directory.getName() + ".jar")) {
                                list.add(jars[i].toURL());
                            }
                        }
                        else {
                            list.add(jars[i].toURL());
                        }
100 101 102 103 104 105 106 107 108
                    }
                }
            }
        }
        catch (MalformedURLException mue) {
            Log.error(mue);
        }
    }

109
    public Collection<URL> getURLS() {
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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
        return list;
    }

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

        // If the classloader is to be used by a child plugin, we should
        // never use the ContextClassLoader, but only reuse the plugin classloader itself.
        if (classLoader != null) {
            classLoader = new URLClassLoader(urlArray, classLoader);
        }
        else {
            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() {
172
        ClassLoader parent = XMPPServer.class.getClassLoader();
173 174 175 176 177 178 179 180
        if (parent == null) {
            parent = this.getClass().getClassLoader();
        }
        if (parent == null) {
            parent = ClassLoader.getSystemClassLoader();
        }
        return parent;
    }
181 182 183

    /**
     * Returns the URLClassloader used.
184
     *
185 186
     * @return the URLClassLoader used.
     */
187
    public ClassLoader getClassLoader() {
188 189
        return classLoader;
    }
190
}