-
Notifications
You must be signed in to change notification settings - Fork 134
Expand file tree
/
Copy pathJarUtils.java
More file actions
114 lines (104 loc) · 5.54 KB
/
JarUtils.java
File metadata and controls
114 lines (104 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package io.arex.agent.bootstrap.util;
import io.arex.agent.bootstrap.constants.ConfigConstants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
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;
public class JarUtils {
private static Method addURL;
private static final File TMP_FILE = new File(AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty("java.io.tmpdir")));
private static final String AREX_TEMP_DIR = TMP_FILE.getAbsolutePath() + File.separator + "arex";
public static Consumer<File> addJarToSystemClassLoader = file -> {
};
static {
try {
addURL = Class.forName("java.net.URLClassLoader").getDeclaredMethod("addURL", URL.class);
addURL.setAccessible(true);
} catch (Exception e) {
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)) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
}
return outputFile;
}
private static File createFile(String path) throws IOException {
File file = new File(path);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
boolean newFile = file.createNewFile();
if (newFile) {
System.out.printf("create file: %s%n", file.getAbsolutePath());
}
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
*/
public static void appendToClassLoaderSearch(ClassLoader classLoader, File jarFile) {
try {
if (classLoader instanceof URLClassLoader) {
addURL.invoke(classLoader, jarFile.toURI().toURL());
}
/*
* Due to Java 8 vs java 9+ incompatibility issues
* See https://stackoverflow.com/questions/46694600/java-9-compatability-issue-with-classloader-getsystemclassloader/51584718
*/
ClassLoader urlClassLoader = ClassLoader.getSystemClassLoader();
if (!(urlClassLoader instanceof URLClassLoader)) {
try (URLClassLoader tempClassLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}, urlClassLoader)) {
addURL.invoke(tempClassLoader, jarFile.toURI().toURL());
}
} else {
addURL.invoke(urlClassLoader, jarFile.toURI().toURL());
}
appendToAppClassLoaderSearch(classLoader, jarFile);
} catch (Exception e) {
System.err.printf("appendToClassLoaderSearch failed, classLoader: %s, jarFile: %s%n",
classLoader.getClass().getName(), jarFile.getAbsolutePath());
}
}
/**
* append jar jdk.internal.loader.ClassLoaders.AppClassLoader
* if java version >= 11 need add jvm option:--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
*/
private static void appendToAppClassLoaderSearch(ClassLoader classLoader, File jarFile) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<? extends ClassLoader> loaderClass = classLoader.getClass();
if (JdkUtils.isJdk11OrHigher() && ConfigConstants.APP_CLASSLOADER_NAME.equalsIgnoreCase(loaderClass.getName())) {
Method classPathMethod = loaderClass.getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
classPathMethod.setAccessible(true);
classPathMethod.invoke(classLoader, jarFile.getPath());
}
}
}