From 9ce1ff4e7c31fe10d43fc9791ee4cbbe604e2979 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Tue, 25 Apr 2023 00:39:41 -0700 Subject: [PATCH] Remove `AntClassLoader` (#7846) --- .../java/hudson/ClassicPluginStrategy.java | 49 ++------- .../java/hudson/PluginFirstClassLoader.java | 99 ------------------ core/src/main/java/hudson/PluginWrapper.java | 8 +- .../java/jenkins/util/AntClassLoader.java | 73 ------------- .../test/java/hudson/PluginWrapperTest.java | 9 -- .../java/jenkins/util/AntClassLoaderTest.java | 47 --------- .../test/resources/plugins/loads-resource.jpi | Bin 12064 -> 0 bytes 7 files changed, 11 insertions(+), 274 deletions(-) delete mode 100644 core/src/main/java/hudson/PluginFirstClassLoader.java delete mode 100644 core/src/main/java/jenkins/util/AntClassLoader.java delete mode 100644 test/src/test/java/jenkins/util/AntClassLoaderTest.java delete mode 100644 test/src/test/resources/plugins/loads-resource.jpi diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index bc4b350c0c..c86b2af6ce 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -59,8 +59,6 @@ import java.util.logging.Logger; import jenkins.ClassLoaderReflectionToolkit; import jenkins.ExtensionFilter; import jenkins.plugins.DetachedPluginsUtil; -import jenkins.util.AntClassLoader; -import jenkins.util.SystemProperties; import jenkins.util.URLClassLoader2; import org.apache.commons.io.output.NullOutputStream; import org.apache.tools.ant.BuildException; @@ -290,30 +288,17 @@ public class ClassicPluginStrategy implements PluginStrategy { boolean usePluginFirstClassLoader = atts != null && Boolean.parseBoolean(atts.getValue("PluginFirstClassLoader")); - if (useAntClassLoader) { - AntClassLoader classLoader; - if (usePluginFirstClassLoader) { - classLoader = new PluginFirstClassLoader(); - classLoader.setParentFirst(false); - classLoader.setParent(parent); - } else { - classLoader = new AntClassLoader(parent, true); - } - classLoader.addPathFiles(paths); - return classLoader; - } else { - List urls = new ArrayList<>(); - for (File path : paths) { - urls.add(path.toURI().toURL()); - } - URLClassLoader2 classLoader; - if (usePluginFirstClassLoader) { - classLoader = new PluginFirstClassLoader2(urls.toArray(new URL[0]), parent); - } else { - classLoader = new URLClassLoader2(urls.toArray(new URL[0]), parent); - } - return classLoader; + List urls = new ArrayList<>(); + for (File path : paths) { + urls.add(path.toURI().toURL()); } + URLClassLoader2 classLoader; + if (usePluginFirstClassLoader) { + classLoader = new PluginFirstClassLoader2(urls.toArray(new URL[0]), parent); + } else { + classLoader = new URLClassLoader2(urls.toArray(new URL[0]), parent); + } + return classLoader; } /** @@ -432,17 +417,6 @@ public class ClassicPluginStrategy implements PluginStrategy { if (classLoader instanceof DependencyClassLoader) { return (DependencyClassLoader) classLoader; } - - if (classLoader instanceof AntClassLoader) { - // AntClassLoaders hold parents not only as AntClassLoader#getParent() - // but also as AntClassLoader#getConfiguredParent() - DependencyClassLoader ret = findAncestorDependencyClassLoader( - ((AntClassLoader) classLoader).getConfiguredParent() - ); - if (ret != null) { - return ret; - } - } } return null; } @@ -716,7 +690,4 @@ public class ClassicPluginStrategy implements PluginStrategy { return null; } } - - @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts") - public static /* not final */ boolean useAntClassLoader = SystemProperties.getBoolean(ClassicPluginStrategy.class.getName() + ".useAntClassLoader"); } diff --git a/core/src/main/java/hudson/PluginFirstClassLoader.java b/core/src/main/java/hudson/PluginFirstClassLoader.java deleted file mode 100644 index f652187a29..0000000000 --- a/core/src/main/java/hudson/PluginFirstClassLoader.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Olivier Lamy - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package hudson; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Collection; -import java.util.Enumeration; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import jenkins.util.AntClassLoader; - -/** - * classLoader which use first /WEB-INF/lib/*.jar and /WEB-INF/classes before core classLoader - * you must use the pluginFirstClassLoader true in the maven-hpi-plugin - * - * @author olamy - * @since 1.371 - * @deprecated use {@link PluginFirstClassLoader2} - */ -@Deprecated -public class PluginFirstClassLoader - extends AntClassLoader -{ - public PluginFirstClassLoader() { - super(null, false); - } - - private List urls = new CopyOnWriteArrayList<>(); - - @Override - public void addPathFiles(Collection paths) - throws IOException - { - for (File f : paths) - { - urls.add(f.toURI().toURL()); - addPathFile(f); - } - } - - /** - * @return List of jar used by the plugin /WEB-INF/lib/*.jar and classes directory /WEB-INF/classes - */ - public List getURLs() - { - return urls; - } - - @Override - protected Enumeration findResources(String name, boolean skipParent) - throws IOException - { - return super.findResources(name, skipParent); - } - - @Override - public Enumeration findResources(String name) - throws IOException - { - return super.findResources(name); - } - - @Override - public URL getResource(String name) - { - return super.getResource(name); - } - - @Override - public InputStream getResourceAsStream(String name) - { - return super.getResourceAsStream(name); - } -} diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java index 44956a5b3f..2161e50108 100644 --- a/core/src/main/java/hudson/PluginWrapper.java +++ b/core/src/main/java/hudson/PluginWrapper.java @@ -72,7 +72,6 @@ import jenkins.YesNoMaybe; import jenkins.model.Jenkins; import jenkins.plugins.DetachedPluginsUtil; import jenkins.security.UpdateSiteWarningsMonitor; -import jenkins.util.AntClassLoader; import jenkins.util.URLClassLoader2; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.LogFactory; @@ -414,12 +413,7 @@ public class PluginWrapper implements Comparable, ModelObject { */ @Restricted(Beta.class) public void injectJarsToClasspath(File... jars) throws Exception { - if (classLoader instanceof AntClassLoader) { - for (File f : jars) { - LOGGER.log(Level.CONFIG, () -> "Inserting " + f + " into " + shortName + " plugin's classpath"); - ((AntClassLoader) classLoader).addPathComponent(f); - } - } else if (classLoader instanceof URLClassLoader2) { + if (classLoader instanceof URLClassLoader2) { for (File f : jars) { LOGGER.log(Level.CONFIG, () -> "Inserting " + f + " into " + shortName + " plugin's classpath"); ((URLClassLoader2) classLoader).addURL(f.toURI().toURL()); diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java deleted file mode 100644 index 1b2685f984..0000000000 --- a/core/src/main/java/jenkins/util/AntClassLoader.java +++ /dev/null @@ -1,73 +0,0 @@ -package jenkins.util; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.Collection; -import java.util.Enumeration; -import jenkins.ClassLoaderReflectionToolkit; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.types.Path; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; - -/** - * {@link org.apache.tools.ant.AntClassLoader} with loosened visibility for use with {@link - * ClassLoaderReflectionToolkit}. - * - * @deprecated use {@link URLClassLoader2} - */ -@Deprecated -@Restricted(NoExternalUse.class) -public class AntClassLoader extends org.apache.tools.ant.AntClassLoader implements JenkinsClassLoader { - - public AntClassLoader(final ClassLoader parent, final Project project, final Path classpath) { - super(parent, project, classpath); - } - - public AntClassLoader() {} - - public AntClassLoader(final Project project, final Path classpath) { - super(project, classpath); - } - - public AntClassLoader(final ClassLoader parent, final Project project, - final Path classpath, final boolean parentFirst) { - super(parent, project, classpath, parentFirst); - } - - public AntClassLoader(final Project project, final Path classpath, - final boolean parentFirst) { - super(project, classpath, parentFirst); - } - - public AntClassLoader(final ClassLoader parent, final boolean parentFirst) { - super(parent, parentFirst); - } - - public void addPathFiles(Collection paths) throws IOException { - for (File f : paths) { - addPathFile(f); - } - } - - @Override - public URL findResource(final String name) { - return super.findResource(name); - } - - @Override - public Enumeration findResources(final String name) throws IOException { - return super.findResources(name); - } - - @Override - public Class findLoadedClass2(String name) { - return super.findLoadedClass(name); - } - - @Override - public Object getClassLoadingLock(String className) { - return super.getClassLoadingLock(className); - } -} diff --git a/core/src/test/java/hudson/PluginWrapperTest.java b/core/src/test/java/hudson/PluginWrapperTest.java index 640b5eb2b4..ba63355b7d 100644 --- a/core/src/test/java/hudson/PluginWrapperTest.java +++ b/core/src/test/java/hudson/PluginWrapperTest.java @@ -24,7 +24,6 @@ import java.util.Locale; import java.util.jar.Attributes; import java.util.jar.Manifest; import jenkins.model.Jenkins; -import jenkins.util.AntClassLoader; import jenkins.util.URLClassLoader2; import org.apache.commons.lang.StringUtils; import org.junit.jupiter.api.AfterAll; @@ -106,14 +105,6 @@ public class PluginWrapperTest { @Issue("JENKINS-66563") @Test public void insertJarsIntoClassPath() throws Exception { - try (AntClassLoader cl = new AntClassLoader()) { - assertInjectingJarsWorks(cl); - } - } - - @Issue("JENKINS-66563") - @Test - public void insertJarsIntoClassPathURLCL() throws Exception { try (URLClassLoader2 cl = new URLClassLoader2(new URL[0])) { assertInjectingJarsWorks(cl); } diff --git a/test/src/test/java/jenkins/util/AntClassLoaderTest.java b/test/src/test/java/jenkins/util/AntClassLoaderTest.java deleted file mode 100644 index c8c882ed81..0000000000 --- a/test/src/test/java/jenkins/util/AntClassLoaderTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * The MIT License - * - * Copyright 2020 CloudBees, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package jenkins.util; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.recipes.WithPlugin; - -public class AntClassLoaderTest { - - @Rule - public JenkinsRule r = new JenkinsRule(); - - @Issue("JENKINS-60644") - @WithPlugin("loads-resource.jpi") - @Test - public void loadsResource() throws Exception { - assertNotNull(r.jenkins.pluginManager.getPlugin("loads-resource").classLoader.getResourceAsStream("io/jenkins/plugins/loads_resource/stuff")); - } - -} diff --git a/test/src/test/resources/plugins/loads-resource.jpi b/test/src/test/resources/plugins/loads-resource.jpi deleted file mode 100644 index 45b9a05e984eb8566c7824ddaa927eb943e4fdc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12064 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 D94#ny