From fc95742ea31f48596d98cf73550c8d835444ec59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20W=C3=B6gerer?= Date: Wed, 1 Jun 2022 19:10:00 +0200 Subject: [PATCH] Make NativeImageSystemClassLoader able to handle having module classloader with a parent ClassPathClassLoader --- .../hosted/NativeImageClassLoaderSupport.java | 8 +- .../hosted/NativeImageSystemClassLoader.java | 103 +++++++++++++----- 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java index c4c93e558529..5ca72f7c1319 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java @@ -102,13 +102,19 @@ public class NativeImageClassLoaderSupport { public final ModuleLayer moduleLayerForImageBuild; public final ModuleFinder modulepathModuleFinder; + static final class ClassPathClassLoader extends URLClassLoader { + ClassPathClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + } + protected NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, String[] classpath, String[] modulePath) { classes = EconomicMap.create(); packages = EconomicMap.create(); emptySet = EconomicSet.create(); - classPathClassLoader = new URLClassLoader(Util.verifyClassPathAndConvertToURLs(classpath), defaultSystemClassLoader); + classPathClassLoader = new ClassPathClassLoader(Util.verifyClassPathAndConvertToURLs(classpath), defaultSystemClassLoader); imagecp = Arrays.stream(classPathClassLoader.getURLs()) .map(Util::urlToPath) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java index d336adb0eb58..cf8cf8922caf 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageSystemClassLoader.java @@ -25,11 +25,13 @@ package com.oracle.svm.hosted; import java.io.IOException; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.security.SecureClassLoader; import java.util.Collections; import java.util.Enumeration; +import java.util.List; import java.util.Set; import java.util.WeakHashMap; import java.util.jar.JarFile; @@ -135,34 +137,56 @@ public boolean isDisallowedClassLoader(ClassLoader c) { private static final Method defineClass = ReflectionUtil.lookupMethod(ClassLoader.class, "defineClass", String.class, byte[].class, int.class, int.class); - static Class loadClass(ClassLoader classLoader, String name, boolean resolve) throws ClassNotFoundException { - Class loadedClass = null; + private static final Constructor> compoundEnumerationConstructor; + static { + /* Reuse utility class defined as package-private class in java.lang.ClassLoader.java */ + String className = "java.lang.CompoundEnumeration"; try { - /* invoke the "loadClass" method on the current class loader */ - loadedClass = ((Class) loadClass.invoke(classLoader, name, resolve)); - } catch (Exception e) { - if (e.getCause() instanceof ClassNotFoundException) { - throw ((ClassNotFoundException) e.getCause()); + @SuppressWarnings("unchecked") + Class> compoundEnumerationClass = (Class>) Class.forName(className); + compoundEnumerationConstructor = ReflectionUtil.lookupConstructor(compoundEnumerationClass, Enumeration[].class); + } catch (ClassNotFoundException | ReflectionUtil.ReflectionUtilError e) { + throw VMError.shouldNotReachHere("Unable to get access to class " + className, e); + } + } + + private static Class loadClass(List activeClassLoaders, String name, boolean resolve) throws ClassNotFoundException { + ClassNotFoundException classNotFoundException = null; + for (ClassLoader loader : activeClassLoaders) { + try { + /* invoke the "loadClass" method on the current class loader */ + return ((Class) loadClass.invoke(loader, name, resolve)); + } catch (Exception e) { + if (e.getCause() instanceof ClassNotFoundException) { + classNotFoundException = ((ClassNotFoundException) e.getCause()); + } else { + String message = String.format("Can not load class: %s, with class loader: %s", name, loader); + VMError.shouldNotReachHere(message, e); + } } - String message = String.format("Can not load class: %s, with class loader: %s", name, classLoader); - VMError.shouldNotReachHere(message, e); } - return loadedClass; + VMError.guarantee(classNotFoundException != null); + throw classNotFoundException; } - static URL findResource(ClassLoader classLoader, String name) { - try { - // invoke the "findResource" method on the current class loader - return (URL) findResource.invoke(classLoader, name); - } catch (ReflectiveOperationException e) { - String message = String.format("Can not find resource: %s using class loader: %s", name, classLoader); - VMError.shouldNotReachHere(message, e); + private static URL findResource(List activeClassLoaders, String name) { + for (ClassLoader loader : activeClassLoaders) { + try { + // invoke the "findResource" method on the current class loader + Object url = findResource.invoke(loader, name); + if (url != null) { + return (URL) url; + } + } catch (ReflectiveOperationException | ClassCastException e) { + String message = String.format("Can not find resource: %s using class loader: %s", name, loader); + VMError.shouldNotReachHere(message, e); + } } return null; } @SuppressWarnings("unchecked") - static Enumeration findResources(ClassLoader classLoader, String name) { + private static Enumeration findResources(ClassLoader classLoader, String name) { try { // invoke the "findResources" method on the current class loader return (Enumeration) findResources.invoke(classLoader, name); @@ -186,22 +210,38 @@ static Class defineClass(ClassLoader classLoader, String name, byte[] b, int @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - return loadClass(getActiveClassLoader(), name, resolve); + return loadClass(getActiveClassLoaders(), name, resolve); } @Override protected URL findResource(String name) { - return findResource(getActiveClassLoader(), name); + return findResource(getActiveClassLoaders(), name); } @Override protected Enumeration findResources(String name) throws IOException { - return findResources(getActiveClassLoader(), name); + List activeClassLoaders = getActiveClassLoaders(); + assert !activeClassLoaders.isEmpty() & activeClassLoaders.size() <= 2; + ClassLoader activeClassLoader = activeClassLoaders.get(0); + ClassLoader activeClassLoaderParent = activeClassLoaders.size() > 1 ? activeClassLoaders.get(1) : null; + if (activeClassLoaderParent != null) { + return newCompoundEnumeration(findResources(activeClassLoaderParent, name), findResources(activeClassLoader, name)); + } + return findResources(activeClassLoader, name); + } + + @SuppressWarnings("unchecked") + private static Enumeration newCompoundEnumeration(Enumeration... enums) { + try { + return (Enumeration) compoundEnumerationConstructor.newInstance((Object) enums); + } catch (ReflectiveOperationException e) { + throw VMError.shouldNotReachHere("Cannot instantiate CompoundEnumeration", e); + } } public Class forNameOrNull(String name, boolean initialize) { try { - return Class.forName(name, initialize, getActiveClassLoader()); + return Class.forName(name, initialize, getActiveClassLoaders().get(0)); } catch (LinkageError | ClassNotFoundException ignored) { return null; } @@ -212,7 +252,7 @@ public Class predefineClass(String name, byte[] array, int offset, int length if (forNameOrNull(name, false) != null) { throw VMError.shouldNotReachHere("The class loader hierarchy already provides a class with the same name as the class submitted for predefinition: " + name); } - return defineClass(getActiveClassLoader(), name, array, offset, length); + return defineClass(getActiveClassLoaders().get(0), name, array, offset, length); } @Override @@ -224,11 +264,18 @@ public String toString() { '}'; } - private ClassLoader getActiveClassLoader() { - ClassLoader delegate = nativeImageClassLoader; - return delegate != null - ? delegate - : defaultSystemClassLoader; + private List getActiveClassLoaders() { + ClassLoader activeClassLoader = nativeImageClassLoader; + if (activeClassLoader != null) { + ClassLoader activeClassLoaderParent = activeClassLoader.getParent(); + if (activeClassLoaderParent instanceof NativeImageClassLoaderSupport.ClassPathClassLoader) { + return List.of(activeClassLoader, activeClassLoaderParent); + } else { + return List.of(activeClassLoader); + } + } else { + return List.of(defaultSystemClassLoader); + } } /**