PluginClassLoader.java 5.85 KB
Newer Older
1
/*
2
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
3
 *
4 5 6 7 8 9 10 11 12 13 14
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
15 16
 */

17
package org.jivesoftware.openfire.container;
18 19 20

import java.io.File;
import java.io.FilenameFilter;
21
import java.net.JarURLConnection;
22 23 24
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
25 26 27
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
28
import java.util.Set;
29

30 31 32 33
import org.jivesoftware.openfire.XMPPServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

34
/**
35 36 37
 * 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:
38 39 40
 * <ul>
 * <li>Any JAR files in the <tt>lib</tt> will be added to the classpath.</li>
 * <li>Any files in the classes directory will be added to the classpath.</li>
41 42 43 44
 * </ul>
 *
 * @author Derek DeMoro
 */
45
public class PluginClassLoader extends URLClassLoader {
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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	private static final Logger Log = LoggerFactory.getLogger(PluginClassLoader.class);
	private List<JarURLConnection> cachedJarFiles = new ArrayList<JarURLConnection>();
    public PluginClassLoader() {
        super(new URL[] {}, findParentClassLoader());
    }

    /**
     * Adds a directory to the class loader.
     *
     * @param directory the directory.
     * @param developmentMode true if the plugin is running in development mode. This
     *      resolves classloader conflicts between the deployed plugin
     * and development classes.
     */
    public void addDirectory(File directory, boolean developmentMode) {
        try {
            // Add classes directory to classpath.
            File classesDir = new File(directory, "classes");
            if (classesDir.exists()) {
                addURL(classesDir.toURI().toURL());
            }

            // Add i18n directory to classpath.
            File databaseDir = new File(directory, "database");
            if(databaseDir.exists()){
                addURL(databaseDir.toURI().toURL());
            }

            // Add i18n directory to classpath.
            File i18nDir = new File(directory, "i18n");
            if(i18nDir.exists()){
                addURL(i18nDir.toURI().toURL());
            }

            // Add web directory to classpath.
            File webDir = new File(directory, "web");
            if(webDir.exists()){
                addURL(webDir.toURI().toURL());
            }

            // Add lib directory to classpath.
            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()) {
                    	String jarFileUri = jars[i].toURI().toString()  + "!/";
                        if (developmentMode) {
                            // Do not add plugin-pluginName.jar to classpath.
                            if (!jars[i].getName().equals("plugin-" + directory.getName() + ".jar")) {
                                addURLFile(new URL("jar", "", -1, jarFileUri));
                            }
                        } else {
	                        addURLFile(new URL("jar", "", -1, jarFileUri));
                        }
                    }
                }
            }
        }
        catch (MalformedURLException mue) {
            Log.error(mue.getMessage(), mue);
        }
    }

    /**
     * Add the given URL to the classpath for this class loader, 
     * caching the JAR file connection so it can be unloaded later
     * 
     * @param file URL for the JAR file or directory to append to classpath
     */
    public void addURLFile(URL file) {
    	try {
        	// open and cache JAR file connection 
        	URLConnection uc = file.openConnection();
        	if (uc instanceof JarURLConnection) {
            	uc.setUseCaches(true);
            	((JarURLConnection) uc).getManifest();
            	cachedJarFiles.add((JarURLConnection)uc);
        	}
    	} catch (Exception e) {
    		Log.warn("Failed to cache plugin JAR file: " + file.toExternalForm());
    	}
        addURL(file);
    }
    
    /**
     * Unload any JAR files that have been cached by this plugin
     */
    public void unloadJarFiles() {
        for (JarURLConnection url : cachedJarFiles) {
        	try {
        		Log.info("Unloading plugin JAR file " + url.getJarFile().getName());
        		url.getJarFile().close();
        	} catch (Exception e) {
        		Log.error("Failed to unload JAR file", e);
        	}
        }
    }

    /**
     * Locates the best parent class loader based on context.
     *
     * @return the best parent classloader to use.
     */
    private static ClassLoader findParentClassLoader() {
        ClassLoader parent = XMPPServer.class.getClassLoader();
        if (parent == null) {
            parent = PluginClassLoader.class.getClassLoader();
        }
        if (parent == null) {
            parent = ClassLoader.getSystemClassLoader();
        }
        return parent;
    }
165
}