Commit ea22a488 authored by Tom Evans's avatar Tom Evans Committed by tevans

OF-197: Keep track of jar files loaded by each plugin; unload jars when the plugin is destroyed

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13395 b35dd754-fafc-0310-a699-88a17e54d16e
parent 62004133
...@@ -22,9 +22,13 @@ package org.jivesoftware.openfire.container; ...@@ -22,9 +22,13 @@ package org.jivesoftware.openfire.container;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.net.JarURLConnection;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -44,7 +48,7 @@ import org.slf4j.LoggerFactory; ...@@ -44,7 +48,7 @@ import org.slf4j.LoggerFactory;
public class PluginClassLoader extends URLClassLoader { public class PluginClassLoader extends URLClassLoader {
private static final Logger Log = LoggerFactory.getLogger(PluginClassLoader.class); private static final Logger Log = LoggerFactory.getLogger(PluginClassLoader.class);
private List<JarURLConnection> cachedJarFiles = new ArrayList<JarURLConnection>();
public PluginClassLoader() { public PluginClassLoader() {
super(new URL[] {}, findParentClassLoader()); super(new URL[] {}, findParentClassLoader());
} }
...@@ -93,14 +97,14 @@ public class PluginClassLoader extends URLClassLoader { ...@@ -93,14 +97,14 @@ public class PluginClassLoader extends URLClassLoader {
if (jars != null) { if (jars != null) {
for (int i = 0; i < jars.length; i++) { for (int i = 0; i < jars.length; i++) {
if (jars[i] != null && jars[i].isFile()) { if (jars[i] != null && jars[i].isFile()) {
String jarFileUri = jars[i].toURI().toString() + "!/";
if (developmentMode) { if (developmentMode) {
// Do not add plugin-pluginName.jar to classpath. // Do not add plugin-pluginName.jar to classpath.
if (!jars[i].getName().equals("plugin-" + directory.getName() + ".jar")) { if (!jars[i].getName().equals("plugin-" + directory.getName() + ".jar")) {
addURL(jars[i].toURL()); addURLFile(new URL("jar", "", -1, jarFileUri));
} }
} } else {
else { addURLFile(new URL("jar", "", -1, jarFileUri));
addURL(jars[i].toURL());
} }
} }
} }
...@@ -111,9 +115,40 @@ public class PluginClassLoader extends URLClassLoader { ...@@ -111,9 +115,40 @@ public class PluginClassLoader extends URLClassLoader {
} }
} }
/**
* 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) { 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); 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. * Locates the best parent class loader based on context.
......
...@@ -634,6 +634,13 @@ public class PluginManager { ...@@ -634,6 +634,13 @@ public class PluginManager {
File pluginFile = pluginDirs.remove(plugin); File pluginFile = pluginDirs.remove(plugin);
PluginClassLoader pluginLoader = classloaders.remove(plugin); PluginClassLoader pluginLoader = classloaders.remove(plugin);
// try to close the cached jar files from the plugin class loader
if (pluginLoader != null) {
pluginLoader.unloadJarFiles();
} else {
Log.warn("No plugin loader found for " + pluginName);
}
// Try to remove the folder where the plugin was exploded. If this works then // Try to remove the folder where the plugin was exploded. If this works then
// the plugin was successfully removed. Otherwise, some objects created by the // the plugin was successfully removed. Otherwise, some objects created by the
// plugin are still in memory. // plugin are still in memory.
...@@ -644,10 +651,9 @@ public class PluginManager { ...@@ -644,10 +651,9 @@ public class PluginManager {
// Ask the system to clean up references. // Ask the system to clean up references.
System.gc(); System.gc();
int count = 0; int count = 0;
while (!deleteDir(dir) && count < 5) { while (!deleteDir(dir) && count++ < 5) {
Log.warn("Error unloading plugin " + pluginName + ". " + "Will attempt again momentarily."); Log.warn("Error unloading plugin " + pluginName + ". " + "Will attempt again momentarily.");
Thread.sleep(8000); Thread.sleep(8000);
count++;
// Ask the system to clean up references. // Ask the system to clean up references.
System.gc(); System.gc();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment