From c78262a8a0d59e4afaa892403a6f133bce119b66 Mon Sep 17 00:00:00 2001 From: LHHDZ Date: Fri, 18 Oct 2024 13:43:42 +0800 Subject: [PATCH] fix classloader --- .../util/AdviceClassesCollector.java | 9 +++++++- .../arex/agent/bootstrap/util/JarUtils.java | 23 ++++++++++++++++--- .../main/java/io/arex/agent/ArexAgent.java | 7 ++++++ .../inst/runtime/listener/EventProcessor.java | 3 +-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/AdviceClassesCollector.java b/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/AdviceClassesCollector.java index e0d8d57dd..d3d4f4f9b 100644 --- a/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/AdviceClassesCollector.java +++ b/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/AdviceClassesCollector.java @@ -121,6 +121,9 @@ private byte[] getBytes(String name, ClassLoader loader) throws IOException { return locator.locate(name).resolve(); } + public void appendToSystemClassLoaderSearch(String jarPackageName) { + appendToClassLoaderSearch(jarPackageName, null); + } public void appendToClassLoaderSearch(String jarPackageName, ClassLoader loader) { List filePathList = THIRD_PARTY_NESTED_JARS_PATH_MAP.get(jarPackageName); if (CollectionUtil.isEmpty(filePathList)) { @@ -130,7 +133,11 @@ public void appendToClassLoaderSearch(String jarPackageName, ClassLoader loader) for (String filePath : filePathList) { JarEntry jarEntry = agentJarFile.getJarEntry(filePath); File extractNestedJar = JarUtils.extractNestedJar(agentJarFile, jarEntry, filePath); - JarUtils.appendToClassLoaderSearch(loader, extractNestedJar); + if (loader == null) { + JarUtils.appendToSystemClassLoaderSearch(extractNestedJar); + }else{ + JarUtils.appendToClassLoaderSearch(loader, extractNestedJar); + } } } catch (Exception ex) { System.err.printf("appendToClassLoaderSearch failed, jarPackageName: %s%n", jarPackageName); diff --git a/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/JarUtils.java b/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/JarUtils.java index ce5a7d0f7..aeb4ca31f 100644 --- a/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/JarUtils.java +++ b/arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/JarUtils.java @@ -12,6 +12,7 @@ import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.function.Consumer; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -19,6 +20,8 @@ public class JarUtils { private static Method addURL; private static final File TMP_FILE = new File(AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty("java.io.tmpdir"))); private static final String AREX_TEMP_DIR = TMP_FILE.getAbsolutePath() + File.separator + "arex"; + public static Consumer addJarToSystemClassLoader = file -> { + }; static { try { @@ -28,10 +31,11 @@ public class JarUtils { System.err.println("Failed to get addURL method from URLClassLoader"); } } + public static File extractNestedJar(JarFile file, JarEntry entry, String entryName) throws IOException { File outputFile = createFile(AREX_TEMP_DIR + File.separator + entryName); - try(InputStream inputStream = file.getInputStream(entry); - FileOutputStream outputStream = new FileOutputStream(outputFile)) { + try (InputStream inputStream = file.getInputStream(entry); + FileOutputStream outputStream = new FileOutputStream(outputFile)) { byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { @@ -53,6 +57,19 @@ private static File createFile(String path) throws IOException { return file; } + /** + * Java Instrumentation.appendToSystemClassLoaderSearch() method is compatible across JDK versions, including JDK 8 through JDK 17. Here’s a detailed breakdown of its compatibility: + * 1. JDK 8 Compatibility + * • The appendToSystemClassLoaderSearch() method was introduced as part of the Java Instrumentation API in JDK 6. This means it is fully available and compatible with JDK 8. + * • It allows you to append JARs to the system class loader’s search path, ensuring that classes within those JARs can be loaded by the system class loader. + * 2. JDK 9+ (including JDK 17) Compatibility + * • With JDK 9, the module system (Project Jigsaw) was introduced, which brought significant changes to class loading and the organization of Java’s internal APIs. + * • Despite these changes, appendToSystemClassLoaderSearch() remains compatible and continues to work as expected in JDK 9 and later, including JDK 17. It can still be used to add JARs to the classpath of the system class loader. + */ + public static void appendToSystemClassLoaderSearch(File jarFile) { + addJarToSystemClassLoader.accept(jarFile); + } + /** * tomcat jdk <= 8, classLoader is ParallelWebappClassLoader, ClassLoader.getSystemClassLoader() is Launcher$AppClassLoader * jdk > 8, classLoader is ParallelWebappClassLoader, ClassLoader.getSystemClassLoader() is ClassLoaders$AppClassLoader @@ -69,7 +86,7 @@ public static void appendToClassLoaderSearch(ClassLoader classLoader, File jarFi */ ClassLoader urlClassLoader = ClassLoader.getSystemClassLoader(); if (!(urlClassLoader instanceof URLClassLoader)) { - try (URLClassLoader tempClassLoader = new URLClassLoader(new URL[] {jarFile.toURI().toURL()}, urlClassLoader)) { + try (URLClassLoader tempClassLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}, urlClassLoader)) { addURL.invoke(tempClassLoader, jarFile.toURI().toURL()); } } else { diff --git a/arex-agent/src/main/java/io/arex/agent/ArexAgent.java b/arex-agent/src/main/java/io/arex/agent/ArexAgent.java index 016cf8712..63a881e5b 100644 --- a/arex-agent/src/main/java/io/arex/agent/ArexAgent.java +++ b/arex-agent/src/main/java/io/arex/agent/ArexAgent.java @@ -40,6 +40,13 @@ private static void init(Instrumentation inst, String agentArgs) { */ installBootstrapJar(inst); AgentInitializer.initialize(inst, getJarFile(ArexAgent.class), agentArgs, ArexAgent.class.getClassLoader()); + io.arex.agent.bootstrap.util.JarUtils.addJarToSystemClassLoader = file -> { + try { + inst.appendToSystemClassLoaderSearch(new JarFile(file, false)); + } catch (Exception e) { + System.out.printf("%s [AREX] Agent add jar to system classloader error, stacktrace: %s%n", getCurrentTime(), e); + } + }; } catch (Exception ex) { System.out.printf("%s [AREX] Agent initialize error, stacktrace: %s%n", getCurrentTime(), ex); } diff --git a/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/listener/EventProcessor.java b/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/listener/EventProcessor.java index 09bd807a6..88d09007b 100644 --- a/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/listener/EventProcessor.java +++ b/arex-instrumentation-api/src/main/java/io/arex/inst/runtime/listener/EventProcessor.java @@ -74,8 +74,7 @@ private static void addEnterLog() { */ private static void initSerializer(ClassLoader contextClassLoader) { if (!existJacksonDependency) { - AdviceClassesCollector.INSTANCE.appendToClassLoaderSearch("jackson", - contextClassLoader); + AdviceClassesCollector.INSTANCE.appendToSystemClassLoaderSearch("jackson"); } final List serializableList = ServiceLoader.load(StringSerializable.class, contextClassLoader); Serializer.builder(serializableList).build();