diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SpringHelper.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SpringHelper.java index 73ec8be30c5..f8826efb16a 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SpringHelper.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SpringHelper.java @@ -1,10 +1,50 @@ package com.datadog.debugger.util; import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SpringHelper { + private static final Logger LOGGER = LoggerFactory.getLogger(SpringHelper.class); public static boolean isSpringUsingOnlyMethodParameters(Instrumentation inst) { + try { + return isSpringUsingOnlyMethodParametersSpringVersion(inst); + } catch (Exception e) { + LOGGER.debug("isSpringUsingOnlyMethodParameters failed for SpringVersion", e); + // fallback to lookup for specific class + return isSpringUsingOnlyMethodParametersSpecificClass(inst); + } + } + + private static boolean isSpringUsingOnlyMethodParametersSpringVersion(Instrumentation inst) { + try { + // scan for getting an already loaded class and get the classloader + ClassLoader springClassLoader = null; + for (Class clazz : inst.getAllLoadedClasses()) { + if (clazz.getName().startsWith("org.springframework.core")) { + springClassLoader = clazz.getClassLoader(); + } + } + if (springClassLoader == null) { + throw new IllegalStateException("Cannot find Spring classloader"); + } + Class springVersionClass = + Class.forName("org.springframework.core.SpringVersion", true, springClassLoader); + Method m = springVersionClass.getDeclaredMethod("getVersion"); + String version = (String) m.invoke(null); + ParsedSpringVersion springVersion = new ParsedSpringVersion(version); + // if Spring version is 6.1+ only using MethodParameters + return springVersion.major > 6 || (springVersion.major == 6 && springVersion.minor >= 1); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static boolean isSpringUsingOnlyMethodParametersSpecificClass(Instrumentation inst) { for (Class clazz : inst.getAllLoadedClasses()) { if ("org.springframework.web.client.RestClient".equals(clazz.getName())) { // 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) { // class not found, probably no Spring return false; } + + private static class ParsedSpringVersion { + private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)"); + + final int major; + final int minor; + final int patch; + + public ParsedSpringVersion(String strVersion) { + Matcher matcher = VERSION_PATTERN.matcher(strVersion); + if (matcher.find()) { + major = Integer.parseInt(matcher.group(1)); + minor = Integer.parseInt(matcher.group(2)); + patch = Integer.parseInt(matcher.group(3)); + } else { + throw new IllegalArgumentException("Cannot parse SpringVersion: " + strVersion); + } + } + } } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java index 2be1684c2d5..8956c182086 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationUpdaterTest.java @@ -639,7 +639,7 @@ public void methodParametersAttribute() throws Exception { Class testClass = loadClass(CLASS_NAME, buffers); if (JavaVirtualMachine.isJavaVersion(17)) { // on JDK 17 introduced Spring6 class - Class springClass = Class.forName("org.springframework.web.client.RestClient"); + Class springClass = Class.forName("org.springframework.core.SpringVersion"); when(inst.getAllLoadedClasses()).thenReturn(new Class[] {testClass, springClass}); } else { when(inst.getAllLoadedClasses()).thenReturn(new Class[] {testClass}); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/SpringHelperTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/SpringHelperTest.java index f2a3e82a1fa..526ecdd2adf 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/SpringHelperTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/SpringHelperTest.java @@ -13,7 +13,16 @@ class SpringHelperTest { @Test @EnabledForJreRange(min = JRE.JAVA_17) - void isSpringUsingOnlyMethodParametersTrue() throws Exception { + void isSpringUsingOnlyMethodParametersTrueSpringVersion() throws Exception { + Class clazz = Class.forName("org.springframework.core.SpringVersion"); + Instrumentation inst = mock(Instrumentation.class); + when(inst.getAllLoadedClasses()).thenReturn(new Class[] {clazz}); + assertTrue(SpringHelper.isSpringUsingOnlyMethodParameters(inst)); + } + + @Test + @EnabledForJreRange(min = JRE.JAVA_17) + void isSpringUsingOnlyMethodParametersTrueFallback() throws Exception { Class clazz = Class.forName("org.springframework.web.client.RestClient"); Instrumentation inst = mock(Instrumentation.class); when(inst.getAllLoadedClasses()).thenReturn(new Class[] {clazz}); @@ -21,7 +30,7 @@ void isSpringUsingOnlyMethodParametersTrue() throws Exception { } @Test - void isSpringUsingOnlyMethodParametersFalse() throws Exception { + void isSpringUsingOnlyMethodParametersFalseFallback() throws Exception { Instrumentation inst = mock(Instrumentation.class); when(inst.getAllLoadedClasses()).thenReturn(new Class[0]); assertFalse(SpringHelper.isSpringUsingOnlyMethodParameters(inst));