Skip to content

Commit a9e2d9d

Browse files
authored
feat: init class before replay (#569)
1 parent da452e4 commit a9e2d9d

File tree

4 files changed

+80
-4
lines changed

4 files changed

+80
-4
lines changed

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
@@ -46,4 +46,5 @@ private ConfigConstants() {
4646
public static final String API_TOKEN = "arex.api.token";
4747
public static final String MOCKER_TAGS = "arex.mocker.tags";
4848
public static final String LOG_PATH = "arex.log.path";
49+
public static final String AREX_STATIC_CLASS_INIT = "arex.static.class.init";
4950
}

arex-instrumentation-api/src/main/java/io/arex/inst/runtime/listener/EventProcessor.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package io.arex.inst.runtime.listener;
22

33
import io.arex.agent.bootstrap.cache.TimeCache;
4+
import io.arex.agent.bootstrap.constants.ConfigConstants;
45
import io.arex.agent.bootstrap.model.Mocker;
56
import io.arex.agent.bootstrap.util.AdviceClassesCollector;
67
import io.arex.agent.bootstrap.util.NumberUtil;
78
import io.arex.agent.bootstrap.util.StringUtil;
89
import io.arex.agent.bootstrap.util.ServiceLoader;
10+
import io.arex.inst.runtime.config.Config;
911
import io.arex.inst.runtime.model.InitializeEnum;
1012
import io.arex.inst.runtime.request.RequestHandlerManager;
1113
import io.arex.inst.runtime.log.LogManager;
@@ -18,6 +20,7 @@
1820

1921
import java.util.List;
2022

23+
import java.util.Set;
2124
import java.util.concurrent.CompletableFuture;
2225
import java.util.concurrent.atomic.AtomicReference;
2326

@@ -119,6 +122,7 @@ public static void onRequest(){
119122
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
120123
// https://bugs.openjdk.org/browse/JDK-8172726
121124
CompletableFuture.runAsync(() -> {
125+
initClass(contextClassLoader);
122126
initSerializer(contextClassLoader);
123127
initLog(contextClassLoader);
124128
RequestHandlerManager.init(contextClassLoader);
@@ -129,6 +133,42 @@ public static void onRequest(){
129133
ContextManager.remove();
130134
}
131135

136+
/**
137+
* To prevent class initialization failure during request replay,
138+
* it is advisable to perform early initialization of classes that rely on external dependencies.
139+
* This helps to avoid triggering the class initialization logic when replaying requests.
140+
*/
141+
private static void initClass(ClassLoader contextClassLoader) {
142+
boolean disableReplay = Config.get().getBoolean(ConfigConstants.DISABLE_REPLAY, false);
143+
if (disableReplay) {
144+
return;
145+
}
146+
String arexStaticClassInit = Config.get().getString(ConfigConstants.AREX_STATIC_CLASS_INIT);
147+
if (StringUtil.isBlank(arexStaticClassInit)) {
148+
return;
149+
}
150+
Set<String> initClassNameSet = StringUtil.splitToSet(arexStaticClassInit, ',');
151+
for (String initClass : initClassNameSet) {
152+
try {
153+
if (isClassLoaded(initClass, contextClassLoader)) {
154+
continue;
155+
}
156+
Class.forName(initClass, true, contextClassLoader);
157+
} catch (ClassNotFoundException e) {
158+
LOGGER.warn(LogManager.buildTitle("init.class"), e);
159+
}
160+
}
161+
}
162+
163+
private static boolean isClassLoaded(String className, ClassLoader classLoader) {
164+
try {
165+
Class<?> clazz = classLoader.loadClass(className);
166+
return clazz != null;
167+
} catch (ClassNotFoundException e) {
168+
return false;
169+
}
170+
}
171+
132172
private static void initLog(ClassLoader contextClassLoader) {
133173
List<Logger> extensionLoggerList = ServiceLoader.load(Logger.class, contextClassLoader);
134174
LogManager.build(extensionLoggerList);

arex-instrumentation-api/src/test/java/io/arex/inst/runtime/listener/EventProcessorTest.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@
77
import com.fasterxml.jackson.databind.ObjectMapper;
88
import com.google.gson.Gson;
99
import com.google.gson.GsonBuilder;
10+
import io.arex.agent.bootstrap.constants.ConfigConstants;
1011
import io.arex.agent.bootstrap.util.StringUtil;
12+
import io.arex.inst.runtime.config.ConfigBuilder;
1113
import io.arex.inst.runtime.context.ArexContext;
1214
import io.arex.inst.runtime.context.ContextManager;
1315
import io.arex.inst.runtime.log.LogManager;
1416
import io.arex.inst.runtime.log.Logger;
15-
import io.arex.inst.runtime.serializer.Serializer;
1617
import io.arex.inst.runtime.serializer.StringSerializable;
1718
import io.arex.agent.bootstrap.util.ServiceLoader;
19+
20+
import java.lang.reflect.Method;
1821
import java.lang.reflect.Type;
19-
import java.util.Arrays;
20-
import java.util.Collections;
22+
2123
import org.junit.jupiter.api.AfterAll;
2224
import org.junit.jupiter.api.Assertions;
2325
import org.junit.jupiter.api.BeforeAll;
@@ -33,12 +35,16 @@ public class EventProcessorTest {
3335
static Logger logger = null;
3436
static MockedStatic<LogManager> mockedStatic = null;
3537
static MockedStatic<ContextManager> contextMockedStatic = null;
38+
static ConfigBuilder configBuilder;
3639
@BeforeAll
3740
static void setUp() {
3841
contextMockedStatic = Mockito.mockStatic(ContextManager.class);
3942
Mockito.mockStatic(ServiceLoader.class);
4043
logger = Mockito.mock(Logger.class);
4144
mockedStatic = Mockito.mockStatic(LogManager.class);
45+
configBuilder = ConfigBuilder.create("testInitClass");
46+
// disPlay
47+
configBuilder.build();
4248
}
4349

4450
@AfterAll
@@ -47,6 +53,8 @@ static void tearDown() {
4753
mockedStatic = null;
4854
contextMockedStatic = null;
4955
Mockito.clearAllCaches();
56+
configBuilder.build();
57+
configBuilder = null;
5058
}
5159

5260
@Test
@@ -80,6 +88,27 @@ void testInit() throws InterruptedException {
8088
contextMockedStatic.verify(() -> ContextManager.remove(), Mockito.times(1));
8189
}
8290

91+
@Test
92+
@Order(3)
93+
void testInitClass() throws Exception {
94+
Method initClassMethod = EventProcessor.class.getDeclaredMethod("initClass", ClassLoader.class);
95+
initClassMethod.setAccessible(true);
96+
// disPlay
97+
initClassMethod.invoke(null, Thread.currentThread().getContextClassLoader());
98+
mockedStatic.verify(() -> LogManager.buildTitle("init.class"), Mockito.never());
99+
// not display but no initClass
100+
configBuilder.addProperty(ConfigConstants.DISABLE_REPLAY, "true");
101+
configBuilder.build();
102+
initClassMethod.invoke(null, Thread.currentThread().getContextClassLoader());
103+
mockedStatic.verify(() -> LogManager.buildTitle("init.class"), Mockito.never());
104+
// no display and initClass
105+
configBuilder.addProperty(ConfigConstants.DISABLE_REPLAY, "false");
106+
configBuilder.addProperty(ConfigConstants.AREX_STATIC_CLASS_INIT, "test1,io.arex.inst.runtime.listener.EventProcessorTest");
107+
configBuilder.build();
108+
initClassMethod.invoke(null, Thread.currentThread().getContextClassLoader());
109+
mockedStatic.verify(() -> LogManager.buildTitle("init.class"), Mockito.times(1));
110+
}
111+
83112
@Test
84113
void onExit() {
85114
Assertions.assertDoesNotThrow(EventProcessor::onExit);

arex-instrumentation-foundation/src/test/java/io/arex/foundation/logger/AgentLoggerFactoryTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import io.arex.agent.bootstrap.constants.ConfigConstants;
66
import java.io.IOException;
7+
import java.io.PrintStream;
8+
import java.lang.reflect.Field;
9+
import java.lang.reflect.Method;
710
import java.nio.file.Files;
811
import java.nio.file.Paths;
912
import java.time.LocalDate;
@@ -16,8 +19,11 @@
1619
class AgentLoggerFactoryTest {
1720

1821
@Test
19-
void getAgentLogger() throws IOException {
22+
void getAgentLogger() throws Exception {
2023
System.setProperty(ConfigConstants.LOG_PATH, "/var/tmp");
24+
Method getPrintStream = AgentLoggerFactory.class.getDeclaredMethod("getPrintStream", String.class);
25+
getPrintStream.setAccessible(true);
26+
getPrintStream.invoke(null, "startup");
2127
AgentLogger agentLogger = AgentLoggerFactory.getAgentLogger(AgentLoggerFactoryTest.class);
2228
assertEquals(AgentLoggerFactoryTest.class.getName(), agentLogger.getName());
2329

0 commit comments

Comments
 (0)