From 303a9f7df70b702dfa3df1fa5bdf0a2afe9b1445 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Sat, 17 Jun 2017 17:00:10 -0400 Subject: [PATCH] [FIXED JENKINS-44898] Add findResource to PluginFirstClassLoader (#2916) * [FIXED JENKINS-44898] Add findResource to PluginFirstClassLoader This fixes GroovyClassLoader.loadClass for a .groovy file in a plugin with a PluginFirstClassLoader, specifically by fixing fast-loading via the UberClassLoader. * Move common code to AntWithFindResourceClassLoader. * Adding testing of new PluginFirstClassLoader behavior. --- .../java/hudson/ClassicPluginStrategy.java | 50 ++------------- .../java/hudson/PluginFirstClassLoader.java | 13 ++-- .../util/AntWithFindResourceClassLoader.java | 60 ++++++++++++++++++ .../test/java/hudson/PluginManagerTest.java | 18 ++++++ .../test/resources/plugins/plugin-first.hpi | Bin 0 -> 9523 bytes 5 files changed, 91 insertions(+), 50 deletions(-) create mode 100644 core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java create mode 100644 test/src/test/resources/plugins/plugin-first.hpi diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index d4745a7653..5981ade069 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -26,6 +26,8 @@ package hudson; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.InvalidPathException; + +import jenkins.util.AntWithFindResourceClassLoader; import jenkins.util.SystemProperties; import com.google.common.collect.Lists; import hudson.Plugin.DummyImpl; @@ -830,53 +832,11 @@ public class ClassicPluginStrategy implements PluginStrategy { /** * {@link AntClassLoader} with a few methods exposed, {@link Closeable} support, and {@link Transformer} support. */ - private final class AntClassLoader2 extends AntClassLoader implements Closeable { - private final Vector pathComponents; - + private final class AntClassLoader2 extends AntWithFindResourceClassLoader implements Closeable { private AntClassLoader2(ClassLoader parent) { - super(parent,true); - - try { - Field $pathComponents = AntClassLoader.class.getDeclaredField("pathComponents"); - $pathComponents.setAccessible(true); - pathComponents = (Vector)$pathComponents.get(this); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new Error(e); - } + super(parent, true); } - - - public void addPathFiles(Collection paths) throws IOException { - for (File f : paths) - addPathFile(f); - } - - public void close() throws IOException { - cleanup(); - } - - /** - * As of 1.8.0, {@link AntClassLoader} doesn't implement {@link #findResource(String)} - * in any meaningful way, which breaks fast lookup. Implement it properly. - */ - @Override - protected URL findResource(String name) { - URL url = null; - - // try and load from this loader if the parent either didn't find - // it or wasn't consulted. - Enumeration e = pathComponents.elements(); - while (e.hasMoreElements() && url == null) { - File pathComponent = (File) e.nextElement(); - url = getResourceURL(pathComponent, name); - if (url != null) { - log("Resource " + name + " loaded from ant loader", Project.MSG_DEBUG); - } - } - - return url; - } - + @Override protected Class defineClassFromData(File container, byte[] classData, String classname) throws IOException { if (!DISABLE_TRANSFORMER) diff --git a/core/src/main/java/hudson/PluginFirstClassLoader.java b/core/src/main/java/hudson/PluginFirstClassLoader.java index 41ca562e6b..018e5ea198 100644 --- a/core/src/main/java/hudson/PluginFirstClassLoader.java +++ b/core/src/main/java/hudson/PluginFirstClassLoader.java @@ -33,7 +33,7 @@ import java.util.Collection; import java.util.Enumeration; import java.util.List; -import org.apache.tools.ant.AntClassLoader; +import jenkins.util.AntWithFindResourceClassLoader; /** * classLoader which use first /WEB-INF/lib/*.jar and /WEB-INF/classes before core classLoader @@ -42,10 +42,14 @@ import org.apache.tools.ant.AntClassLoader; * @since 1.371 */ public class PluginFirstClassLoader - extends AntClassLoader + extends AntWithFindResourceClassLoader implements Closeable { - + + public PluginFirstClassLoader() { + super(null, false); + } + private List urls = new ArrayList(); public void addPathFiles( Collection paths ) @@ -100,6 +104,5 @@ public class PluginFirstClassLoader { InputStream is = super.getResourceAsStream( name ); return is; - } - + } } diff --git a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java new file mode 100644 index 0000000000..048dbcdf1c --- /dev/null +++ b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java @@ -0,0 +1,60 @@ +package jenkins.util; + +import org.apache.tools.ant.Project; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.URL; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Vector; + +/** + * As of 1.8.0, {@link org.apache.tools.ant.AntClassLoader} doesn't implement {@link #findResource(String)} + * in any meaningful way, which breaks fast lookup. Implement it properly. + */ +public class AntWithFindResourceClassLoader extends AntClassLoader implements Closeable { + private final Vector pathComponents; + + public AntWithFindResourceClassLoader(ClassLoader parent, boolean parentFirst) { + super(parent, parentFirst); + + try { + Field $pathComponents = AntClassLoader.class.getDeclaredField("pathComponents"); + $pathComponents.setAccessible(true); + pathComponents = (Vector)$pathComponents.get(this); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new Error(e); + } + } + + public void addPathFiles(Collection paths) throws IOException { + for (File f : paths) + addPathFile(f); + } + + public void close() throws IOException { + cleanup(); + } + + @Override + protected URL findResource(String name) { + URL url = null; + + // try and load from this loader if the parent either didn't find + // it or wasn't consulted. + Enumeration e = pathComponents.elements(); + while (e.hasMoreElements() && url == null) { + File pathComponent = (File) e.nextElement(); + url = getResourceURL(pathComponent, name); + if (url != null) { + log("Resource " + name + " loaded from ant loader", Project.MSG_DEBUG); + } + } + + return url; + } + +} diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index a19792b3a7..b384c387b8 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -43,6 +43,8 @@ import java.net.URLClassLoader; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; + +import jenkins.ClassLoaderReflectionToolkit; import jenkins.RestartRequiredException; import net.sf.json.JSONArray; import net.sf.json.JSONObject; @@ -515,4 +517,20 @@ public class PluginManagerTest { assertNotNull(r.jenkins.getPluginManager().getPlugin("Parameterized-Remote-Trigger")); assertNotNull(r.jenkins.getPluginManager().getPlugin("token-macro")); } + + @Issue("JENKINS-44898") + @WithPlugin("plugin-first.hpi") + @Test + public void findResourceForPluginFirstClassLoader() throws Exception { + PluginWrapper w = r.jenkins.getPluginManager().getPlugin("plugin-first"); + assertNotNull(w); + + URL fromPlugin = w.classLoader.getResource("org/jenkinsci/plugins/pluginfirst/HelloWorldBuilder/config.jelly"); + assertNotNull(fromPlugin); + + // This is how UberClassLoader.findResource functions. + URL fromToolkit = ClassLoaderReflectionToolkit._findResource(w.classLoader, "org/jenkinsci/plugins/pluginfirst/HelloWorldBuilder/config.jelly"); + + assertEquals(fromPlugin, fromToolkit); + } } diff --git a/test/src/test/resources/plugins/plugin-first.hpi b/test/src/test/resources/plugins/plugin-first.hpi new file mode 100644 index 0000000000000000000000000000000000000000..71157eb83180e97bca7463703deb068bb3964951 GIT binary patch literal 9523 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