Skip to content

Commit ad71b9b

Browse files
committed
feat: package all in one agent jar
1 parent 9220c18 commit ad71b9b

File tree

29 files changed

+492
-190
lines changed

29 files changed

+492
-190
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,9 @@ or build the artifacts with the following commands. The build process supports J
7676
`mvn clean install -DskipTests`
7777

7878
The agent jar is in the folder `arex-agent-jar/` after the build process.
79-
There will be two jar files in the folder.
8079

8180
```other
8281
arex-agent.jar
83-
arex-agent-bootstrap.jar
8482
```
8583

8684
If you wanna jar with version, build the artifacts with the following commands.

arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/AgentInitializer.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ public class AgentInitializer {
1010

1111
private static ClassLoader classLoader;
1212

13-
public static void initialize(Instrumentation inst, File agentFile, String agentArgs)
13+
/**
14+
* @param parentClassLoader Normally, the parentClassLoader should be ClassLoaders.AppClassLoader.
15+
*/
16+
public static void initialize(Instrumentation inst, File agentFile, String agentArgs, ClassLoader parentClassLoader)
1417
throws Exception {
1518
if (classLoader != null) {
1619
return;
@@ -20,7 +23,7 @@ public static void initialize(Instrumentation inst, File agentFile, String agent
2023
System.setProperty(ConfigConstants.SHADED_LOGGER_SHOW_DATE_TIME, "true");
2124
System.setProperty(ConfigConstants.SHADED_LOGGER_DATE_TIME_FORMAT, "yyyy-MM-dd HH:mm:ss:SSS");
2225
File[] extensionFiles = getExtensionJarFiles(agentFile);
23-
classLoader = createAgentClassLoader(agentFile, extensionFiles);
26+
classLoader = new AgentClassLoader(agentFile, parentClassLoader, extensionFiles);
2427
InstrumentationHolder.setAgentClassLoader(classLoader);
2528
InstrumentationHolder.setInstrumentation(inst);
2629
AgentInstaller installer = createAgentInstaller(inst, agentFile, agentArgs);
@@ -40,10 +43,6 @@ private static void addJarToLoaderSearch(File agentFile, File[] extensionFiles)
4043
}
4144
}
4245

43-
private static AgentClassLoader createAgentClassLoader(File agentFile, File[] extensionFiles) {
44-
return new AgentClassLoader(agentFile, getParentClassLoader(), extensionFiles);
45-
}
46-
4746
private static File[] getExtensionJarFiles(File jarFile) {
4847
String extensionDir = jarFile.getParent() + "/extensions/";
4948
return new File(extensionDir).listFiles(AgentInitializer::isJar);
@@ -58,8 +57,4 @@ private static AgentInstaller createAgentInstaller(Instrumentation inst, File fi
5857
Constructor<?> constructor = clazz.getDeclaredConstructor(Instrumentation.class, File.class, String.class);
5958
return (AgentInstaller) constructor.newInstance(inst, file, agentArgs);
6059
}
61-
62-
private static ClassLoader getParentClassLoader() {
63-
return AgentInitializer.class.getClassLoader();
64-
}
6560
}

arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/constants/ConfigConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ private ConfigConstants() {
3333
public static final String SHADED_LOGGER_SHOW_DATE_TIME = "shaded.org.slf4j.simpleLogger.showDateTime";
3434
public static final String SHADED_LOGGER_DATE_TIME_FORMAT = "shaded.org.slf4j.simpleLogger.dateTimeFormat";
3535
public static final String COVERAGE_PACKAGES = "arex.coverage.packages";
36+
public static final String APP_CLASSLOADER_NAME = "jdk.internal.loader.ClassLoaders$AppClassLoader";
3637
}

arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/AdviceClassesCollector.java

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55

66
import java.io.File;
77
import java.io.IOException;
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Method;
10+
import java.net.URL;
11+
import java.net.URLClassLoader;
812
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.nio.file.Paths;
915
import java.util.jar.JarEntry;
16+
import java.util.jar.JarFile;
1017
import java.util.jar.JarInputStream;
18+
import java.util.stream.Stream;
19+
20+
import io.arex.agent.bootstrap.constants.ConfigConstants;
1121
import net.bytebuddy.dynamic.ClassFileLocator;
1222

1323
public class AdviceClassesCollector {
@@ -18,6 +28,7 @@ public class AdviceClassesCollector {
1828
private static final String CLASS_SERIALIZER_PREFIX = "io/arex/foundation/serializer";
1929
private static final String CLASS_SUFFIX = ".class";
2030
private static final int CLASS_SUFFIX_LENGTH = CLASS_SUFFIX.length();
31+
private static final String AREX_THIRD_PARTY_TEMP_DIR = System.getProperty("java.io.tmpdir") + File.separator + "arex" + File.separator + "third-party";
2132

2233
private AdviceClassesCollector() {
2334
}
@@ -34,13 +45,14 @@ public void addJarToLoaderSearch(File file) {
3445
private void addJarToLoaderSearch(File file, boolean isExtensionJar) {
3546
try (JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(file.toPath()))) {
3647
JarEntry jarEntry;
48+
JarFile jarFile = new JarFile(file);
3749
do {
3850
jarEntry = jarInputStream.getNextJarEntry();
3951

4052
if (jarEntry != null && !jarEntry.isDirectory()) {
4153
String entryName = jarEntry.getName();
4254
if (ServiceLoader.match(entryName)) {
43-
ServiceLoader.buildCache(file, jarEntry, entryName);
55+
ServiceLoader.buildCache(jarFile, jarEntry, entryName);
4456
}
4557
// exclude package io.arex.inst.runtime/extension, not class, and shaded class.
4658
boolean isFilterEntry = StringUtil.isEmpty(entryName) ||
@@ -62,7 +74,7 @@ private void addJarToLoaderSearch(File file, boolean isExtensionJar) {
6274

6375
} while (jarEntry != null);
6476
} catch (Throwable ex) {
65-
System.err.printf("add jar classes to advice failed, file: %s%n", file.getAbsolutePath());
77+
System.err.printf("add jar classes to advice failed, file: %s, exception: %s%n", file.getAbsolutePath(), ex);
6678
}
6779
}
6880

@@ -95,6 +107,63 @@ private void addClassToInjectorCache(String adviceClassName) {
95107
}
96108
}
97109

110+
private static void appendToClassLoaderSearch(ClassLoader classLoader, File jarFile) {
111+
try {
112+
Method addURL = Class.forName("java.net.URLClassLoader").getDeclaredMethod("addURL", URL.class);
113+
addURL.setAccessible(true);
114+
115+
if (classLoader instanceof URLClassLoader) {
116+
addURL.invoke(classLoader, jarFile.toURI().toURL());
117+
}
118+
119+
/*
120+
* Due to Java 8 vs java 9+ incompatibility issues
121+
* See https://stackoverflow.com/questions/46694600/java-9-compatability-issue-with-classloader-getsystemclassloader/51584718
122+
*/
123+
ClassLoader urlClassLoader = ClassLoader.getSystemClassLoader();
124+
if (!(urlClassLoader instanceof URLClassLoader)) {
125+
try (URLClassLoader tempClassLoader = new URLClassLoader(new URL[] {jarFile.toURI().toURL()}, urlClassLoader)) {
126+
addURL.invoke(tempClassLoader, jarFile.toURI().toURL());
127+
appendToAppClassLoaderSearch(classLoader, jarFile);
128+
}
129+
} else {
130+
addURL.invoke(urlClassLoader, jarFile.toURI().toURL());
131+
appendToAppClassLoaderSearch(classLoader, jarFile);
132+
}
133+
} catch (Exception e) {
134+
System.err.printf("appendToClassLoaderSearch failed, classLoader: %s, jarFile: %s%n",
135+
classLoader.getClass().getName(), jarFile.getAbsolutePath());
136+
}
137+
}
138+
139+
/**
140+
* append jar jdk.internal.loader.ClassLoaders.AppClassLoader
141+
* if java >= 11 need add jvm option:--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
142+
* @param classLoader
143+
* @param jarFile
144+
*/
145+
private static void appendToAppClassLoaderSearch(ClassLoader classLoader, File jarFile) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
146+
Class<? extends ClassLoader> loaderClass = classLoader.getClass();
147+
if (JdkUtils.isJdk11() && ConfigConstants.APP_CLASSLOADER_NAME.equalsIgnoreCase(loaderClass.getName())) {
148+
Method classPathMethod = loaderClass.getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
149+
classPathMethod.setAccessible(true);
150+
classPathMethod.invoke(classLoader, jarFile.getPath());
151+
}
152+
}
153+
154+
public void addJarInThirdPartyToLoaderSearch(String jarPackageName) {
155+
if (StringUtil.isEmpty(AREX_THIRD_PARTY_TEMP_DIR)) {
156+
return;
157+
}
158+
String searchPath = AREX_THIRD_PARTY_TEMP_DIR + File.separator + jarPackageName;
159+
try (Stream<Path> pathStream = Files.walk(Paths.get(searchPath))) {
160+
pathStream.filter(Files::isRegularFile)
161+
.forEach(path -> appendToClassLoaderSearch(Thread.currentThread().getContextClassLoader(), path.toFile()));
162+
} catch (IOException e) {
163+
System.err.printf("addSpecificJarToLoaderSearch failed, searchPath: %s%n", searchPath);
164+
}
165+
}
166+
98167
private byte[] getBytes(String name, ClassLoader loader) throws IOException {
99168
ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(loader);
100169
return locator.locate(name).resolve();

arex-instrumentation-foundation/src/main/java/io/arex/foundation/util/JdkUtils.java renamed to arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/JdkUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.arex.foundation.util;
1+
package io.arex.agent.bootstrap.util;
22

33
public class JdkUtils {
44
public static final int JDK_11 = 11;

arex-agent-bootstrap/src/main/java/io/arex/agent/bootstrap/util/ServiceLoader.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.arex.agent.bootstrap.util;
22

33
import java.io.BufferedReader;
4-
import java.io.File;
54
import java.io.InputStream;
65
import java.io.InputStreamReader;
76
import java.io.Reader;
@@ -48,9 +47,8 @@ public static <T> List<T> load(Class<T> service, ClassLoader loader) {
4847
* SERVICE_CACHE: key: io.arex.inst.runtime.serializer.StringSerializable
4948
* value: [io.arex.foundation.serializer.gson.GsonSerializer, io.arex.foundation.serializer.jackson.JacksonSerializer]
5049
*/
51-
public static void buildCache(File file, JarEntry jarEntry, String entryName) {
52-
try(JarFile jarFile = new JarFile(file);
53-
InputStream inputStream = jarFile.getInputStream(jarEntry)) {
50+
public static void buildCache(JarFile jarFile, JarEntry jarEntry, String entryName) {
51+
try(InputStream inputStream = jarFile.getInputStream(jarEntry)) {
5452
List<String> serviceList = readAllLines(inputStream);
5553
if (CollectionUtil.isNotEmpty(serviceList)) {
5654
String className = entryName.substring(PREFIX.length());

arex-agent-bootstrap/src/test/java/io/arex/agent/bootstrap/AgentInitializerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static void tearDown() {
3737
@Test
3838
void testFirstInitialize() {
3939
try (MockedConstruction<AgentClassLoader> mocked = Mockito.mockConstruction(AgentClassLoader.class, (mock, context) -> Mockito.doReturn(InstrumentationInstallerTest.class).when(mock).loadClass(any()))){
40-
Assertions.assertDoesNotThrow(() -> AgentInitializer.initialize(instrumentation, zipFile, null));
40+
Assertions.assertDoesNotThrow(() -> AgentInitializer.initialize(instrumentation, zipFile, null, AgentInitializerTest.class.getClassLoader()));
4141
} catch (Throwable ex) {
4242
ex.printStackTrace();
4343
}
@@ -46,8 +46,8 @@ void testFirstInitialize() {
4646
@Test
4747
void testDoubleInitialize() {
4848
try (MockedConstruction<AgentClassLoader> mocked = Mockito.mockConstruction(AgentClassLoader.class, (mock, context) -> Mockito.doReturn(InstrumentationInstallerTest.class).when(mock).loadClass(any()))){
49-
Assertions.assertDoesNotThrow(() -> AgentInitializer.initialize(instrumentation, zipFile, null));
50-
Assertions.assertDoesNotThrow(() -> AgentInitializer.initialize(instrumentation, zipFile, null));
49+
Assertions.assertDoesNotThrow(() -> AgentInitializer.initialize(instrumentation, zipFile, null, AgentInitializerTest.class.getClassLoader()));
50+
Assertions.assertDoesNotThrow(() -> AgentInitializer.initialize(instrumentation, zipFile, null, AgentInitializerTest.class.getClassLoader()));
5151
} catch (Throwable ex) {
5252
ex.printStackTrace();
5353
}

arex-agent-bootstrap/src/test/java/io/arex/agent/bootstrap/CreateFileCommon.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public static File createFile(String name) {
8080
return createFile(path, name);
8181
}
8282

83-
private static File createFile(String path, String name) {
83+
public static File createFile(String path, String name) {
8484
try {
8585
File file = new File(path + name);
8686
if (!file.getParentFile().exists()) {

arex-agent-bootstrap/src/test/java/io/arex/agent/bootstrap/util/AdviceClassesCollectorTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
import java.lang.reflect.Method;
1010
import java.net.URL;
1111
import java.net.URLClassLoader;
12-
import java.nio.file.Path;
13-
import java.nio.file.Paths;
1412

1513
import org.junit.jupiter.api.AfterAll;
1614
import org.junit.jupiter.api.BeforeAll;
@@ -81,9 +79,11 @@ void testNull() {
8179
}
8280

8381
@Test
84-
public void test() {
85-
final File file = new File("D:\\Users\\yongwuhe\\IdeaProjects\\arex-agent-java\\arex-agent-jar\\arex-agent-0.3.6.jar");
86-
String enrtyName = "META-INF/services/com.fasterxml.jackson.core.JsonFactory";
87-
final Path path = Paths.get(file.getAbsolutePath() + "!/" + enrtyName);
82+
void addJarToLoaderSearch() {
83+
// no file
84+
assertDoesNotThrow(() -> AdviceClassesCollector.INSTANCE.addJarInThirdPartyToLoaderSearch("jackson"));
85+
// file path
86+
File file = CreateFileCommon.createFile(System.getProperty("java.io.tmpdir"),"/arex/third-party/jackson/jackson-test.jar");
87+
assertDoesNotThrow(() -> AdviceClassesCollector.INSTANCE.addJarInThirdPartyToLoaderSearch("jackson"));
8888
}
89-
}
89+
}

arex-instrumentation-foundation/src/test/java/io/arex/foundation/util/JdkUtilsTest.java renamed to arex-agent-bootstrap/src/test/java/io/arex/agent/bootstrap/util/JdkUtilsTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
package io.arex.foundation.util;
2-
3-
import static org.junit.jupiter.api.Assertions.*;
1+
package io.arex.agent.bootstrap.util;
42

53
import org.junit.jupiter.api.AfterEach;
64
import org.junit.jupiter.api.BeforeEach;
75
import org.junit.jupiter.api.Test;
86

9-
class JdkUtilsTest {
7+
import static org.junit.jupiter.api.Assertions.*;
108

9+
class JdkUtilsTest {
1110
@BeforeEach
1211
void setUp() {
1312
}
@@ -25,4 +24,5 @@ void getJavaVersion() {
2524
void isJdk11() {
2625
assertInstanceOf(Boolean.class, JdkUtils.isJdk11());
2726
}
28-
}
27+
28+
}

arex-agent-core/src/main/java/io/arex/agent/instrumentation/BaseAgentInstaller.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
import io.arex.agent.bootstrap.util.FileUtils;
77
import io.arex.foundation.config.ConfigManager;
88
import io.arex.foundation.healthy.HealthManager;
9-
import io.arex.foundation.serializer.jackson.JacksonSerializer;
109
import io.arex.foundation.services.ConfigService;
1110
import io.arex.foundation.services.DataCollectorService;
1211
import io.arex.foundation.services.TimerService;
1312
import io.arex.foundation.util.NetUtils;
1413
import io.arex.inst.runtime.context.RecordLimiter;
15-
import io.arex.inst.runtime.serializer.Serializer;
1614
import io.arex.inst.runtime.service.DataCollector;
1715
import io.arex.inst.runtime.service.DataService;
1816

@@ -113,16 +111,8 @@ private void timedReportStatus() {
113111
private void initDependentComponents() {
114112
TraceContextManager.init(NetUtils.getIpAddress());
115113
RecordLimiter.init(HealthManager::acquire);
116-
initSerializer();
117114
initDataCollector();
118115
}
119-
120-
/**
121-
* add class to user loader search. ex: ParallelWebappClassLoader
122-
*/
123-
private void initSerializer() {
124-
Serializer.builder(JacksonSerializer.INSTANCE).build();
125-
}
126116
private void initDataCollector() {
127117
DataCollector collector = DataCollectorService.INSTANCE;
128118
if (ConfigManager.INSTANCE.isLocalStorage()) {

arex-agent/pom.xml

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,48 @@
239239
<Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
240240
<Build-Time>${maven.build.timestamp}</Build-Time>
241241
<Built-By>arextest.com</Built-By>
242-
<Class-Path>${bootStrapJarName}</Class-Path>
243242
</manifestEntries>
244243
</archive>
245244
</configuration>
246245
</plugin>
247246

247+
<plugin>
248+
<groupId>org.apache.maven.plugins</groupId>
249+
<artifactId>maven-assembly-plugin</artifactId>
250+
<version>3.3.0</version>
251+
<configuration>
252+
<finalName>arex</finalName>
253+
<descriptors>
254+
<descriptor>${assembly-xml-path}</descriptor>
255+
</descriptors>
256+
<archive>
257+
<manifest>
258+
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
259+
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
260+
</manifest>
261+
<manifestEntries>
262+
<Premain-Class>io.arex.agent.ArexJavaAgent</Premain-Class>
263+
<Agent-Class>io.arex.agent.ArexJavaAgent</Agent-Class>
264+
<Can-Redefine-Classes>true</Can-Redefine-Classes>
265+
<Can-Retransform-Classes>true</Can-Retransform-Classes>
266+
<Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
267+
<Build-Time>${maven.build.timestamp}</Build-Time>
268+
<Built-By>arextest.com</Built-By>
269+
</manifestEntries>
270+
</archive>
271+
</configuration>
272+
273+
<executions>
274+
<execution>
275+
<id>make-assembly</id>
276+
<phase>package</phase>
277+
<goals>
278+
<goal>single</goal>
279+
</goals>
280+
</execution>
281+
</executions>
282+
</plugin>
283+
248284
<plugin>
249285
<groupId>org.apache.maven.plugins</groupId>
250286
<artifactId>maven-shade-plugin</artifactId>
@@ -282,7 +318,6 @@
282318
<include>net.bytebuddy:byte-buddy</include>
283319
<include>org.slf4j:slf4j-simple</include>
284320
<include>io.arex:**</include>
285-
<include>com.fasterxml.jackson.core:**</include>
286321
</includes>
287322
</artifactSet>
288323
</configuration>
@@ -304,7 +339,6 @@
304339
</delete>
305340
<copy todir="../arex-agent-jar">
306341
<fileset dir="../arex-agent/target/" includes="arex-agent*.jar" />
307-
<fileset dir="../arex-agent-bootstrap/target/" includes="arex-agent-bootstrap*.jar" />
308342
</copy>
309343
</target>
310344
</configuration>

0 commit comments

Comments
 (0)