From a31176489ce6e5dbef52d83b90dfe23ed13a2ba6 Mon Sep 17 00:00:00 2001 From: kohsuke Date: Tue, 7 Jul 2009 18:35:32 +0000 Subject: [PATCH] Using AntClassLoader with Closeable so that we can predictably release jar files opened by URLClassLoader git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@19472 71c3de6d-444a-0410-be80-ed276b4c234a --- .../java/hudson/ClassicPluginStrategy.java | 45 ++++++++++++++----- core/src/main/java/hudson/PluginManager.java | 3 +- core/src/main/java/hudson/PluginWrapper.java | 17 +++++-- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index 82f9612bcc..6b239e1072 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -33,15 +33,18 @@ import java.io.FileInputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; +import java.io.Closeable; import java.net.URL; -import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; +import java.util.Arrays; +import java.util.Collection; import java.util.jar.Manifest; import java.util.logging.Logger; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; +import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.taskdefs.Expand; import org.apache.tools.ant.types.FileSet; @@ -115,7 +118,7 @@ public class ClassicPluginStrategy implements PluginStrategy { // TODO: define a mechanism to hide classes // String export = manifest.getMainAttributes().getValue("Export"); - List paths = new ArrayList(); + List paths = new ArrayList(); if (isLinked) { parseClassPath(manifest, archive, paths, "Libraries", ","); parseClassPath(manifest, archive, paths, "Class-Path", " +"); // backward @@ -127,13 +130,11 @@ public class ClassicPluginStrategy implements PluginStrategy { } else { File classes = new File(expandDir, "WEB-INF/classes"); if (classes.exists()) - paths.add(classes.toURI().toURL()); + paths.add(classes); File lib = new File(expandDir, "WEB-INF/lib"); File[] libs = lib.listFiles(JAR_FILTER); - if (libs != null) { - for (File jar : libs) - paths.add(jar.toURI().toURL()); - } + if (libs != null) + paths.addAll(Arrays.asList(libs)); baseResourceURL = expandDir.toURI().toURL(); } @@ -176,8 +177,10 @@ public class ClassicPluginStrategy implements PluginStrategy { ClassLoader dependencyLoader = new DependencyClassLoader(getClass() .getClassLoader(), Util.join(dependencies,optionalDependencies)); - ClassLoader classLoader = new URLClassLoader(paths.toArray(new URL[paths.size()]), - dependencyLoader); + + // using AntClassLoader with Closeable so that we can predictably release jar files opened by URLClassLoader + AntClassLoader2 classLoader = new AntClassLoader2(dependencyLoader); + classLoader.addPathFiles(paths); return new PluginWrapper(archive, manifest, baseResourceURL, classLoader, disableFile, dependencies, optionalDependencies); @@ -247,7 +250,7 @@ public class ClassicPluginStrategy implements PluginStrategy { return new File(base.getParentFile(),relative); } - private static void parseClassPath(Manifest manifest, File archive, List paths, String attributeName, String separator) throws IOException { + private static void parseClassPath(Manifest manifest, File archive, List paths, String attributeName, String separator) throws IOException { String classPath = manifest.getMainAttributes().getValue(attributeName); if(classPath==null) return; // attribute not found for (String s : classPath.split(separator)) { @@ -259,12 +262,12 @@ public class ClassicPluginStrategy implements PluginStrategy { fs.setDir(dir); fs.setIncludes(file.getName()); for( String included : fs.getDirectoryScanner(new Project()).getIncludedFiles() ) { - paths.add(new File(dir,included).toURI().toURL()); + paths.add(new File(dir,included)); } } else { if(!file.exists()) throw new IOException("No such file: "+file); - paths.add(file.toURI().toURL()); + paths.add(file); } } } @@ -359,4 +362,22 @@ public class ClassicPluginStrategy implements PluginStrategy { // TODO: delegate resources? watch out for diamond dependencies } + + /** + * {@link AntClassLoader} with a few methods exposed and {@link Closeable} support. + */ + private static final class AntClassLoader2 extends AntClassLoader implements Closeable { + private AntClassLoader2(ClassLoader parent) { + super(parent,true); + } + + public void addPathFiles(Collection paths) throws IOException { + for (File f : paths) + addPathFile(f); + } + + public void close() throws IOException { + cleanup(); + } + } } diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 2052160caa..df5321408e 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -322,9 +322,8 @@ public final class PluginManager extends AbstractModelObject { * Orderly terminates all the plugins. */ public void stop() { - for (PluginWrapper p : activePlugins) { + for (PluginWrapper p : activePlugins) p.stop(); - } // Work around a bug in commons-logging. // See http://www.szegedi.org/articles/memleak.html LogFactory.release(uberClassLoader); diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index fd6fc5a1ad..a53e7c3f3d 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -30,13 +30,14 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.Closeable; import java.net.URL; import java.util.List; import java.util.jar.Manifest; import java.util.logging.Logger; +import static java.util.logging.Level.WARNING; import org.apache.commons.logging.LogFactory; -import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; /** @@ -283,6 +284,13 @@ public final class PluginWrapper { // Work around a bug in commons-logging. // See http://www.szegedi.org/articles/memleak.html LogFactory.release(classLoader); + + if (classLoader instanceof Closeable) + try { + ((Closeable) classLoader).close(); + } catch (IOException e) { + LOGGER.log(WARNING, "Failed to shut down classloader",e); + } } /** @@ -369,12 +377,13 @@ public final class PluginWrapper { // Action methods // // - public void doMakeEnabled(StaplerRequest req, StaplerResponse rsp) throws IOException { - Hudson.getInstance().checkPermission(Hudson.ADMINISTER); + public void doMakeEnabled(StaplerResponse rsp) throws IOException { + Hudson.getInstance().checkPermission(Hudson.ADMINISTER); enable(); rsp.setStatus(200); } - public void doMakeDisabled(StaplerRequest req, StaplerResponse rsp) throws IOException { + + public void doMakeDisabled(StaplerResponse rsp) throws IOException { Hudson.getInstance().checkPermission(Hudson.ADMINISTER); disable(); rsp.setStatus(200); -- GitLab