diff --git a/core/src/main/java/hudson/ExtensionFinder.java b/core/src/main/java/hudson/ExtensionFinder.java
index 92e524e04d..62234e0692 100644
--- a/core/src/main/java/hudson/ExtensionFinder.java
+++ b/core/src/main/java/hudson/ExtensionFinder.java
@@ -486,12 +486,6 @@ public abstract class ExtensionFinder implements ExtensionPoint {
return;
}
try {
- ClassLoader ecl = c.getClassLoader();
- if (ecl != null) { // Not bootstrap classloader
- Method m = ClassLoader.class.getDeclaredMethod("resolveClass", Class.class);
- m.setAccessible(true);
- m.invoke(ecl, c);
- }
for (Class> cc = c; cc != Object.class && cc != null; cc = cc.getSuperclass()) {
/*
* See {@link com.google.inject.spi.InjectionPoint#getInjectionPoints(TypeLiteral, boolean, Errors)}
diff --git a/core/src/main/java/hudson/PluginFirstClassLoader.java b/core/src/main/java/hudson/PluginFirstClassLoader.java
index dde635d24d..51ef789da0 100644
--- a/core/src/main/java/hudson/PluginFirstClassLoader.java
+++ b/core/src/main/java/hudson/PluginFirstClassLoader.java
@@ -84,7 +84,7 @@ public class PluginFirstClassLoader
}
@Override
- protected Enumeration findResources( String name )
+ public Enumeration findResources( String name )
throws IOException
{
return super.findResources( name );
diff --git a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java
index 052fc7bd8f..06a61b879b 100644
--- a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java
+++ b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java
@@ -4,41 +4,29 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
+import java.net.URLClassLoader;
import java.util.Enumeration;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
+import jenkins.util.AntClassLoader;
+import jenkins.util.AntWithFindResourceClassLoader;
/**
* Reflective access to various {@link ClassLoader} methods which are otherwise {@code protected}.
+ *
+ * Initially tries to access methods using known classloaders in use that expose the methods
+ * to prevent illegal reflective access errors on Java 11+
+ * Then falls back to accessing the {@link ClassLoader} methods.
+ *
+ * All reflection method initialisation is delayed until first use so that we don't access the methods if we don't need to.
+ *
+ * Note: Currently there is no known production use-case for the fallback case of accessing these methods via reflection:
+ * the {@code JenkinsRule} tests use a different classloader,
+ * but once that is made consistent with production Jenkins we can re-evaluate the fallback code.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class ClassLoaderReflectionToolkit {
- private static final Method FIND_CLASS, FIND_LOADED_CLASS, FIND_RESOURCE, FIND_RESOURCES, GET_CLASS_LOADING_LOCK;
-
- static {
- try {
- FIND_CLASS = ClassLoader.class.getDeclaredMethod("findClass",String.class);
- FIND_CLASS.setAccessible(true);
- FIND_LOADED_CLASS = ClassLoader.class.getDeclaredMethod("findLoadedClass",String.class);
- FIND_LOADED_CLASS.setAccessible(true);
- FIND_RESOURCE = ClassLoader.class.getDeclaredMethod("findResource",String.class);
- FIND_RESOURCE.setAccessible(true);
- FIND_RESOURCES = ClassLoader.class.getDeclaredMethod("findResources",String.class);
- FIND_RESOURCES.setAccessible(true);
- } catch (NoSuchMethodException e) {
- throw new AssertionError(e);
- }
- Method gCLL;
- try {
- gCLL = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
- gCLL.setAccessible(true);
- } catch (NoSuchMethodException x) {
- throw new AssertionError(x);
- }
- GET_CLASS_LOADING_LOCK = gCLL;
- }
-
private static Object invoke(Method method, Class exception, Object obj, Object... args) throws T {
try {
return method.invoke(obj, args);
@@ -59,7 +47,25 @@ public class ClassLoaderReflectionToolkit {
}
private static Object getClassLoadingLock(ClassLoader cl, String name) {
- return invoke(GET_CLASS_LOADING_LOCK, RuntimeException.class, cl, name);
+ if (cl instanceof AntWithFindResourceClassLoader) {
+ return ((AntWithFindResourceClassLoader) cl).getClassLoadingLock(name);
+ }
+ return invoke(GetClassLoadingLock.GET_CLASS_LOADING_LOCK, RuntimeException.class, cl, name);
+ }
+
+ private static class GetClassLoadingLock {
+ private static final Method GET_CLASS_LOADING_LOCK;
+
+ static {
+ Method gCLL;
+ try {
+ gCLL = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
+ gCLL.setAccessible(true);
+ } catch (NoSuchMethodException x) {
+ throw new AssertionError(x);
+ }
+ GET_CLASS_LOADING_LOCK = gCLL;
+ }
}
/**
@@ -68,7 +74,27 @@ public class ClassLoaderReflectionToolkit {
*/
public static @CheckForNull Class> _findLoadedClass(ClassLoader cl, String name) {
synchronized (getClassLoadingLock(cl, name)) {
- return (Class) invoke(FIND_LOADED_CLASS, RuntimeException.class, cl, name);
+ Class> c;
+ if (cl instanceof AntWithFindResourceClassLoader) {
+ c = ((AntWithFindResourceClassLoader) cl).findLoadedClass2(name);
+ } else {
+ c = (Class) invoke(FindLoadedClass.FIND_LOADED_CLASS, RuntimeException.class, cl, name);
+ }
+
+ return c;
+ }
+ }
+
+ private static class FindLoadedClass {
+ private static final Method FIND_LOADED_CLASS;
+
+ static {
+ try {
+ FIND_LOADED_CLASS = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ FIND_LOADED_CLASS.setAccessible(true);
}
}
@@ -77,17 +103,57 @@ public class ClassLoaderReflectionToolkit {
* @since 1.553
*/
public static @NonNull Class> _findClass(ClassLoader cl, String name) throws ClassNotFoundException {
+ if (cl instanceof AntClassLoader) {
+ return ((AntClassLoader) cl).findClass(name);
+ }
+
synchronized (getClassLoadingLock(cl, name)) {
- return (Class) invoke(FIND_CLASS, ClassNotFoundException.class, cl, name);
+ return (Class) invoke(FindClass.FIND_CLASS, ClassNotFoundException.class, cl, name);
}
}
+ private static class FindClass {
+ private static final Method FIND_CLASS;
+
+ static {
+ try {
+ FIND_CLASS = ClassLoader.class.getDeclaredMethod("findClass",String.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ FIND_CLASS.setAccessible(true);
+ }
+ }
+
+
/**
* Calls {@link ClassLoader#findResource}.
* @since 1.553
*/
public static @CheckForNull URL _findResource(ClassLoader cl, String name) {
- return (URL) invoke(FIND_RESOURCE, RuntimeException.class, cl, name);
+ URL url;
+ if (cl instanceof AntWithFindResourceClassLoader) {
+ url = ((AntWithFindResourceClassLoader) cl).findResource(name);
+ } else if (cl instanceof URLClassLoader) {
+ url = ((URLClassLoader) cl).findResource(name);
+ } else {
+ url = (URL) invoke(FindResource.FIND_RESOURCE, RuntimeException.class, cl, name);
+ }
+
+ return url;
+ }
+
+ private static class FindResource {
+ private static final Method FIND_RESOURCE;
+
+ static {
+ try {
+ FIND_RESOURCE = ClassLoader.class.getDeclaredMethod("findResource", String.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ FIND_RESOURCE.setAccessible(true);
+ }
}
/**
@@ -95,7 +161,27 @@ public class ClassLoaderReflectionToolkit {
* @since 1.553
*/
public static @NonNull Enumeration _findResources(ClassLoader cl, String name) throws IOException {
- return (Enumeration) invoke(FIND_RESOURCES, IOException.class, cl, name);
+ Enumeration urls;
+ if (cl instanceof AntWithFindResourceClassLoader) {
+ urls = ((AntWithFindResourceClassLoader) cl).findResources(name);
+ } else {
+ urls = (Enumeration) invoke(FindResources.FIND_RESOURCES, IOException.class, cl, name);
+ }
+
+ return urls;
+ }
+
+ private static class FindResources {
+ private static final Method FIND_RESOURCES;
+
+ static {
+ try {
+ FIND_RESOURCES = ClassLoader.class.getDeclaredMethod("findResources", String.class);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ FIND_RESOURCES.setAccessible(true);
+ }
}
/** @deprecated unsafe */
@@ -105,7 +191,7 @@ public class ClassLoaderReflectionToolkit {
@Deprecated
public Class findLoadedClass(ClassLoader cl, String name) throws InvocationTargetException {
try {
- return (Class)FIND_LOADED_CLASS.invoke(cl,name);
+ return (Class)FindLoadedClass.FIND_LOADED_CLASS.invoke(cl,name);
} catch (IllegalAccessException e) {
throw new Error(e);
}
@@ -115,7 +201,7 @@ public class ClassLoaderReflectionToolkit {
@Deprecated
public Class findClass(ClassLoader cl, String name) throws InvocationTargetException {
try {
- return (Class)FIND_CLASS.invoke(cl,name);
+ return (Class)FindClass.FIND_CLASS.invoke(cl,name);
} catch (IllegalAccessException e) {
throw new Error(e);
}
@@ -125,7 +211,7 @@ public class ClassLoaderReflectionToolkit {
@Deprecated
public URL findResource(ClassLoader cl, String name) throws InvocationTargetException {
try {
- return (URL)FIND_RESOURCE.invoke(cl,name);
+ return (URL)FindResource.FIND_RESOURCE.invoke(cl,name);
} catch (IllegalAccessException e) {
throw new Error(e);
}
@@ -135,7 +221,7 @@ public class ClassLoaderReflectionToolkit {
@Deprecated
public Enumeration findResources(ClassLoader cl, String name) throws InvocationTargetException {
try {
- return (Enumeration)FIND_RESOURCES.invoke(cl,name);
+ return (Enumeration)FindResources.FIND_RESOURCES.invoke(cl,name);
} catch (IllegalAccessException e) {
throw new Error(e);
}
diff --git a/core/src/main/java/jenkins/model/AssetManager.java b/core/src/main/java/jenkins/model/AssetManager.java
index a688f0f718..5d317f2b2a 100644
--- a/core/src/main/java/jenkins/model/AssetManager.java
+++ b/core/src/main/java/jenkins/model/AssetManager.java
@@ -2,8 +2,10 @@ package jenkins.model;
import hudson.Extension;
import hudson.model.UnprotectedRootAction;
+
import java.util.concurrent.TimeUnit;
+import jenkins.ClassLoaderReflectionToolkit;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.StaplerRequest;
@@ -14,8 +16,6 @@ import edu.umd.cs.findbugs.annotations.NonNull;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
@@ -83,41 +83,26 @@ public class AssetManager implements UnprotectedRootAction {
return null;
}
- try {
- if (path.contains("..")) // crude avoidance of directory traversal attack
- throw new IllegalArgumentException(path);
+ if (path.contains("..")) // crude avoidance of directory traversal attack
+ throw new IllegalArgumentException(path);
- String name;
- if (path.charAt(0) == '/') {
- name = "assets" + path;
- } else {
- name = "assets/" + path;
- }
-
- ClassLoader cl = Jenkins.class.getClassLoader();
- URL url = (URL) $findResource.invoke(cl, name);
- if (url==null) {
- // pick the last one, which is the one closest to the leaf of the classloader tree.
- Enumeration e = cl.getResources(name);
- while (e.hasMoreElements()) {
- url = e.nextElement();
- }
- }
- return url;
- } catch (InvocationTargetException|IllegalAccessException e) {
- throw new Error(e);
+ String name;
+ if (path.charAt(0) == '/') {
+ name = "assets" + path;
+ } else {
+ name = "assets/" + path;
}
+
+ ClassLoader cl = Jenkins.class.getClassLoader();
+ URL url = ClassLoaderReflectionToolkit._findResource(cl, name);
+ if (url == null) {
+ // pick the last one, which is the one closest to the leaf of the classloader tree.
+ Enumeration e = cl.getResources(name);
+ while (e.hasMoreElements()) {
+ url = e.nextElement();
+ }
+ }
+ return url;
}
- private static final Method $findResource = init();
-
- private static Method init() {
- try {
- Method m = ClassLoader.class.getDeclaredMethod("findResource", String.class);
- m.setAccessible(true);
- return m;
- } catch (NoSuchMethodException e) {
- throw (Error)new NoSuchMethodError().initCause(e);
- }
- }
}
diff --git a/core/src/main/java/jenkins/util/AntClassLoader.java b/core/src/main/java/jenkins/util/AntClassLoader.java
index 7843abb69a..656678a7e8 100644
--- a/core/src/main/java/jenkins/util/AntClassLoader.java
+++ b/core/src/main/java/jenkins/util/AntClassLoader.java
@@ -960,7 +960,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
* @return an enumeration of URLs for the resources
* @exception IOException if I/O errors occurs (can't happen)
*/
- protected Enumeration/**/ findResources(String name) throws IOException {
+ public Enumeration/**/ findResources(String name) throws IOException {
return findResources(name, true);
}
diff --git a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java
index 7b70413a48..6f45892bfa 100644
--- a/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java
+++ b/core/src/main/java/jenkins/util/AntWithFindResourceClassLoader.java
@@ -37,10 +37,25 @@ public class AntWithFindResourceClassLoader extends AntClassLoader implements Cl
}
@Override
- protected URL findResource(String name) {
+ public URL findResource(String name) {
// try and load from this loader if the parent either didn't find
// it or wasn't consulted.
return getUrl(pathComponents, name);
}
+ /**
+ * Public version of {@link ClassLoader#findLoadedClass(String)}
+ */
+ public Class> findLoadedClass2(String name) {
+ return findLoadedClass(name);
+ }
+
+ /**
+ * Public version of {@link ClassLoader#getClassLoadingLock(String)}
+ */
+ @Override
+ public Object getClassLoadingLock(String className) {
+ return super.getClassLoadingLock(className);
+ }
+
}