diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7acf78d19f..8dd5c2f912 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -320,7 +320,7 @@ jobs:
test-java-distribution: ${{ matrix.distribution }}
command: ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true
- name: Run tests for ${{ matrix.version }}:${{ matrix.distribution }}
- run: ./mvnw test -Delastic.jdkCompatibilityTest=true -Dtest_java_binary=${{ env.TEST_JAVA_BINARY }}
+ run: ./mvnw test -Delastic.jdkCompatibilityTest=true -Dtest_java_binary=${{ env.TEST_JAVA_BINARY }} -Dtest_java_version=${{ matrix.version }}
- name: Store test results
if: success() || failure()
uses: actions/upload-artifact@v6
diff --git a/CHANGELOG.next-release.md b/CHANGELOG.next-release.md
index 74d5d98219..f3f3f448b7 100644
--- a/CHANGELOG.next-release.md
+++ b/CHANGELOG.next-release.md
@@ -15,7 +15,7 @@ This file contains all changes which are not released yet.
# Features and enhancements
-
+* Support Spring Boot 4
# Deprecations
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml
index 096efbba2e..3a09af902d 100644
--- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/pom.xml
@@ -54,4 +54,19 @@
test
+
+
+
+
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/src/test/java/co/elastic/apm/agent/restclient/SpringRestClientInstrumentationTest.java b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/src/test/java/co/elastic/apm/agent/restclient/SpringRestClientInstrumentationTest.java
index 78790f7e5f..8a91db45cf 100644
--- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/src/test/java/co/elastic/apm/agent/restclient/SpringRestClientInstrumentationTest.java
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-restclient-test/src/test/java/co/elastic/apm/agent/restclient/SpringRestClientInstrumentationTest.java
@@ -66,7 +66,7 @@ protected boolean isRedirectFollowingSupported() {
* The code is compiled with java 17 but potentially run with java 11.
* JUnit will inspect the test class, therefore it must not contain any references to java 17 code.
*/
- private static class Java17Code {
+ public static class Java17Code {
public static void performGet(Object restClient, String path) {
((RestClient) restClient).get().uri(path).retrieve().body(String.class);
}
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestRequestHeaderSetter.java b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestRequestHeaderSetter.java
index ff04b829bc..83e733c4ef 100644
--- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestRequestHeaderSetter.java
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestRequestHeaderSetter.java
@@ -27,7 +27,8 @@ public class SpringRestRequestHeaderSetter implements TextHeaderSetter
+
+ ${project.groupId}
+ apm-httpclient-core
+ ${project.version}
+ test-jar
+ test
+
${project.groupId}
apm-spring-resttemplate-plugin
@@ -31,6 +38,13 @@
test-jar
test
+
+ ${project.groupId}
+ apm-spring-restclient-test
+ ${project.version}
+ test-jar
+ test
+
org.apache.ivy
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestClientVersionsIT.java b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestClientVersionsIT.java
new file mode 100644
index 0000000000..661107b59e
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestClientVersionsIT.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.resttemplate;
+
+import co.elastic.apm.agent.restclient.SpringRestClientInstrumentationTest;
+import co.elastic.apm.agent.testutils.TestClassWithDependencyRunner;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@EnabledForJreRange(min = JRE.JAVA_17, disabledReason = "Spring 6 and up require JDK 17")
+public class SpringRestClientVersionsIT {
+
+
+ @Test
+ void version6() throws Exception {
+ testVersionImpl("6.1.0");
+ }
+
+ @Test
+ void version7() throws Exception {
+ testVersionImpl("7.0.0");
+ }
+
+ void testVersionImpl(String version) throws Exception {
+ var springDependencies = Stream.of(
+ "spring-webmvc",
+ "spring-aop",
+ "spring-beans",
+ "spring-context",
+ "spring-core",
+ "spring-expression",
+ "spring-web")
+ .map(s -> String.format("org.springframework:%s:%s", s, version));
+
+ var additionalDependencies = Stream.of(
+ "io.micrometer:micrometer-observation:1.10.2",
+ "io.micrometer:micrometer-commons:1.10.2",
+ "commons-logging:commons-logging:1.3.0",
+ "org.apache.logging.log4j:log4j-api:2.25.3"
+ );
+
+ List dependencies = Stream.concat(springDependencies, additionalDependencies).collect(Collectors.toList());
+
+ new TestClassWithDependencyRunner(dependencies, SpringRestClientInstrumentationTest.class.getName(), SpringRestClientInstrumentationTest.Java17Code.class.getName()).run();
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateVersionsIT.java b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateVersionsIT.java
index 16de8f0b51..88f20f8395 100644
--- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateVersionsIT.java
+++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/src/test/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateVersionsIT.java
@@ -92,6 +92,20 @@ void testVersion6(String version) throws Exception {
));
}
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "7.0.0"
+ })
+ @EnabledForJreRange(min = JRE.JAVA_17, disabledReason = "Spring 7 requires JDK 17")
+ void testVersion7(String version) throws Exception {
+ testVersionImpl(version, true, List.of(
+ "commons-logging:commons-logging:1.3.0",
+ "io.micrometer:micrometer-observation:1.10.2",
+ "io.micrometer:micrometer-commons:1.10.2",
+ "org.apache.logging.log4j:log4j-api:2.25.3"
+ ));
+ }
+
void testVersionImpl(String version, boolean isSupported, List additionalDependencies) throws Exception {
List dependencies = Stream.of(
"spring-webmvc",
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/pom.xml b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/pom.xml
new file mode 100644
index 0000000000..a4f1e1eb2e
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+
+ apm-spring-webmvc
+ co.elastic.apm
+ 1.55.5-SNAPSHOT
+
+
+ apm-spring-webmvc-spring7-test
+ ${project.groupId}:${project.artifactId}
+
+
+ true
+ ${project.basedir}/../../..
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 4.0.0
+ pom
+ import
+
+
+
+
+
+
+ ${project.groupId}
+ apm-spring-webmvc-plugin
+ ${project.version}
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ test
+
+
+ ${project.groupId}
+ apm-spring-webmvc-spring5
+ test-jar
+ ${project.version}
+ test
+
+
+ org.thymeleaf
+ thymeleaf-spring6
+ test
+
+
+
+
+
+
+
+
+ testing-jdk-11
+
+
+ test_java_version
+ 11
+
+
+
+ true
+
+
+
+
+
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/Spring7TransactionNameInstrumentationTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/Spring7TransactionNameInstrumentationTest.java
new file mode 100644
index 0000000000..ccc78312ea
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/Spring7TransactionNameInstrumentationTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.ServletWrappingController;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.function.Function;
+
+public class Spring7TransactionNameInstrumentationTest extends AbstractSpringTransactionNameInstrumentationTest {
+ public static class TestServlet extends HttpServlet {
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.getWriter().print("TestServlet");
+ }
+ }
+
+ @Configuration
+ public static class TestConfiguration extends AbstractTestConfiguration {
+
+ @Override
+ protected Class> getTestServletClass() {
+ return TestServlet.class;
+ }
+
+ @Override
+ protected void configureTestServletOnController(ServletWrappingController controller) {
+ controller.setServletClass(TestServlet.class);
+ }
+
+ @Override
+ protected ServletWrappingController createMyCustomController(Function handler) {
+ return new MyCustomController(handler);
+ }
+
+ @Override
+ protected ServletWrappingController createAnonymousController(Function handler) {
+ return new ServletWrappingController() {
+ @Override
+ public ModelAndView handleRequest(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ return handler.apply(response.getWriter());
+ }
+ };
+ }
+ }
+
+
+ private static class MyCustomController extends ServletWrappingController {
+
+ private final Function handler;
+
+ public MyCustomController(Function handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ return handler.apply(response.getWriter());
+ }
+ }
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithExceptionHandlerTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithExceptionHandlerTest.java
new file mode 100644
index 0000000000..f91c43a341
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithExceptionHandlerTest.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.exception;
+
+public class Spring7ExceptionHandlerInstrumentationWithExceptionHandlerTest extends Spring5ExceptionHandlerInstrumentationWithExceptionHandlerTest {
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithExceptionResolverTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithExceptionResolverTest.java
new file mode 100644
index 0000000000..37c5c74db1
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithExceptionResolverTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.exception;
+
+import co.elastic.apm.agent.springwebmvc.exception.testapp.exception_resolver.AbstractRestResponseStatusExceptionResolver;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.web.servlet.ModelAndView;
+
+@ContextConfiguration(classes = {
+ Spring7ExceptionHandlerInstrumentationWithExceptionResolverTest.Spring7ResponseStatusExceptionResolver.class
+})
+public class Spring7ExceptionHandlerInstrumentationWithExceptionResolverTest extends AbstractExceptionHandlerInstrumentationWithExceptionResolverTest {
+
+ @Component
+ public static class Spring7ResponseStatusExceptionResolver extends AbstractRestResponseStatusExceptionResolver {
+
+ @Override
+ protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
+ return super.doResolveException(handler, ex);
+ }
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithGlobalAdviceTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithGlobalAdviceTest.java
new file mode 100644
index 0000000000..59d73baa0f
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithGlobalAdviceTest.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.exception;
+
+public class Spring7ExceptionHandlerInstrumentationWithGlobalAdviceTest extends Spring5ExceptionHandlerInstrumentationWithGlobalAdviceTest {
+
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithResponseStatusExceptionTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithResponseStatusExceptionTest.java
new file mode 100644
index 0000000000..d3bb1cd497
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/exception/Spring7ExceptionHandlerInstrumentationWithResponseStatusExceptionTest.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.exception;
+
+public class Spring7ExceptionHandlerInstrumentationWithResponseStatusExceptionTest extends Spring5ExceptionHandlerInstrumentationWithResponseStatusExceptionTest {
+ }
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7FreeMarkerViewTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7FreeMarkerViewTest.java
new file mode 100644
index 0000000000..75911e76c0
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7FreeMarkerViewTest.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.template;
+
+public class Spring7FreeMarkerViewTest extends Spring5FreeMarkerViewTest {
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7GroovyTemplateTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7GroovyTemplateTest.java
new file mode 100644
index 0000000000..6b3e42a614
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7GroovyTemplateTest.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.template;
+
+public class Spring7GroovyTemplateTest extends Spring5GroovyTemplateTest {
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7Jackson2JsonViewTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7Jackson2JsonViewTest.java
new file mode 100644
index 0000000000..3dda62242f
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7Jackson2JsonViewTest.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.template;
+
+public class Spring7Jackson2JsonViewTest extends Spring5Jackson2JsonViewTest {
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7JspViewTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7JspViewTest.java
new file mode 100644
index 0000000000..c275bf6b40
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7JspViewTest.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.template;
+
+public class Spring7JspViewTest extends Spring5JspViewTest {
+
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7ThymeleafTest.java b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7ThymeleafTest.java
new file mode 100644
index 0000000000..604b829e01
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/java/co/elastic/apm/agent/springwebmvc/template/Spring7ThymeleafTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.springwebmvc.template;
+
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.thymeleaf.spring6.SpringTemplateEngine;
+import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
+import org.thymeleaf.spring6.view.ThymeleafViewResolver;
+
+@ContextConfiguration(classes = {Spring7ThymeleafTest.ThymeleafConfiguration.class})
+public class Spring7ThymeleafTest extends AbstractThymeleafTest {
+
+ @Configuration
+ @EnableWebMvc
+ public static class ThymeleafConfiguration {
+
+ @Bean
+ public SpringTemplateEngine templateEngine() {
+ SpringTemplateEngine templateEngine = new SpringTemplateEngine();
+ templateEngine.setTemplateResolver(thymeleafTemplateResolver());
+ return templateEngine;
+ }
+
+ @Bean
+ public SpringResourceTemplateResolver thymeleafTemplateResolver() {
+ SpringResourceTemplateResolver templateResolver
+ = new SpringResourceTemplateResolver();
+ templateResolver.setPrefix("/thymeleaf/");
+ templateResolver.setSuffix(".html");
+ templateResolver.setTemplateMode("HTML5");
+ return templateResolver;
+ }
+
+ @Bean
+ public ThymeleafViewResolver thymeleafViewResolver() {
+ ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
+ viewResolver.setTemplateEngine(templateEngine());
+ return viewResolver;
+ }
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/freemarker/example.ftl b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/freemarker/example.ftl
new file mode 100644
index 0000000000..f415507f83
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/freemarker/example.ftl
@@ -0,0 +1 @@
+FreeMarker Template example: ${message}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/groovy/hello.tpl b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/groovy/hello.tpl
new file mode 100644
index 0000000000..95c1773b32
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/groovy/hello.tpl
@@ -0,0 +1,11 @@
+yieldUnescaped ''
+html(lang:'en') {
+ head {
+ meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')
+ title('My page')
+ }
+ body {
+ h2 ('A Groovy View with Spring MVC')
+ div ("msg: $message")
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/jade/hello.jade b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/jade/hello.jade
new file mode 100644
index 0000000000..3469148c5f
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/jade/hello.jade
@@ -0,0 +1,4 @@
+doctype html
+html
+ body
+ Hello #{message}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/jsp/message-view.jsp b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/jsp/message-view.jsp
new file mode 100644
index 0000000000..254821bcee
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/jsp/message-view.jsp
@@ -0,0 +1 @@
+${message}
diff --git a/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/thymeleaf/thymeleaf.html b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/thymeleaf/thymeleaf.html
new file mode 100644
index 0000000000..cd303dbf89
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webmvc/apm-spring-webmvc-spring7-test/src/test/resources/thymeleaf/thymeleaf.html
@@ -0,0 +1 @@
+
diff --git a/apm-agent-plugins/apm-spring-webmvc/pom.xml b/apm-agent-plugins/apm-spring-webmvc/pom.xml
index c339369d3a..c0b6ce19e0 100644
--- a/apm-agent-plugins/apm-spring-webmvc/pom.xml
+++ b/apm-agent-plugins/apm-spring-webmvc/pom.xml
@@ -20,6 +20,7 @@
apm-spring-webmvc-plugin
apm-spring-webmvc-spring5
+ apm-spring-webmvc-spring7-test
diff --git a/docs/reference/supported-technologies.md b/docs/reference/supported-technologies.md
index e9251b2fee..9b8d2ca68e 100644
--- a/docs/reference/supported-technologies.md
+++ b/docs/reference/supported-technologies.md
@@ -78,10 +78,10 @@ As this message states, you can disable this check if required by adding `-Delas
| Framework | Supported versions | Description | Since |
|-----------------------------------|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------|
| Servlet API | ≥ 3.x, ≤ 6.x | A transaction will be created for all incoming HTTP requests to your Servlet API-based application. Starting in version 1.18.0, additional spans are created if the servlet dispatches execution to another servlet through the `forward` or `include` APIs, or to an error page. See also [Application Servers/Servlet Containers](#supported-app-servers) | 1.0.0, 4.0+ (`jakarta.servlet`) since 1.28.0 |
-| Spring Web MVC | ≥ 4.x, ≤ 6.x | If you are using Spring MVC (for example with Spring Boot), the transactions are named based on your controllers (`ControllerClass#controllerMethod`). | 1.0.0, 6.x since 1.38.0 |
+| Spring Web MVC | ≥ 4.x, ≤ 7.x | If you are using Spring MVC (for example with Spring Boot), the transactions are named based on your controllers (`ControllerClass#controllerMethod`). | 1.0.0, 6.x since 1.38.0 |
| Spring Webflux | ≥ 5.2.3, ≤ 6.x | Creates transactions for incoming HTTP requests, supports annotated and functional endpoints. | 1.24.0 (experimental), 1.34.0 (GA), 6.1+ since 1.45.0 |
| JavaServer Faces | ≥ 2.2.x, ≤ 3.0.0 | If you are using JSF, transactions are named based on the requested Facelets and spans are captured for visibility into execution andrendering | 1.0.0, `jakarta.faces` since 1.28.0 |
-| Spring Boot | ≥ 1.5.x, ≤ 3.x | Supports embedded Tomcat, Jetty and Undertow | 1.0.0, 3.x since 1.38.0 |
+| Spring Boot | ≥ 1.5.x, ≤ 4.x | Supports embedded Tomcat, Jetty and Undertow | 1.0.0, 3.x since 1.38.0 |
| JAX-RS | ≥ 2.x, ≤ 3.x | The transactions are named based on your resources (`ResourceClass#resourceMethod`). Note that only the packages configured in [`application_packages`](/reference/config-stacktrace.md#config-application-packages) are scanned for JAX-RS resources. If you don’t set this option, all classes are scanned. This comes at the cost of increased startup times, however.
Note: JAX-RS is only supported when running on a supported [Application Server/Servlet Container](#supported-app-servers). | 1.0.0, `jakarta.ws.rs` since 1.28.0 |
| JAX-WS | | The transactions are named based on your `@javax.jws.WebService`, `@jakarta.jws.WebService` annotated classes and `@javax.jws.WebMethod`, `@jakarta.jws.WebMethod` annotated method names (`WebServiceClass#webMethod`). Note that only the packages configured in [`application_packages`](/reference/config-stacktrace.md#config-application-packages) are scanned for JAX-WS resources. If you don’t set this option, all classes are scanned. This comes at the cost of increased startup times, however.
Note: JAX-WS is only supported when running on a supported [Application Server/Servlet Container](#supported-app-servers) and when using the HTTP binding. | 1.4.0, `jakarta.jws` since 1.28.0 |
| Grails | ≥ 3.x, ≤ 4.x | | 1.17.0 |
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index cb6fef8c90..a8367e1896 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -29,6 +29,7 @@
jakartaee-jsf-app
main-app-test
spring-boot-3
+ spring-boot-4
aws-lambda-test
diff --git a/integration-tests/spring-boot-4/pom.xml b/integration-tests/spring-boot-4/pom.xml
new file mode 100644
index 0000000000..34ce6db3c0
--- /dev/null
+++ b/integration-tests/spring-boot-4/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+
+
+ integration-tests
+ co.elastic.apm
+ 1.55.5-SNAPSHOT
+
+
+ spring-boot-4
+ pom
+
+ ${project.groupId}:${project.artifactId}
+
+
+ spring-boot-4-base
+ spring-boot-4-jetty
+ spring-boot-4-tomcat
+
+
+
+ ${project.basedir}/../..
+ true
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 4.0.0
+ pom
+ import
+
+
+ ${project.groupId}
+ spring-boot-4-base
+ ${project.version}
+ test-jar
+ test
+
+
+
+
+
+
+ ${project.groupId}
+ apm-agent-core
+ test-jar
+ ${project.version}
+ test
+
+
+
+ ${project.groupId}
+ apm-spring-webmvc-plugin
+ ${project.version}
+
+
+ ${project.groupId}
+ apm-servlet-plugin
+ ${project.version}
+
+
+ ${project.groupId}
+ apm-api-plugin
+ ${project.version}
+
+
+ ${project.groupId}
+ apm-agent-api
+ ${project.version}
+ test
+
+
+
diff --git a/integration-tests/spring-boot-4/spring-boot-4-base/pom.xml b/integration-tests/spring-boot-4/spring-boot-4-base/pom.xml
new file mode 100644
index 0000000000..76934d49b2
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-base/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+
+ spring-boot-4
+ co.elastic.apm
+ 1.55.5-SNAPSHOT
+
+
+ spring-boot-4-base
+
+ ${project.groupId}:${project.artifactId}
+
+
+ ${project.basedir}/../../..
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ org.springframework.boot
+ spring-boot-resttestclient
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+
+
diff --git a/integration-tests/spring-boot-4/spring-boot-4-base/src/test/java/co/elastic/apm/spring/boot/AbstractSpringBootTest.java b/integration-tests/spring-boot-4/spring-boot-4-base/src/test/java/co/elastic/apm/spring/boot/AbstractSpringBootTest.java
new file mode 100644
index 0000000000..2b34657522
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-base/src/test/java/co/elastic/apm/spring/boot/AbstractSpringBootTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.spring.boot;
+
+import co.elastic.apm.agent.MockReporter;
+import co.elastic.apm.agent.MockTracer;
+import co.elastic.apm.agent.bci.ElasticApmAgent;
+import co.elastic.apm.agent.impl.transaction.TransactionImpl;
+import co.elastic.apm.agent.report.ReporterConfigurationImpl;
+import co.elastic.apm.agent.tracer.configuration.WebConfiguration;
+import co.elastic.apm.api.ElasticApm;
+import net.bytebuddy.agent.ByteBuddyAgent;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.restclient.RestTemplateBuilder;
+import org.springframework.boot.resttestclient.TestRestTemplate;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.stagemonitor.configuration.ConfigurationRegistry;
+
+import java.time.Duration;
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = RANDOM_PORT)
+public abstract class AbstractSpringBootTest {
+
+ private static MockReporter reporter;
+ private static ConfigurationRegistry config;
+ @LocalServerPort
+ private int port;
+ private TestRestTemplate restTemplate;
+
+ @BeforeAll
+ public static void beforeClass() {
+ MockTracer.MockInstrumentationSetup mockInstrumentationSetup = MockTracer.createMockInstrumentationSetup();
+ config = mockInstrumentationSetup.getConfig();
+ reporter = mockInstrumentationSetup.getReporter();
+ ElasticApmAgent.initInstrumentation(mockInstrumentationSetup.getTracer(), ByteBuddyAgent.install());
+ }
+
+ @AfterAll
+ public static void tearDown() {
+ ElasticApmAgent.reset();
+ }
+
+ @BeforeEach
+ public void setUp() {
+ doReturn(true).when(config.getConfig(ReporterConfigurationImpl.class)).isReportSynchronously();
+ restTemplate = new TestRestTemplate(new RestTemplateBuilder()
+ .connectTimeout(Duration.ofSeconds(10))
+ .readTimeout(Duration.ofSeconds(10))
+ .basicAuthentication("username", "password"));
+ reporter.reset();
+ }
+
+ @Test
+ public void greetingShouldReturnDefaultMessage() throws Exception {
+ assertThat(restTemplate.getForObject("http://localhost:" + port + "/", String.class))
+ .contains("Hello World");
+
+ // the transaction might not have been reported yet, as the http call returns when the ServletOutputStream has been closed,
+ // which is before the transaction has ended
+ final TransactionImpl transaction = reporter.getFirstTransaction(500);
+ assertThat(transaction.getNameAsString()).isEqualTo("TestApp#greeting");
+ assertThat(transaction.getContext().getUser().getDomain()).isEqualTo("domain");
+ assertThat(transaction.getContext().getUser().getId()).isEqualTo("id");
+ assertThat(transaction.getContext().getUser().getEmail()).isEqualTo("email");
+ assertThat(transaction.getContext().getUser().getUsername()).isEqualTo("username");
+ // as this test runs in a standalone application and not in a servlet container,
+ // the service.name will not be overwritten for the webapp class loader based on spring.application.name
+ assertThat(transaction.getTraceContext().getServiceName()).isNull();
+ assertThat(transaction.getFrameworkName()).isEqualTo("Spring Web MVC");
+ assertThat(transaction.getFrameworkVersion()).matches(getExpectedSpringVersionRegex());
+ }
+
+ protected String getExpectedSpringVersionRegex() {
+ return "5\\.[0-9]+\\.[0-9]+";
+ }
+
+ @Test
+ public void testStaticFile() {
+ doReturn(Collections.emptyList()).when(config.getConfig(WebConfiguration.class)).getIgnoreUrls();
+ assertThat(restTemplate.getForObject("http://localhost:" + port + "/script.js", String.class))
+ .contains("// empty test script");
+
+ assertThat(reporter.getFirstTransaction(500).getNameAsString()).isEqualTo("ResourceHttpRequestHandler");
+ assertThat(reporter.getFirstTransaction().getFrameworkName()).isEqualTo("Spring Web MVC");
+ assertThat(reporter.getFirstTransaction().getContext().getUser().getUsername()).isEqualTo("username");
+ }
+
+ @RestController
+ @SpringBootApplication
+ @EnableWebSecurity
+ public static class TestApp {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestApp.class, args);
+ }
+
+ @GetMapping("/")
+ public String greeting() {
+ ElasticApm.currentTransaction().setUser("id", "email", "username", "domain");
+ return "Hello World";
+ }
+
+ @Bean
+ public SecurityFilterChain filterChain(HttpSecurity http) {
+ return http.authorizeHttpRequests(registry -> registry.anyRequest().authenticated())
+ .httpBasic(registry -> {})
+ .build();
+ }
+
+ @Bean
+ public UserDetailsService userDetailsService() {
+ return new InMemoryUserDetailsManager(User.withDefaultPasswordEncoder()
+ .username("username")
+ .password("password")
+ .roles("USER")
+ .build());
+ }
+
+ }
+}
diff --git a/integration-tests/spring-boot-4/spring-boot-4-base/src/test/resources/application.properties b/integration-tests/spring-boot-4/spring-boot-4-base/src/test/resources/application.properties
new file mode 100644
index 0000000000..d11e76254d
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-base/src/test/resources/application.properties
@@ -0,0 +1 @@
+spring.application.name=spring-boot-test
diff --git a/integration-tests/spring-boot-4/spring-boot-4-base/src/test/resources/static/script.js b/integration-tests/spring-boot-4/spring-boot-4-base/src/test/resources/static/script.js
new file mode 100644
index 0000000000..65091b2d6a
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-base/src/test/resources/static/script.js
@@ -0,0 +1 @@
+// empty test script
diff --git a/integration-tests/spring-boot-4/spring-boot-4-jetty/pom.xml b/integration-tests/spring-boot-4/spring-boot-4-jetty/pom.xml
new file mode 100644
index 0000000000..9405a7f83d
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-jetty/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ spring-boot-4
+ co.elastic.apm
+ 1.55.5-SNAPSHOT
+
+ 4.0.0
+
+ spring-boot-4-jetty
+
+ ${project.groupId}:${project.artifactId}
+
+
+ ${project.basedir}/../../..
+
+
+
+
+ ${project.groupId}
+ spring-boot-4-base
+ ${project.version}
+ test-jar
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
+
+
+
diff --git a/integration-tests/spring-boot-4/spring-boot-4-jetty/src/test/java/co/elastic/apm/spring/boot/SpringBoot4JettyIT.java b/integration-tests/spring-boot-4/spring-boot-4-jetty/src/test/java/co/elastic/apm/spring/boot/SpringBoot4JettyIT.java
new file mode 100644
index 0000000000..d0aecae1b2
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-jetty/src/test/java/co/elastic/apm/spring/boot/SpringBoot4JettyIT.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.spring.boot;
+
+public class SpringBoot4JettyIT extends AbstractSpringBootTest {
+
+ @Override
+ protected String getExpectedSpringVersionRegex() {
+ return "7\\.[0-9]+\\.[0-9]+";
+ }
+}
diff --git a/integration-tests/spring-boot-4/spring-boot-4-tomcat/pom.xml b/integration-tests/spring-boot-4/spring-boot-4-tomcat/pom.xml
new file mode 100644
index 0000000000..5aa6367322
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-tomcat/pom.xml
@@ -0,0 +1,28 @@
+
+
+
+ spring-boot-4
+ co.elastic.apm
+ 1.55.5-SNAPSHOT
+
+ 4.0.0
+
+ spring-boot-4-tomcat
+
+ ${project.groupId}:${project.artifactId}
+
+
+ ${project.basedir}/../../..
+
+
+
+
+ ${project.groupId}
+ spring-boot-4-base
+ ${project.version}
+ test-jar
+ test
+
+
+
+
diff --git a/integration-tests/spring-boot-4/spring-boot-4-tomcat/src/test/java/co/elastic/apm/spring/boot/SpringBoot4TomcatIT.java b/integration-tests/spring-boot-4/spring-boot-4-tomcat/src/test/java/co/elastic/apm/spring/boot/SpringBoot4TomcatIT.java
new file mode 100644
index 0000000000..3a7d0c362e
--- /dev/null
+++ b/integration-tests/spring-boot-4/spring-boot-4-tomcat/src/test/java/co/elastic/apm/spring/boot/SpringBoot4TomcatIT.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.spring.boot;
+
+public class SpringBoot4TomcatIT extends AbstractSpringBootTest {
+
+ @Override
+ protected String getExpectedSpringVersionRegex() {
+ return "7\\.[0-9]+\\.[0-9]+";
+ }
+}