diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index cb242b3257..a8f0a5f382 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -35,6 +35,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
===== Features
* Added support for OpenTelemetry annotations - `WithSpan` and `SpanAttribute` - {pull}3406[#3406]
* Only automatically apply redacted exceptions for Corretto JVM 17-20. Outside that, user should use capture_exception_details=false to workaround the JVM race-condition bug if it gets triggered: {pull}3438[#3438]
+* Added support for Spring 6.1 / Spring-Boot 3.2 - {pull}3440[#3440]
[[release-notes-1.x]]
=== Java Agent version 1.x
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/pom.xml
index 5471808cd5..c9f3638827 100755
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/pom.xml
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/pom.xml
@@ -38,6 +38,11 @@
apm-httpclient-core
${project.version}
+
+ ${project.groupId}
+ apm-spring-webflux-common
+ ${project.version}
+
org.springframework
spring-web
@@ -60,6 +65,11 @@
spring-webflux
provided
+
+ org.springframework
+ spring-context
+ provided
+
${project.groupId}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebClientSubscriber.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebClientSubscriber.java
index e86d0c750e..ed000ca409 100755
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebClientSubscriber.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebClientSubscriber.java
@@ -23,6 +23,7 @@
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.Tracer;
import co.elastic.apm.agent.tracer.reference.ReferenceCountedMap;
+import co.elastic.apm.agent.webfluxcommon.SpringWebVersionUtils;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -74,14 +75,15 @@ public void onNext(T t) {
try {
if (span != null && t instanceof ClientResponse) {
ClientResponse clientResponse = (ClientResponse) t;
- int statusCode = clientResponse.rawStatusCode();
+ int statusCode = SpringWebVersionUtils.getClientStatusCode(clientResponse);
span.withOutcome(ResultUtil.getOutcomeByHttpClientStatus(statusCode));
span.getContext().getHttp().withStatusCode(statusCode);
}
subscriber.onNext(t);
} catch (Throwable e) {
thrown = e;
- throw e;
+ // Since spring 6.1 we can't throw checked exceptions here anymore, so we have to use this trick
+ throwException(e);
} finally {
doExit(hasActivated, "onNext", span);
discardIf(thrown != null);
@@ -193,4 +195,17 @@ private void cancelSpan() {
}
}
+ @SuppressWarnings("unchecked")
+ private static void throwException(Throwable exception, Object dummy) throws T {
+ throw (T) exception;
+ }
+
+ /**
+ * Utility method for throwing a checked exception like an unchecked one.
+ */
+ private static void throwException(Throwable exception) {
+ throwException(exception, null);
+ }
+
+
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebclientPluginRootPackageCustomizer.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebclientPluginRootPackageCustomizer.java
new file mode 100644
index 0000000000..9b2837d1b8
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/java/co/elastic/apm/agent/springwebclient/WebclientPluginRootPackageCustomizer.java
@@ -0,0 +1,31 @@
+/*
+ * 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.springwebclient;
+
+import co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+public class WebclientPluginRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer {
+ @Override
+ public Collection pluginClassLoaderRootPackages() {
+ return Arrays.asList(getPluginPackage(), "co.elastic.apm.agent.webfluxcommon");
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer
new file mode 100644
index 0000000000..98bd77b6fc
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer
@@ -0,0 +1 @@
+co.elastic.apm.agent.springwebclient.WebclientPluginRootPackageCustomizer
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/pom.xml
new file mode 100644
index 0000000000..1b9121cc87
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ co.elastic.apm
+ apm-spring-webflux
+ 1.44.1-SNAPSHOT
+
+
+ apm-spring-webflux-common-spring5
+ ${project.groupId}:${project.artifactId}
+
+
+
+ ${project.basedir}/../../..
+ true
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${version.spring-boot-2}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework
+ spring-webflux
+ provided
+
+
+
+
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWeb5Utils.java
similarity index 82%
rename from apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java
rename to apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWeb5Utils.java
index a649c79b7f..39c303104a 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWeb5Utils.java
@@ -16,10 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package co.elastic.apm.agent.springwebflux;
+package co.elastic.apm.agent.webfluxcommon;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.reactive.function.client.ClientResponse;
/**
* This class is compiled with spring-web 5.x, relying on the {@link ServerHttpResponse#getStatusCode()}, which changed in 6.0.0.
@@ -29,8 +30,13 @@
public class SpringWeb5Utils implements SpringWebVersionUtils.ISpringWebVersionUtils {
@Override
- public int getStatusCode(Object response) {
+ public int getServerStatusCode(Object response) {
HttpStatus statusCode = ((ServerHttpResponse) response).getStatusCode();
return statusCode != null ? statusCode.value() : 200;
}
+
+ @Override
+ public int getClientStatusCode(Object response) {
+ return ((ClientResponse) response).rawStatusCode();
+ }
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWebVersionUtils.java
similarity index 55%
rename from apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java
rename to apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWebVersionUtils.java
index 2451029042..2a47e026b7 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWebVersionUtils.java
@@ -16,7 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package co.elastic.apm.agent.springwebflux;
+package co.elastic.apm.agent.webfluxcommon;
+
+import org.springframework.web.reactive.function.server.ServerResponse;
import javax.annotation.Nullable;
@@ -27,8 +29,8 @@
*/
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";
+ private static final String SPRING_WEB_5_UTILS_CLASS_NAME = "co.elastic.apm.agent.webfluxcommon.SpringWeb5Utils";
+ private static final String SPRING_WEB_6_UTILS_CLASS_NAME = "co.elastic.apm.agent.webfluxcommon.SpringWeb6Utils";
@Nullable
private static ISpringWebVersionUtils instance = null;
@@ -40,21 +42,16 @@ private static synchronized void initialize() throws Exception {
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);
+ Class> statusCodeType = ServerResponse.class.getMethod("statusCode").getReturnType();
+ switch (statusCodeType.getName()) {
+ case "org.springframework.http.HttpStatusCode": // spring 6.0.0 or higher
+ instance = (ISpringWebVersionUtils) Class.forName(SPRING_WEB_6_UTILS_CLASS_NAME).getDeclaredConstructor().newInstance();
+ break;
+ case "org.springframework.http.HttpStatus": // spring < 6
+ instance = (ISpringWebVersionUtils) Class.forName(SPRING_WEB_5_UTILS_CLASS_NAME).getDeclaredConstructor().newInstance();
+ break;
+ default:
+ throw new IllegalStateException("Unexpected type: "+statusCodeType.getName());
}
} finally {
initialized = true;
@@ -77,10 +74,27 @@ private static ISpringWebVersionUtils getImplementation() throws Exception {
* expected
* @return the status code of the provided response
*/
- public static int getStatusCode(Object response) throws Exception {
+ public static int getServerStatusCode(Object response) throws Exception {
+ ISpringWebVersionUtils implementation = getImplementation();
+ if (implementation != null) {
+ return implementation.getServerStatusCode(response);
+ }
+ return 200;
+ }
+
+
+ /**
+ * A utility method to get the status code of a {@code org.springframework.web.reactive.function.client.ClientResponse} from any version
+ * of Spring framework.
+ *
+ * @param response must be of type {@code org.springframework.web.reactive.function.client.ClientResponse}, otherwise an Exception is
+ * expected
+ * @return the status code of the provided response
+ */
+ public static int getClientStatusCode(Object response) throws Exception {
ISpringWebVersionUtils implementation = getImplementation();
if (implementation != null) {
- return implementation.getStatusCode(response);
+ return implementation.getClientStatusCode(response);
}
return 200;
}
@@ -93,6 +107,16 @@ public interface ISpringWebVersionUtils {
* @param response must be of type {@code org.springframework.http.server.reactive.ServerHttpResponse}
* @return the corresponding status code
*/
- int getStatusCode(Object response);
+ int getServerStatusCode(Object response);
+
+ /**
+ * A utility method to get the status code of a {@code org.springframework.web.reactive.function.client.ClientResponse} from any version
+ * of Spring framework.
+ *
+ * @param response must be of type {@code org.springframework.web.reactive.function.client.ClientResponse}
+ * @return the corresponding status code
+ */
+ int getClientStatusCode(Object response);
+
}
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/test/java/co/elastic/apm/agent/webfluxcommon/SpringWeb5UtilsTest.java
similarity index 67%
rename from apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java
rename to apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/test/java/co/elastic/apm/agent/webfluxcommon/SpringWeb5UtilsTest.java
index f0fb189b42..5d0e524d2d 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common-spring5/src/test/java/co/elastic/apm/agent/webfluxcommon/SpringWeb5UtilsTest.java
@@ -16,11 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-package co.elastic.apm.agent.springwebflux;
+package co.elastic.apm.agent.webfluxcommon;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.reactive.function.client.ClientResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -30,14 +31,21 @@
class SpringWeb5UtilsTest {
@Test
- void testGetStatusCode() throws Exception {
+ void testGetServerStatusCode() throws Exception {
ServerHttpResponse mockResponse = mock(ServerHttpResponse.class);
doReturn(HttpStatus.IM_USED).when(mockResponse).getStatusCode();
- assertThat(SpringWebVersionUtils.getStatusCode(mockResponse)).isEqualTo(226);
+ assertThat(SpringWebVersionUtils.getServerStatusCode(mockResponse)).isEqualTo(226);
}
@Test
void testWrongResponseType() {
- assertThatThrownBy(() -> SpringWebVersionUtils.getStatusCode(new Object())).isInstanceOf(ClassCastException.class);
+ assertThatThrownBy(() -> SpringWebVersionUtils.getServerStatusCode(new Object())).isInstanceOf(ClassCastException.class);
+ }
+
+ @Test
+ void testGetClientStatusCode() throws Exception {
+ ClientResponse mockResponse = mock(ClientResponse.class);
+ doReturn(226).when(mockResponse).rawStatusCode();
+ assertThat(SpringWebVersionUtils.getClientStatusCode(mockResponse)).isEqualTo(226);
}
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/pom.xml
new file mode 100644
index 0000000000..3ee1c8ab58
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+ co.elastic.apm
+ apm-spring-webflux
+ 1.44.1-SNAPSHOT
+
+
+ apm-spring-webflux-common
+ ${project.groupId}:${project.artifactId}
+
+
+
+ ${project.basedir}/../../..
+ true
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${version.spring-boot-3}
+ pom
+ import
+
+
+
+
+
+
+ ${project.groupId}
+ apm-spring-webflux-common-spring5
+ ${project.version}
+
+
+ org.springframework
+ spring-webflux
+ provided
+
+
+
+
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWeb6Utils.java
similarity index 80%
rename from apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java
rename to apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWeb6Utils.java
index 3029363ef2..5792bfe450 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/src/main/java/co/elastic/apm/agent/webfluxcommon/SpringWeb6Utils.java
@@ -16,10 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-package co.elastic.apm.agent.springwebflux;
+package co.elastic.apm.agent.webfluxcommon;
+
import org.springframework.http.HttpStatusCode;
import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.reactive.function.client.ClientResponse;
/**
* 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.
@@ -28,8 +30,14 @@
@SuppressWarnings("unused") //Created via reflection
public class SpringWeb6Utils implements SpringWebVersionUtils.ISpringWebVersionUtils {
@Override
- public int getStatusCode(Object response) {
+ public int getServerStatusCode(Object response) {
HttpStatusCode statusCode = ((ServerHttpResponse) response).getStatusCode();
return statusCode != null ? statusCode.value() : 200;
}
+
+ @Override
+ public int getClientStatusCode(Object response) {
+ HttpStatusCode statusCode = ((ClientResponse) response).statusCode();
+ return statusCode.value();
+ }
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/src/test/java/co/elastic/apm/agent/webfluxcommon/SpringWeb6UtilsTest.java
similarity index 67%
rename from apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java
rename to apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/src/test/java/co/elastic/apm/agent/webfluxcommon/SpringWeb6UtilsTest.java
index dfc6060b4e..71c910202c 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-common/src/test/java/co/elastic/apm/agent/webfluxcommon/SpringWeb6UtilsTest.java
@@ -16,17 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package co.elastic.apm.agent.springwebflux;
+package co.elastic.apm.agent.webfluxcommon;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.mockito.Mockito;
+import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.reactive.function.client.ClientResponse;
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;
@EnabledForJreRange(min = JRE.JAVA_17)
public class SpringWeb6UtilsTest {
@@ -35,11 +39,18 @@ public class SpringWeb6UtilsTest {
void testGetStatusCode() throws Exception {
ServerHttpResponse mockResponse = Mockito.mock(ServerHttpResponse.class);
Mockito.doReturn(HttpStatusCode.valueOf(222)).when(mockResponse).getStatusCode();
- assertThat(SpringWebVersionUtils.getStatusCode(mockResponse)).isEqualTo(222);
+ assertThat(SpringWebVersionUtils.getServerStatusCode(mockResponse)).isEqualTo(222);
}
@Test
void testWrongResponseType() {
- assertThatThrownBy(() -> SpringWebVersionUtils.getStatusCode(new Object())).isInstanceOf(ClassCastException.class);
+ assertThatThrownBy(() -> SpringWebVersionUtils.getServerStatusCode(new Object())).isInstanceOf(ClassCastException.class);
+ }
+
+ @Test
+ void testGetClientStatusCode() throws Exception {
+ ClientResponse mockResponse = mock(ClientResponse.class);
+ doReturn(HttpStatusCode.valueOf(222)).when(mockResponse).statusCode();
+ assertThat(SpringWebVersionUtils.getClientStatusCode(mockResponse)).isEqualTo(222);
}
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/pom.xml
index 13205c66ee..9473db30f0 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/pom.xml
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/pom.xml
@@ -14,7 +14,6 @@
${project.basedir}/../../..
-
true
@@ -36,6 +35,11 @@
apm-httpserver-core
${project.version}
+
+ ${project.groupId}
+ apm-spring-webflux-common
+ ${project.version}
+
org.springframework
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java
index 39fa391b35..01a68fd110 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java
@@ -35,9 +35,11 @@
import co.elastic.apm.agent.sdk.internal.util.LoggerUtils;
import co.elastic.apm.agent.sdk.internal.util.PrivilegedActionUtils;
import co.elastic.apm.agent.tracer.util.TransactionNameUtils;
+import co.elastic.apm.agent.webfluxcommon.SpringWebVersionUtils;
import org.reactivestreams.Publisher;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.MultiValueMap;
@@ -197,7 +199,11 @@ public static void setTransactionName(@Nullable Transaction> transaction, Serv
path = exchange.getRequest().getPath().value();
}
}
- String method = exchange.getRequest().getMethodValue();
+ String method = "unknown";
+ HttpMethod methodObj = exchange.getRequest().getMethod();
+ if(methodObj != null) {
+ method = methodObj.name();
+ }
StringBuilder transactionName = transaction.getAndOverrideName(namePriority, false);
if (path != null) {
@@ -252,7 +258,11 @@ private static void fillRequest(Transaction> transaction, ServerWebExchange ex
ServerHttpRequest serverRequest = exchange.getRequest();
Request request = transaction.getContext().getRequest();
- request.withMethod(serverRequest.getMethodValue());
+
+ HttpMethod method = serverRequest.getMethod();
+ if (method != null) {
+ request.withMethod(method.name());
+ }
InetSocketAddress remoteAddress = serverRequest.getRemoteAddress();
if (remoteAddress != null && remoteAddress.getAddress() != null) {
@@ -273,7 +283,7 @@ private static void fillResponse(Transaction> transaction, ServerWebExchange e
ServerHttpResponse serverResponse = exchange.getResponse();
int status = 0;
try {
- status = SpringWebVersionUtils.getStatusCode(serverResponse);
+ status = SpringWebVersionUtils.getServerStatusCode(serverResponse);
} catch (Exception e) {
oneTimeResponseCodeErrorLogger.error("Failed to get response code", e);
}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxPluginRootPackageCustomizer.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxPluginRootPackageCustomizer.java
new file mode 100644
index 0000000000..799da0d45e
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxPluginRootPackageCustomizer.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 co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class WebfluxPluginRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer {
+ @Override
+ public Collection pluginClassLoaderRootPackages() {
+ return Arrays.asList(getPluginPackage(), "co.elastic.apm.agent.webfluxcommon");
+ }
+}
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer
new file mode 100644
index 0000000000..ecece5539c
--- /dev/null
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-spring5/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.internal.PluginClassLoaderRootPackageCustomizer
@@ -0,0 +1 @@
+co.elastic.apm.agent.springwebflux.WebfluxPluginRootPackageCustomizer
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingAnnotated.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingAnnotated.java
index 378358409d..3ce7f49bc5 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingAnnotated.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingAnnotated.java
@@ -141,7 +141,7 @@ public Mono patchMapping() {
@RequestMapping(path = "/hello-mapping", method = {RequestMethod.HEAD, RequestMethod.OPTIONS, RequestMethod.TRACE})
public Mono otherMapping(ServerHttpRequest request) {
- return greetingHandler.helloMessage(request.getMethodValue());
+ return greetingHandler.helloMessage(request.getMethod().name());
}
@GetMapping("/with-parameters/{id}")
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingWebClient.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingWebClient.java
index 49778ccbe8..6b6b428d33 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingWebClient.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/GreetingWebClient.java
@@ -222,7 +222,7 @@ private WebClient.ResponseSpec request(String method, Function
.headers(httpHeaders -> httpHeaders.addAll(headers))
.cookies(httpCookies -> httpCookies.addAll(cookies))
.retrieve()
- .onRawStatus(status -> status != expectedStatus, r -> Mono.error(new IllegalStateException(String.format("unexpected response status %d", r.rawStatusCode()))));
+ .onRawStatus(status -> status != expectedStatus, r -> Mono.error(new IllegalStateException("unexpected response status")));
}
@Override
diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/WebFluxConfig.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/WebFluxConfig.java
index dcf8a485b3..a68b77687e 100644
--- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/WebFluxConfig.java
+++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/src/main/java/co/elastic/apm/agent/springwebflux/testapp/WebFluxConfig.java
@@ -105,7 +105,22 @@ ReactorResourceFactory reactorServerResourceFactory() {
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(@Qualifier("reactorServerResourceFactory") ReactorResourceFactory resourceFactory,
ObjectProvider routes, ObjectProvider serverCustomizers) {
NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
- serverFactory.setResourceFactory(resourceFactory);
+
+ //The argument type of setResourceFactory was changed in spring 6.1 from
+ // org.springframework.http.client.reactive.ReactorResourceFactory to
+ // org.springframework.http.client.ReactorResourceFactory
+ // we therefor use reflection to call the correct one
+ try {
+ try {
+ Class> argType = Class.forName("org.springframework.http.client.reactive.ReactorResourceFactory");
+ serverFactory.getClass().getMethod("setResourceFactory", argType).invoke(serverFactory, resourceFactory);
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ Class> argType = Class.forName("org.springframework.http.client.ReactorResourceFactory");
+ serverFactory.getClass().getMethod("setResourceFactory", argType).invoke(serverFactory, resourceFactory);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
routes.orderedStream().forEach(serverFactory::addRouteProviders);
serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return serverFactory;
diff --git a/apm-agent-plugins/apm-spring-webflux/pom.xml b/apm-agent-plugins/apm-spring-webflux/pom.xml
index a8c08366da..5e1b1e2b6a 100644
--- a/apm-agent-plugins/apm-spring-webflux/pom.xml
+++ b/apm-agent-plugins/apm-spring-webflux/pom.xml
@@ -18,7 +18,7 @@
2.7.16
- 3.1.5
+ 3.2.0
@@ -26,6 +26,8 @@
apm-spring-webclient-plugin
apm-spring-webflux-testapp
apm-spring-webflux-spring5
+ apm-spring-webflux-common
+ apm-spring-webflux-common-spring5
diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc
index c5adbdf713..910b421953 100644
--- a/docs/supported-technologies.asciidoc
+++ b/docs/supported-technologies.asciidoc
@@ -112,7 +112,7 @@ the JVM arguments, or setting `ELASTIC_APM_DISABLE_BOOTSTRAP_CHECKS=true` for th
|Spring Webflux
|5.2.3+
|Creates transactions for incoming HTTP requests, supports annotated and functional endpoints.
-|1.24.0 (experimental), 1.34.0 (GA)
+|1.24.0 (experimental), 1.34.0 (GA), 6.1+ since 1.45.0
|JavaServer Faces
|2.2.x, 2.3.x, 3.0.x