Skip to content

Commit 2dd49e9

Browse files
committed
Use SpringVersion to detect MethodParameters usage
Prefer using SpringVersion class to get the Spring version reliably Use the specific class method as fallback
1 parent 4c3b6f3 commit 2dd49e9

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

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/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)