diff --git a/.ci/load/Jenkinsfile b/.ci/load/Jenkinsfile
index 1f40d81292..8aa328a5fb 100644
--- a/.ci/load/Jenkinsfile
+++ b/.ci/load/Jenkinsfile
@@ -81,8 +81,8 @@ pipeline {
echo "Switch to requested agent version = ${params.apm_version}"
sh(script: "git checkout ${params.apm_version}")
- echo 'Building agent with Java 11'
- sh(script: "JAVA_HOME=${env.HUDSON_HOME}/.java/java11 ./mvnw --batch-mode clean package -DskipTests=true -Dmaven.javadoc.skip=true -Dmaven.sources.skip=true")
+ echo 'Building agent with Java 17'
+ sh(script: "JAVA_HOME=${env.HUDSON_HOME}/.java/jdk17 ./mvnw --batch-mode clean package -DskipTests=true -Dmaven.javadoc.skip=true -Dmaven.sources.skip=true")
// copy agent jar to a known filename that don't change with version
sh(script: "cp -v \$(find ./elastic-apm-agent/target -name '*.jar' | grep -v sources | grep -v original | grep -v javadoc) ${WORKSPACE}/elastic-apm-agent.jar")
diff --git a/.ci/packer_cache.sh b/.ci/packer_cache.sh
index b37b6f9b0b..5fe042b881 100755
--- a/.ci/packer_cache.sh
+++ b/.ci/packer_cache.sh
@@ -3,7 +3,7 @@ set +e
source /usr/local/bin/bash_standard_lib.sh
-retry 3 JAVA_HOME=$HOME/.java/java11 \
+retry 3 JAVA_HOME=$HOME/.java/jdk17 \
./mvnw clean package \
-q -B \
-DskipTests=true \
diff --git a/.ci/release/Jenkinsfile b/.ci/release/Jenkinsfile
index deb3259002..c2883860a4 100644
--- a/.ci/release/Jenkinsfile
+++ b/.ci/release/Jenkinsfile
@@ -11,7 +11,7 @@ pipeline {
NEXUS_SECRET = 'secret/apm-team/ci/nexus'
MAVEN_CONFIG = '-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dmaven.repo.local=.m2'
HOME = "${env.WORKSPACE}"
- JAVA_HOME = "${env.HUDSON_HOME}/.java/java10"
+ JAVA_HOME = "${env.HUDSON_HOME}/.java/jdk17"
PATH = "${env.JAVA_HOME}/bin:${env.PATH}"
SLACK_CHANNEL = '#apm-agent-java'
NOTIFY_TO = 'build-apm+apm-agent-java@elastic.co'
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index e500b6d54b..cfdf0c3313 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -35,6 +35,7 @@ endif::[]
* Add warning when agent is accidentally started on a JVM/JDK command-line tool - {pull}2924[#2924]
* Fix `NullPointerException` caused by the Elasticsearch REST client instrumentation when collecting dropped span metrics - {pull}2959[#2959]
* Fix SQS Instrumentation for Non-MessageReceive actions to avoid NoSuchElementException - {pull}2979[#2979]
+* Fix `java.lang.NoSuchMethodError` when using the agent with WebFlux and Spring 6.x/Spring Boot 3.x - {pull}2935[#2935]
===== Potentially breaking changes
diff --git a/Jenkinsfile b/Jenkinsfile
index c6093e3a67..93dd59b780 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -31,7 +31,7 @@ pipeline {
issueCommentTrigger("(${obltGitHubComments()}|^run (jdk compatibility|benchmark|integration|windows) tests)")
}
parameters {
- string(name: 'JAVA_VERSION', defaultValue: 'java11', description: 'Java version to build & test')
+ string(name: 'JAVA_VERSION', defaultValue: 'jdk17', description: 'Java version to build & test')
string(name: 'MAVEN_CONFIG', defaultValue: '-V -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=25', description: 'Additional maven options.')
// Note about GH checks and optional steps
@@ -362,21 +362,21 @@ pipeline {
}
}
}
- environment {
- PATH = "${env.JAVA_HOME}/bin:${env.PATH}"
- }
matrix {
agent { label 'linux && immutable' }
axes {
axis {
- // the list of support java versions can be found in the infra repo (ansible/roles/java/defaults/main.yml)
+ // the list of supported java versions can be found in the infra repo (ansible/roles/java/defaults/main.yml)
name 'JDK_VERSION'
- // 'openjdk18' disabled for now see https://github.com/elastic/apm-agent-java/issues/2328
- values 'openjdk17'
+ values 'java11', 'jdk19'
}
}
stages {
stage('JDK Unit Tests') {
+ environment {
+ JAVA_HOME = "${env.HUDSON_HOME}/.java/${env.JDK_VERSION}"
+ PATH = "${env.JAVA_HOME}/bin:${env.PATH}"
+ }
steps {
withGithubNotify(context: "${STAGE_NAME} ${JDK_VERSION}", tab: 'tests') {
deleteDir()
diff --git a/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java b/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java
index bf6c435f53..a1e7c7e66e 100644
--- a/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java
+++ b/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java
@@ -18,7 +18,7 @@
*/
package co.elastic.apm.attach;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
diff --git a/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java b/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java
index ac9a08fa21..6b4b2c0271 100644
--- a/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java
+++ b/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java
@@ -18,7 +18,7 @@
*/
package co.elastic.apm.attach;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import co.elastic.apm.attach.bouncycastle.BouncyCastleVerifier;
import org.junit.jupiter.api.Test;
diff --git a/apm-agent-common/src/main/java/co/elastic/apm/agent/util/Version.java b/apm-agent-common/src/main/java/co/elastic/apm/agent/common/util/Version.java
similarity index 98%
rename from apm-agent-common/src/main/java/co/elastic/apm/agent/util/Version.java
rename to apm-agent-common/src/main/java/co/elastic/apm/agent/common/util/Version.java
index 49436f1f9e..ac92153e2d 100644
--- a/apm-agent-common/src/main/java/co/elastic/apm/agent/util/Version.java
+++ b/apm-agent-common/src/main/java/co/elastic/apm/agent/common/util/Version.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package co.elastic.apm.agent.util;
+package co.elastic.apm.agent.common.util;
import java.util.Arrays;
import java.util.Objects;
diff --git a/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java b/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java
index 5c5e79b620..baf7011db9 100644
--- a/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java
+++ b/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java
@@ -18,6 +18,7 @@
*/
package co.elastic.apm.agent.util;
+import co.elastic.apm.agent.common.util.Version;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java
index 27b023cf17..113322d130 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java
@@ -23,7 +23,7 @@
import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent;
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
import co.elastic.apm.agent.util.ClassLoaderUtils;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.annotation.AnnotationSource;
import net.bytebuddy.description.method.MethodDescription;
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java
index 1ee37d0757..e40b247ed8 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java
@@ -21,7 +21,7 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.report.ssl.SslUtils;
import co.elastic.apm.agent.util.UrlConnectionUtils;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import co.elastic.apm.agent.util.VersionUtils;
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java
index f0f3213800..275ebd07d1 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java
@@ -19,7 +19,7 @@
package co.elastic.apm.agent.report;
import co.elastic.apm.agent.util.ExecutorUtils;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import com.dslplatform.json.DslJson;
import com.dslplatform.json.JsonReader;
import com.dslplatform.json.Nullable;
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java
new file mode 100644
index 0000000000..2451029042
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java
@@ -0,0 +1,98 @@
+/*
+ * 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.springwebflux;
+
+import javax.annotation.Nullable;
+
+/**
+ * This class must be located within the same package as used by the WebFlux instrumentation, otherwise it will be loaded by the
+ * agent class loader, rather than the WebFlux plugin class loader. If the {@link ISpringWebVersionUtils} implementations are loaded
+ * anywhere other than the WebFlux plugin class loader, their loading will cause a {@link LinkageError}.
+ */
+public class SpringWebVersionUtils {
+
+ private static final String SPRING_WEB_5_UTILS_CLASS_NAME = "co.elastic.apm.agent.springwebflux.SpringWeb5Utils";
+ private static final String SPRING_WEB_6_UTILS_CLASS_NAME = "co.elastic.apm.agent.springwebflux.SpringWeb6Utils";
+
+ @Nullable
+ private static ISpringWebVersionUtils instance = null;
+
+ private static volatile boolean initialized = false;
+
+ private static synchronized void initialize() throws Exception {
+ if (initialized) {
+ return;
+ }
+ try {
+ // check if using spring 6.0.0 or higher
+ Class.forName("org.springframework.http.HttpStatusCode");
+ try {
+ // loading class by name so to avoid linkage attempt when spring-web 6 is unavailable
+ instance = (ISpringWebVersionUtils) Class.forName(SPRING_WEB_6_UTILS_CLASS_NAME).getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException("Spring-web 6.x+ identified, but failed to load related utility class", e);
+ }
+ } catch (ClassNotFoundException ignored) {
+ // assuming spring-web < 6.x
+ try {
+ // loading class by name so to avoid linkage attempt on spring-web 6, where the getStatusCode API has changed
+ instance = (ISpringWebVersionUtils) Class.forName(SPRING_WEB_5_UTILS_CLASS_NAME).getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException("Spring-web < 6.x identified, but failed to load related utility class", e);
+ }
+ } finally {
+ initialized = true;
+ }
+ }
+
+ @Nullable
+ private static ISpringWebVersionUtils getImplementation() throws Exception {
+ if (!initialized) {
+ initialize();
+ }
+ return instance;
+ }
+
+ /**
+ * A utility method to get the status code of a {@code org.springframework.http.server.reactive.ServerHttpResponse} from any version
+ * of Spring framework.
+ *
+ * @param response must be of type {@code org.springframework.http.server.reactive.ServerHttpResponse}, otherwise an Exception is
+ * expected
+ * @return the status code of the provided response
+ */
+ public static int getStatusCode(Object response) throws Exception {
+ ISpringWebVersionUtils implementation = getImplementation();
+ if (implementation != null) {
+ return implementation.getStatusCode(response);
+ }
+ return 200;
+ }
+
+ public interface ISpringWebVersionUtils {
+ /**
+ * A utility method to get the status code of a {@code org.springframework.http.server.reactive.ServerHttpResponse} from any version
+ * of Spring framework.
+ *
+ * @param response must be of type {@code org.springframework.http.server.reactive.ServerHttpResponse}
+ * @return the corresponding status code
+ */
+ int getStatusCode(Object response);
+ }
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/package-info.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/package-info.java
new file mode 100644
index 0000000000..86685638b7
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * This package is common as it needs to be used by Spring WebFlux instrumentations from different modules (compiled by different Java
+ * versions). It must have the same package name as used by the WebFlux instrumentation, as it is expected to be loaded by the same
+ * plugin class loader that loads the WebFlux advice and helper classes.
+ */
+package co.elastic.apm.agent.springwebflux;
+
diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java
index 5af6fc9d5e..f8f68d9b75 100644
--- a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java
+++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java
@@ -25,7 +25,7 @@
import co.elastic.apm.agent.objectpool.TestObjectPoolFactory;
import co.elastic.apm.agent.report.ApmServerClient;
import co.elastic.apm.agent.report.Reporter;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import org.stagemonitor.configuration.ConfigurationRegistry;
import static org.mockito.ArgumentMatchers.any;
diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java
index abee0dc648..c268a10c5f 100644
--- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java
+++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java
@@ -29,7 +29,7 @@
import co.elastic.apm.agent.impl.transaction.Transaction;
import co.elastic.apm.agent.objectpool.TestObjectPoolFactory;
import co.elastic.apm.agent.objectpool.impl.BookkeeperObjectPool;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import org.assertj.core.util.Lists;
diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java
index 9056e66241..51e5ef978e 100644
--- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java
+++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java
@@ -18,7 +18,7 @@
*/
package co.elastic.apm.agent.report;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import org.junit.jupiter.api.Test;
import java.io.IOException;
diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java
index dcb2285dd1..9da16bce93 100644
--- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java
+++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java
@@ -31,7 +31,7 @@
import co.elastic.apm.agent.impl.transaction.Transaction;
import co.elastic.apm.agent.report.processor.ProcessorEventHandler;
import co.elastic.apm.agent.report.serialize.DslJsonSerializer;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import com.dslplatform.json.DslJson;
import com.dslplatform.json.JsonWriter;
import com.fasterxml.jackson.databind.JsonNode;
diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java
index ba7ca695bc..a4dfd53dda 100644
--- a/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java
+++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java
@@ -19,12 +19,13 @@
package co.elastic.apm.agent.util;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.io.TempDir;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessController;
@@ -37,6 +38,7 @@
import static co.elastic.apm.agent.testutils.assertions.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+@EnabledForJreRange(max = JRE.JAVA_17, disabledReason = "SecurityManager is not supported anymore")
class PrivilegedActionUtilsTest {
private static final AtomicBoolean enabled = new AtomicBoolean(false);
diff --git a/apm-agent-java-17/apm-agent-spring-6/pom.xml b/apm-agent-java-17/apm-agent-spring-6/pom.xml
new file mode 100755
index 0000000000..14f40051e2
--- /dev/null
+++ b/apm-agent-java-17/apm-agent-spring-6/pom.xml
@@ -0,0 +1,38 @@
+
+
+ 4.0.0
+
+
+ co.elastic.apm
+ apm-agent-java-17
+ 1.35.1-SNAPSHOT
+
+
+ apm-agent-spring-6
+ ${project.groupId}:${project.artifactId}
+
+
+ ${project.basedir}/../..
+
+
+
+
+ ${project.groupId}
+ apm-agent-core
+ ${project.version}
+
+
+ org.springframework
+ spring-web
+ 6.0.1
+ provided
+
+
+
+ io.projectreactor
+ reactor-core
+ 3.2.0.RELEASE
+ test
+
+
+
diff --git a/apm-agent-java-17/apm-agent-spring-6/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java b/apm-agent-java-17/apm-agent-spring-6/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java
new file mode 100644
index 0000000000..86ed8487d6
--- /dev/null
+++ b/apm-agent-java-17/apm-agent-spring-6/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java
@@ -0,0 +1,34 @@
+/*
+ * 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.springwebflux;
+
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+
+/**
+ * This class is compiled with spring-web 6.x, as it relies on {@link HttpStatusCode} and an API that was introduced in 6.0.0.
+ * Therefore, it MUST only be loaded through its class name through {@link SpringWebVersionUtils}.
+ */
+public class SpringWeb6Utils implements SpringWebVersionUtils.ISpringWebVersionUtils {
+ @Override
+ public int getStatusCode(Object response) {
+ HttpStatusCode statusCode = ((ServerHttpResponse) response).getStatusCode();
+ return statusCode != null ? statusCode.value() : 200;
+ }
+}
diff --git a/apm-agent-java-17/apm-agent-spring-6/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java b/apm-agent-java-17/apm-agent-spring-6/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java
new file mode 100644
index 0000000000..ab6bff651f
--- /dev/null
+++ b/apm-agent-java-17/apm-agent-spring-6/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.springwebflux;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatusCode;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class SpringWeb6UtilsTest {
+
+ @Test
+ void testGetStatusCode() throws Exception {
+ ServerHttpResponse mockResponse = mock(ServerHttpResponse.class);
+ doReturn(HttpStatusCode.valueOf(222)).when(mockResponse).getStatusCode();
+ assertThat(SpringWebVersionUtils.getStatusCode(mockResponse)).isEqualTo(222);
+ }
+
+ @Test
+ void testWrongResponseType() {
+ assertThatThrownBy(() -> SpringWebVersionUtils.getStatusCode(new Object())).isInstanceOf(ClassCastException.class);
+ }
+}
diff --git a/apm-agent-java-17/pom.xml b/apm-agent-java-17/pom.xml
new file mode 100644
index 0000000000..ceaac15c5f
--- /dev/null
+++ b/apm-agent-java-17/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+
+ apm-agent-parent
+ co.elastic.apm
+ 1.35.1-SNAPSHOT
+
+
+ apm-agent-java-17
+ pom
+ ${project.groupId}:${project.artifactId}
+
+
+ apm-agent-spring-6
+
+
+
+
+ ${project.basedir}/..
+
+
+ true
+
+
+
diff --git a/apm-agent-plugins/apm-dubbo-plugin/pom.xml b/apm-agent-plugins/apm-dubbo-plugin/pom.xml
index 8600daf4aa..7baa24ffa3 100644
--- a/apm-agent-plugins/apm-dubbo-plugin/pom.xml
+++ b/apm-agent-plugins/apm-dubbo-plugin/pom.xml
@@ -55,4 +55,17 @@
test
+
+
+
+
+ maven-surefire-plugin
+
+
+ --add-opens java.base/java.lang=ALL-UNNAMED
+
+
+
+
+
diff --git a/apm-agent-plugins/apm-javalin-plugin/pom.xml b/apm-agent-plugins/apm-javalin-plugin/pom.xml
index 6c566756fb..1226310543 100644
--- a/apm-agent-plugins/apm-javalin-plugin/pom.xml
+++ b/apm-agent-plugins/apm-javalin-plugin/pom.xml
@@ -57,4 +57,16 @@
+
+
+
+ maven-surefire-plugin
+
+
+ --enable-preview
+
+
+
+
+
diff --git a/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java b/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java
index 88a4c60e88..0dca50f024 100644
--- a/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java
+++ b/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java
@@ -20,7 +20,7 @@
import co.elastic.apm.agent.testutils.JUnit4TestClassWithDependencyRunner;
import co.elastic.apm.agent.mongodb.AbstractMongoClientInstrumentationIT;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java
index 9ad9677662..45a3463e70 100644
--- a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java
+++ b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java
@@ -19,7 +19,7 @@
package co.elastic.apm.agent.okhttp;
import co.elastic.apm.agent.httpclient.AbstractHttpClientInstrumentationTest;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import co.elastic.apm.agent.util.VersionUtils;
import okhttp3.Call;
import okhttp3.Callback;
diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java
index ab4281c688..3608f1f142 100644
--- a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java
+++ b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java
@@ -19,7 +19,7 @@
package co.elastic.apm.agent.okhttp;
import co.elastic.apm.agent.httpclient.AbstractHttpClientInstrumentationTest;
-import co.elastic.apm.agent.util.Version;
+import co.elastic.apm.agent.common.util.Version;
import co.elastic.apm.agent.util.VersionUtils;
import okhttp3.OkHttpClient;
import okhttp3.Request;
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml
index 8091850618..dbe54a411b 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml
@@ -92,7 +92,6 @@
test
-
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java
new file mode 100644
index 0000000000..4acda6f375
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java
@@ -0,0 +1,35 @@
+/*
+ * 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.springwebflux;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+
+/**
+ * This class is compiled with spring-web 5.x, relying on the {@link ServerHttpResponse#getStatusCode()}, which changed in 6.0.0.
+ * Therefore, it MUST only be loaded through its class name through {@link SpringWebVersionUtils}.
+ */
+public class SpringWeb5Utils implements SpringWebVersionUtils.ISpringWebVersionUtils {
+
+ @Override
+ public int getStatusCode(Object response) {
+ HttpStatus statusCode = ((ServerHttpResponse) response).getStatusCode();
+ return statusCode != null ? statusCode.value() : 200;
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java
index 1c0e31f72b..aaeba84ea1 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java
@@ -31,13 +31,13 @@
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent;
import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
+import co.elastic.apm.agent.util.LoggerUtils;
import co.elastic.apm.agent.util.PotentiallyMultiValuedMap;
import co.elastic.apm.agent.util.PrivilegedActionUtils;
import co.elastic.apm.agent.util.TransactionNameUtils;
import org.reactivestreams.Publisher;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
@@ -64,6 +64,7 @@
public class WebfluxHelper {
private static final Logger log = LoggerFactory.getLogger(WebfluxHelper.class);
+ private static final Logger oneTimeResponseCodeErrorLogger = LoggerUtils.logOnce(log);
public static final String TRANSACTION_ATTRIBUTE = WebfluxHelper.class.getName() + ".transaction";
private static final String SUBSCRIBER_ATTRIBUTE = WebfluxHelper.class.getName() + ".wrapped_subscriber";
@@ -270,8 +271,12 @@ private static void fillRequest(Transaction transaction, ServerWebExchange excha
private static void fillResponse(Transaction transaction, ServerWebExchange exchange) {
ServerHttpResponse serverResponse = exchange.getResponse();
- HttpStatus statusCode = serverResponse.getStatusCode();
- int status = statusCode != null ? statusCode.value() : 200;
+ int status = 0;
+ try {
+ status = SpringWebVersionUtils.getStatusCode(serverResponse);
+ } catch (Exception e) {
+ oneTimeResponseCodeErrorLogger.error("Failed to get response code", e);
+ }
transaction.withResultIfUnset(ResultUtil.getResultByHttpStatus(status));
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java
new file mode 100644
index 0000000000..f0fb189b42
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.springwebflux;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+class SpringWeb5UtilsTest {
+
+ @Test
+ void testGetStatusCode() throws Exception {
+ ServerHttpResponse mockResponse = mock(ServerHttpResponse.class);
+ doReturn(HttpStatus.IM_USED).when(mockResponse).getStatusCode();
+ assertThat(SpringWebVersionUtils.getStatusCode(mockResponse)).isEqualTo(226);
+ }
+
+ @Test
+ void testWrongResponseType() {
+ assertThatThrownBy(() -> SpringWebVersionUtils.getStatusCode(new Object())).isInstanceOf(ClassCastException.class);
+ }
+}
diff --git a/apm-agent/pom.xml b/apm-agent/pom.xml
index 8e036b4b09..1fc768b25f 100644
--- a/apm-agent/pom.xml
+++ b/apm-agent/pom.xml
@@ -20,7 +20,6 @@
${project.basedir}/..
-
@@ -402,6 +401,22 @@
+
+
+ non-java-11
+
+ !11
+
+
+
+ ${project.groupId}
+ apm-agent-spring-6
+ ${project.version}
+
+
+
+
+
diff --git a/integration-tests/external-plugin-test/external-plugin/pom.xml b/integration-tests/external-plugin-test/external-plugin/pom.xml
index 212c395074..688221c67b 100644
--- a/integration-tests/external-plugin-test/external-plugin/pom.xml
+++ b/integration-tests/external-plugin-test/external-plugin/pom.xml
@@ -87,7 +87,7 @@
-->
maven-shade-plugin
- 3.2.4
+ 3.3.0
package
diff --git a/pom.xml b/pom.xml
index a2889ff1d6..c108ed2e3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -221,6 +221,17 @@
+
+
+ non-java-11
+
+ !11
+
+
+ apm-agent-java-17
+
+
no-errorprone
@@ -520,7 +531,7 @@
org.jacoco
jacoco-maven-plugin
- 0.8.7
+ 0.8.8
org.codehaus.mojo
@@ -539,7 +550,7 @@
maven-shade-plugin
- 3.2.4
+ 3.3.0
maven-clean-plugin