Skip to content

Commit c5fe133

Browse files
authored
Use SpringVersion to detect MethodParameters usage (#10750)
Use SpringVersion to detect MethodParameters usage Prefer using SpringVersion class to get the Spring version reliably Use the specific class method as fallback fix test Co-authored-by: jean-philippe.bempel <jean-philippe.bempel@datadoghq.com>
1 parent bd05946 commit c5fe133

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SpringHelper.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,50 @@
11
package com.datadog.debugger.util;
22

33
import java.lang.instrument.Instrumentation;
4+
import java.lang.reflect.Method;
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
49

510
public class SpringHelper {
11+
private static final Logger LOGGER = LoggerFactory.getLogger(SpringHelper.class);
612

713
public static boolean isSpringUsingOnlyMethodParameters(Instrumentation inst) {
14+
try {
15+
return isSpringUsingOnlyMethodParametersSpringVersion(inst);
16+
} catch (Exception e) {
17+
LOGGER.debug("isSpringUsingOnlyMethodParameters failed for SpringVersion", e);
18+
// fallback to lookup for specific class
19+
return isSpringUsingOnlyMethodParametersSpecificClass(inst);
20+
}
21+
}
22+
23+
private static boolean isSpringUsingOnlyMethodParametersSpringVersion(Instrumentation inst) {
24+
try {
25+
// scan for getting an already loaded class and get the classloader
26+
ClassLoader springClassLoader = null;
27+
for (Class<?> clazz : inst.getAllLoadedClasses()) {
28+
if (clazz.getName().startsWith("org.springframework.core")) {
29+
springClassLoader = clazz.getClassLoader();
30+
}
31+
}
32+
if (springClassLoader == null) {
33+
throw new IllegalStateException("Cannot find Spring classloader");
34+
}
35+
Class<?> springVersionClass =
36+
Class.forName("org.springframework.core.SpringVersion", true, springClassLoader);
37+
Method m = springVersionClass.getDeclaredMethod("getVersion");
38+
String version = (String) m.invoke(null);
39+
ParsedSpringVersion springVersion = new ParsedSpringVersion(version);
40+
// if Spring version is 6.1+ only using MethodParameters
41+
return springVersion.major > 6 || (springVersion.major == 6 && springVersion.minor >= 1);
42+
} catch (Exception e) {
43+
throw new RuntimeException(e);
44+
}
45+
}
46+
47+
private static boolean isSpringUsingOnlyMethodParametersSpecificClass(Instrumentation inst) {
848
for (Class<?> clazz : inst.getAllLoadedClasses()) {
949
if ("org.springframework.web.client.RestClient".equals(clazz.getName())) {
1050
// If this class (coming from Spring web since version 6.1) is found loaded it means Spring
@@ -15,4 +55,23 @@ public static boolean isSpringUsingOnlyMethodParameters(Instrumentation inst) {
1555
// class not found, probably no Spring
1656
return false;
1757
}
58+
59+
private static class ParsedSpringVersion {
60+
private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
61+
62+
final int major;
63+
final int minor;
64+
final int patch;
65+
66+
public ParsedSpringVersion(String strVersion) {
67+
Matcher matcher = VERSION_PATTERN.matcher(strVersion);
68+
if (matcher.find()) {
69+
major = Integer.parseInt(matcher.group(1));
70+
minor = Integer.parseInt(matcher.group(2));
71+
patch = Integer.parseInt(matcher.group(3));
72+
} else {
73+
throw new IllegalArgumentException("Cannot parse SpringVersion: " + strVersion);
74+
}
75+
}
76+
}
1877
}

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ public void methodParametersAttribute() throws Exception {
639639
Class<?> testClass = loadClass(CLASS_NAME, buffers);
640640
if (JavaVirtualMachine.isJavaVersion(17)) {
641641
// on JDK 17 introduced Spring6 class
642-
Class<?> springClass = Class.forName("org.springframework.web.client.RestClient");
642+
Class<?> springClass = Class.forName("org.springframework.core.SpringVersion");
643643
when(inst.getAllLoadedClasses()).thenReturn(new Class[] {testClass, springClass});
644644
} else {
645645
when(inst.getAllLoadedClasses()).thenReturn(new Class[] {testClass});

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/SpringHelperTest.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,24 @@ class SpringHelperTest {
1313

1414
@Test
1515
@EnabledForJreRange(min = JRE.JAVA_17)
16-
void isSpringUsingOnlyMethodParametersTrue() throws Exception {
16+
void isSpringUsingOnlyMethodParametersTrueSpringVersion() throws Exception {
17+
Class<?> clazz = Class.forName("org.springframework.core.SpringVersion");
18+
Instrumentation inst = mock(Instrumentation.class);
19+
when(inst.getAllLoadedClasses()).thenReturn(new Class[] {clazz});
20+
assertTrue(SpringHelper.isSpringUsingOnlyMethodParameters(inst));
21+
}
22+
23+
@Test
24+
@EnabledForJreRange(min = JRE.JAVA_17)
25+
void isSpringUsingOnlyMethodParametersTrueFallback() throws Exception {
1726
Class<?> clazz = Class.forName("org.springframework.web.client.RestClient");
1827
Instrumentation inst = mock(Instrumentation.class);
1928
when(inst.getAllLoadedClasses()).thenReturn(new Class[] {clazz});
2029
assertTrue(SpringHelper.isSpringUsingOnlyMethodParameters(inst));
2130
}
2231

2332
@Test
24-
void isSpringUsingOnlyMethodParametersFalse() throws Exception {
33+
void isSpringUsingOnlyMethodParametersFalseFallback() throws Exception {
2534
Instrumentation inst = mock(Instrumentation.class);
2635
when(inst.getAllLoadedClasses()).thenReturn(new Class[0]);
2736
assertFalse(SpringHelper.isSpringUsingOnlyMethodParameters(inst));

0 commit comments

Comments
 (0)