Support Spring Framework 7 (Spring Boot 4) and Spring Cloud Gateway 5.x (#826)
* Support Spring Framework 7 (Spring Boot 4) and Spring Cloud Gateway 5.x
Spring Framework 7.0 dropped the MultiValueMap contract from HttpHeaders
(spring-projects/spring-framework gh-33913), removing List get(Object). The
webflux-6.x plugin was compiled against that overload, so every request through
DispatcherHandler#handle threw NoSuchMethodError before the entry span was
created: the service kept serving traffic but produced no traces at all.
spring-cloud-gateway-4.x shades a byte-identical copy of that interceptor and
its witness class still resolves on Spring Cloud Gateway 5.x, so gateway users
hit it through the documented plugin as well.
A bytecode sweep of every built plugin jar, resolving all org/springframework
constant-pool refs against spring-web/webflux 5.3.39, 6.2.0 and 7.0.5, found
only two Spring 7 removals reaching the agent, across three call sites.
- spring-webflux-6.x / -5.x: read the header via HttpHeaders#getFirst(String),
which is declared on HttpHeaders itself in Spring 5, 6 and 7 with the same
descriptor. Note HttpHeaders#get(String) does NOT work: compiled against
Spring 6 javac still emits the (Object) descriptor. getOrEmpty is not
cross-version safe either, its descriptor changes between 6 and 7.
- mvc-annotation-commons RequestUtil: use getValuesAsList(String). getFirst is
wrong here because this site consumes every value. Note it additionally
splits comma-delimited values.
- mvc-annotation-commons AbstractMethodInterceptor: drop the premature reactive
status read. ServerHttpResponse#getRawStatusCode() was removed in Spring 7,
and it ran before the response was written so it normally read null anyway.
The version-specific InvokeInterceptor already sets http.status_code in
Mono#doFinally, once the status is actually known.
Add Spring generation witnesses to both webflux plugins. The only guard was
WitnessMethod(Mono, contextWrite), which bounds Reactor and not Spring, which
is why the plugin attached to Spring 7 and then failed on every request rather
than cleanly not attaching. On Reactor 3.4 both Mono#subscriberContext and
Mono#contextWrite exist, so the 5.x and 6.x plugins would both match
DispatcherHandler#handle on Spring Boot 2.4-2.7.
Split the WebFlux application toolkit. Reactor removed Signal#getContext() in
3.5.0 and ContextView does not exist before 3.4.0, so no single artifact can
serve both; continueTracing has been broken on all of Spring Boot 3 and 4 since
Nov 2022. apm-toolkit-webflux becomes apm-toolkit-webflux-5.x (package
...toolkit.webflux.v5, unchanged behaviour) and a new apm-toolkit-webflux-6.x
(package ...toolkit.webflux.v6) compiles against Reactor 3.5 and uses
Signal#getContextView(). Compiling forward is deliberate: the module rotted
silently for three years because it compiled against the oldest Reactor, so
javac never flagged the removal. Distinct packages make picking the wrong
artifact a compile error rather than a runtime NoSuchMethodError. The
activation keeps matching the legacy un-versioned names, so applications that
never upgrade their toolkit still work against a newer agent.
No plugin, framework version or Reactor version loses support.
Add webflux-6.x-7.x-scenario (Spring Boot 3.0 -> 4.1, one version per minor)
and gateway-5.x-scenario (Spring Cloud Gateway 5.0.3 on Spring Boot 4). Both
drive a full round-trip so the cross-process ref is asserted, which is the only
thing that proves the sw8 header is read back correctly.
Closes apache/skywalking#14047
diff --git a/.github/workflows/plugins-jdk17-test.1.yaml b/.github/workflows/plugins-jdk17-test.1.yaml
index 7738b85..824239b 100644
--- a/.github/workflows/plugins-jdk17-test.1.yaml
+++ b/.github/workflows/plugins-jdk17-test.1.yaml
@@ -85,6 +85,7 @@
- caffeine-3.x-scenario
- lettuce-webflux-6x-scenario
- elasticsearch-java-9.x-scenario
+ - webflux-6.x-7.x-scenario
steps:
- uses: actions/checkout@v2
with:
diff --git a/.github/workflows/plugins-jdk21-test.0.yaml b/.github/workflows/plugins-jdk21-test.0.yaml
index 9e68055..51ed4fe 100644
--- a/.github/workflows/plugins-jdk21-test.0.yaml
+++ b/.github/workflows/plugins-jdk21-test.0.yaml
@@ -72,6 +72,7 @@
case:
- spring-6.x-scenario
- jdk-virtual-thread-executor-scenario
+ - gateway-5.x-scenario
steps:
- uses: actions/checkout@v2
with:
diff --git a/CHANGES.md b/CHANGES.md
index 138d0ab..88d88ee 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,6 +6,22 @@
------------------
* Add Spring LDAP 3.3.x-4.x plugin.
+* Fix `NoSuchMethodError: org.apache.skywalking.apm.plugin.spring.webflux.v6.DispatcherHandlerHandleMethodInterceptor`
+ on Spring Framework 7 (Spring Boot 4). `HttpHeaders` no longer implements `MultiValueMap`, so
+ `List get(Object)` was removed; the entry span was never created and the service produced no
+ traces. Affects `spring-webflux-6.x` and, through its shaded copy, `spring-cloud-gateway-4.x`
+ (apache/skywalking#14047).
+* Extend `spring-webflux-6.x`, `spring-webflux-6.x-webclient`, `springmvc-annotation-6.x`,
+ `spring-resttemplate-6.x` and `spring-cloud-gateway-4.x` plugins to support Spring Framework 7,
+ Spring Boot 4 and Spring Cloud Gateway 5.x.
+* Add Spring version witnesses to the `spring-webflux-5.x` and `spring-webflux-6.x` plugins, so they
+ no longer both match `DispatcherHandler#handle` on Reactor 3.4 (Spring Boot 2.4-2.7).
+* **Breaking:** rename `apm-toolkit-webflux` to `apm-toolkit-webflux-5.x` and move its package to
+ `org.apache.skywalking.apm.toolkit.webflux.v5`; add `apm-toolkit-webflux-6.x`
+ (`org.apache.skywalking.apm.toolkit.webflux.v6`) for Reactor 3.5+ / Spring Boot 3.x and 4.x, where
+ `WebFluxSkyWalkingOperators#continueTracing` previously threw `NoSuchMethodError` because Reactor
+ removed `Signal#getContext()` in 3.5.0. Existing `apm-toolkit-webflux` jars (9.7.0 and earlier)
+ remain instrumented by the agent, so upgrading the agent alone does not force a change.
* Fix the Log4j2 plugin descriptor (`Log4j2Plugins.dat`) missing from the `apm-toolkit-log4j-2.x` jar since 9.5.0, which broke `%traceId` and `%sw_ctx` resolution in Log4j2 `PatternLayout` (apache/skywalking#14006).
All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/263?closed=1)
diff --git a/apm-application-toolkit/apm-toolkit-webflux/pom.xml b/apm-application-toolkit/apm-toolkit-webflux-5.x/pom.xml
similarity index 96%
rename from apm-application-toolkit/apm-toolkit-webflux/pom.xml
rename to apm-application-toolkit/apm-toolkit-webflux-5.x/pom.xml
index fb4778f..86a4609 100644
--- a/apm-application-toolkit/apm-toolkit-webflux/pom.xml
+++ b/apm-application-toolkit/apm-toolkit-webflux-5.x/pom.xml
@@ -24,7 +24,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
- <artifactId>apm-toolkit-webflux</artifactId>
+ <artifactId>apm-toolkit-webflux-5.x</artifactId>
<properties>
<spring-webflux.version>5.1.0.RELEASE</spring-webflux.version>
diff --git a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java b/apm-application-toolkit/apm-toolkit-webflux-5.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v5/WebFluxSkyWalkingOperators.java
similarity index 98%
rename from apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java
rename to apm-application-toolkit/apm-toolkit-webflux-5.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v5/WebFluxSkyWalkingOperators.java
index 70ebacc..bc165d5 100644
--- a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java
+++ b/apm-application-toolkit/apm-toolkit-webflux-5.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v5/WebFluxSkyWalkingOperators.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.toolkit.webflux;
+package org.apache.skywalking.apm.toolkit.webflux.v5;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Signal;
diff --git a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingTraceContext.java b/apm-application-toolkit/apm-toolkit-webflux-5.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v5/WebFluxSkyWalkingTraceContext.java
similarity index 97%
rename from apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingTraceContext.java
rename to apm-application-toolkit/apm-toolkit-webflux-5.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v5/WebFluxSkyWalkingTraceContext.java
index 82198ed..7e4de8c 100644
--- a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingTraceContext.java
+++ b/apm-application-toolkit/apm-toolkit-webflux-5.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v5/WebFluxSkyWalkingTraceContext.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.toolkit.webflux;
+package org.apache.skywalking.apm.toolkit.webflux.v5;
import org.springframework.web.server.ServerWebExchange;
diff --git a/apm-application-toolkit/apm-toolkit-webflux-6.x/pom.xml b/apm-application-toolkit/apm-toolkit-webflux-6.x/pom.xml
new file mode 100644
index 0000000..bf1c90e
--- /dev/null
+++ b/apm-application-toolkit/apm-toolkit-webflux-6.x/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>apm-application-toolkit</artifactId>
+ <groupId>org.apache.skywalking</groupId>
+ <version>9.8.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>apm-toolkit-webflux-6.x</artifactId>
+
+ <properties>
+ <spring-webflux.version>6.0.0</spring-webflux.version>
+ <!--
+ Compile against a Reactor that has already REMOVED Signal#getContext (removed in 3.5.0).
+ This is deliberate: the 5.x toolkit rotted silently for three years because it compiled
+ against the oldest Reactor (3.2 via spring-webflux 5.1), so javac never flagged the
+ removal. Pinning forward makes any future Reactor removal a compile error instead of a
+ runtime NoSuchMethodError. Reactor 3.5.x is still Java 8 bytecode, so the module keeps
+ the project-wide maven.compiler.release=8.
+ The emitted refs (Signal#getContextView, Context#of(ContextView)) exist from 3.4.0, so
+ the jar links on Reactor 3.4 -> 3.8.
+ -->
+ <reactor-core.version>3.5.0</reactor-core.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-core</artifactId>
+ <version>${reactor-core.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webflux</artifactId>
+ <version>${spring-webflux.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java b/apm-application-toolkit/apm-toolkit-webflux-6.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingOperators.java
similarity index 95%
copy from apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java
copy to apm-application-toolkit/apm-toolkit-webflux-6.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingOperators.java
index 70ebacc..93f8525 100644
--- a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java
+++ b/apm-application-toolkit/apm-toolkit-webflux-6.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingOperators.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.toolkit.webflux;
+package org.apache.skywalking.apm.toolkit.webflux.v6;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Signal;
@@ -63,7 +63,7 @@
if (signalType != signal.getType()) {
return;
}
- continueTracing(signal.getContext(), () -> consumer.accept(signal));
+ continueTracing(Context.of(signal.getContextView()), () -> consumer.accept(signal));
};
}
@@ -75,7 +75,7 @@
*/
public static Consumer<Signal> continueTracing(Runnable runnable) {
return signal -> {
- Context context = signal.getContext();
+ Context context = Context.of(signal.getContextView());
continueTracing(context, runnable);
};
}
diff --git a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingTraceContext.java b/apm-application-toolkit/apm-toolkit-webflux-6.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingTraceContext.java
similarity index 97%
copy from apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingTraceContext.java
copy to apm-application-toolkit/apm-toolkit-webflux-6.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingTraceContext.java
index 82198ed..23b6597 100644
--- a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingTraceContext.java
+++ b/apm-application-toolkit/apm-toolkit-webflux-6.x/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingTraceContext.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.toolkit.webflux;
+package org.apache.skywalking.apm.toolkit.webflux.v6;
import org.springframework.web.server.ServerWebExchange;
diff --git a/apm-application-toolkit/pom.xml b/apm-application-toolkit/pom.xml
index 4f2c0e3..c7c2df3 100644
--- a/apm-application-toolkit/pom.xml
+++ b/apm-application-toolkit/pom.xml
@@ -41,6 +41,7 @@
<module>apm-toolkit-micrometer-registry</module>
<module>apm-toolkit-micrometer-1.10</module>
<module>apm-toolkit-kafka</module>
- <module>apm-toolkit-webflux</module>
+ <module>apm-toolkit-webflux-5.x</module>
+ <module>apm-toolkit-webflux-6.x</module>
</modules>
</project>
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java
index f6cc016..2b7ea9c 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestUtil.java
@@ -101,7 +101,12 @@
}
public static Enumeration<String> getHeaders(final ServerHttpRequest request, final String headerName) {
- List<String> values = request.getHeaders().get(headerName);
+ // Use getValuesAsList(String) rather than get(Object): Spring Framework 7 dropped the
+ // MultiValueMap contract from HttpHeaders, removing List get(Object). getValuesAsList(String)
+ // is declared on HttpHeaders itself in Spring 5, 6 and 7 with an identical descriptor.
+ // Note it additionally splits comma-delimited values, which is the more correct reading
+ // for header collection.
+ List<String> values = request.getHeaders().getValuesAsList(headerName);
if (values == null) {
return Collections.enumeration(Collections.emptyList());
}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java
index 8f70a71..9477030 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java
@@ -188,9 +188,6 @@
if (httpServletResponse != null) {
statusCode = httpServletResponse.getStatus();
} else if (ServerHttpResponse.class.isAssignableFrom(response.getClass())) {
- if (HttpResponseWrappers.servletStatusSupported()) {
- statusCode = ((ServerHttpResponse) response).getRawStatusCode();
- }
Object context = runtimeContext.get(REACTIVE_ASYNC_SPAN_IN_RUNTIME_CONTEXT);
if (context != null) {
((AbstractSpan[]) context)[0] = span.prepareForAsync();
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsActivation.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsActivation.java
index 09d9b81..4703686 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsActivation.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsActivation.java
@@ -21,28 +21,42 @@
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
-import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
-import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.ClassStaticMethodsEnhancePluginDefineV2;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
-import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch.byMultiClassMatch;
/**
*/
-public class WebFluxSkyWalkingOperatorsActivation extends ClassStaticMethodsEnhancePluginDefine {
+public class WebFluxSkyWalkingOperatorsActivation extends ClassStaticMethodsEnhancePluginDefineV2 {
public static final String INTERCEPT_CLASS =
"org.apache.skywalking.apm.toolkit.activation.webflux.WebFluxSkyWalkingOperatorsInterceptor";
+ /**
+ * The un-versioned name shipped by apm-toolkit-webflux 9.7.0 and earlier. Still matched so that
+ * applications which never upgrade their toolkit keep working against a newer agent.
+ */
public static final String ENHANCE_CLASS =
"org.apache.skywalking.apm.toolkit.webflux.WebFluxSkyWalkingOperators";
+ /**
+ * apm-toolkit-webflux-5.x, for Reactor 3.1-3.4 (Spring Boot 2.x).
+ */
+ public static final String ENHANCE_CLASS_V5 =
+ "org.apache.skywalking.apm.toolkit.webflux.v5.WebFluxSkyWalkingOperators";
+ /**
+ * apm-toolkit-webflux-6.x, for Reactor 3.5+ (Spring Boot 3.x and 4.x).
+ */
+ public static final String ENHANCE_CLASS_V6 =
+ "org.apache.skywalking.apm.toolkit.webflux.v6.WebFluxSkyWalkingOperators";
public static final String ENHANCE_METHOD = "continueTracing";
@Override
protected ClassMatch enhanceClass() {
- return byName(ENHANCE_CLASS);
+ return byMultiClassMatch(ENHANCE_CLASS, ENHANCE_CLASS_V5, ENHANCE_CLASS_V6);
}
@Override
@@ -51,9 +65,9 @@
}
@Override
- public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
- return new StaticMethodsInterceptPoint[] {
- new StaticMethodsInterceptPoint() {
+ public StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points() {
+ return new StaticMethodsInterceptV2Point[] {
+ new StaticMethodsInterceptV2Point() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named(ENHANCE_METHOD).and(takesArguments(2))
@@ -62,7 +76,7 @@
}
@Override
- public String getMethodsInterceptor() {
+ public String getMethodsInterceptorV2() {
return INTERCEPT_CLASS;
}
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsInterceptor.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsInterceptor.java
index 8ae39df..21e2403 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsInterceptor.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingOperatorsInterceptor.java
@@ -21,7 +21,8 @@
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
-import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
import org.springframework.web.server.ServerWebExchange;
import reactor.util.context.Context;
@@ -29,39 +30,57 @@
import java.lang.reflect.Method;
/**
+ * Continues the trace context inside a Reactor operator.
+ * <p>
+ * A span is only created when a context snapshot is actually available - an empty Reactor context, or a
+ * ServerWebExchange that was never enhanced because the optional WebFlux plugin is not installed, both
+ * legitimately yield nothing to continue. Because of that, this interceptor must remember whether it
+ * created a span for THIS invocation: {@link MethodInvocationContext} carries that decision from
+ * {@link #beforeMethod} to {@link #afterMethod}, so the interceptor never stops or logs against a span
+ * that belongs to somebody else, and nesting is handled correctly.
*/
-public class WebFluxSkyWalkingOperatorsInterceptor extends WebFluxSkyWalkingStaticMethodsAroundInterceptor {
-
+public class WebFluxSkyWalkingOperatorsInterceptor implements StaticMethodsAroundInterceptorV2 {
+
+ private static final Object SPAN_CREATED = new Object();
+
@Override
public void beforeMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
- MethodInterceptResult result) {
- // get ContextSnapshot from reactor context, the snapshot is set to reactor context by any other plugin
+ MethodInvocationContext context) {
+ // get ContextSnapshot from reactor context, the snapshot is set to reactor context by any other plugin
// such as DispatcherHandlerHandleMethodInterceptor in spring-webflux-5.x-plugin
+ ContextSnapshot snapshot = null;
if (parameterTypes[0] == Context.class) {
- ((Context) allArguments[0]).getOrEmpty("SKYWALKING_CONTEXT_SNAPSHOT")
- .ifPresent(ctx -> {
- ContextManager.createLocalSpan("WebFluxOperators/onNext").setComponent(ComponentsDefine.SPRING_WEBFLUX);
- ContextManager.continued((ContextSnapshot) ctx);
- });
+ snapshot = (ContextSnapshot) ((Context) allArguments[0])
+ .getOrEmpty("SKYWALKING_CONTEXT_SNAPSHOT")
+ .orElse(null);
} else if (parameterTypes[0] == ServerWebExchange.class) {
- EnhancedInstance instance = getInstance(allArguments[0]);
+ EnhancedInstance instance = WebFluxSkyWalkingStaticMethodsAroundInterceptor.getInstance(allArguments[0]);
if (instance != null && instance.getSkyWalkingDynamicField() != null) {
- ContextManager.createLocalSpan("WebFluxOperators/onNext").setComponent(ComponentsDefine.SPRING_WEBFLUX);
- ContextManager.continued((ContextSnapshot) instance.getSkyWalkingDynamicField());
+ snapshot = (ContextSnapshot) instance.getSkyWalkingDynamicField();
}
}
+
+ if (snapshot != null) {
+ ContextManager.createLocalSpan("WebFluxOperators/onNext").setComponent(ComponentsDefine.SPRING_WEBFLUX);
+ ContextManager.continued(snapshot);
+ context.setContext(SPAN_CREATED);
+ }
}
@Override
- public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Object ret) {
- ContextManager.stopSpan();
+ public Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Object ret,
+ MethodInvocationContext context) {
+ if (context.getContext() != null) {
+ ContextManager.stopSpan();
+ }
return ret;
}
@Override
public void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
- Throwable t) {
- ContextManager.activeSpan().log(t);
+ Throwable t, MethodInvocationContext context) {
+ if (context.getContext() != null) {
+ ContextManager.activeSpan().log(t);
+ }
}
-
}
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingStaticMethodsAroundInterceptor.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingStaticMethodsAroundInterceptor.java
index 138d944..f783a47 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingStaticMethodsAroundInterceptor.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingStaticMethodsAroundInterceptor.java
@@ -29,7 +29,7 @@
public abstract class WebFluxSkyWalkingStaticMethodsAroundInterceptor implements StaticMethodsAroundInterceptor {
- protected EnhancedInstance getInstance(Object o) {
+ protected static EnhancedInstance getInstance(Object o) {
EnhancedInstance instance = null;
if (o instanceof DefaultServerWebExchange && o instanceof EnhancedInstance) {
instance = (EnhancedInstance) o;
@@ -40,7 +40,7 @@
return instance;
}
- protected ContextSnapshot getContextSnapshot(Object o) {
+ protected static ContextSnapshot getContextSnapshot(Object o) {
return Optional.ofNullable(getInstance(o))
.map(EnhancedInstance::getSkyWalkingDynamicField)
.filter(ContextSnapshot.class::isInstance)
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingTraceContextActivation.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingTraceContextActivation.java
index 77d522f..fe11b53 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingTraceContextActivation.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-webflux-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/webflux/WebFluxSkyWalkingTraceContextActivation.java
@@ -23,7 +23,7 @@
import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassStaticMethodsEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
-import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch;
import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -32,7 +32,19 @@
public static final String TRACE_ID_INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.webflux.WebFluxSkyWalkingTraceIDInterceptor";
public static final String SEGMENT_ID_INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.webflux.WebFluxSkyWalkingSegmentIDInterceptor";
public static final String SPAN_ID_INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.webflux.WebFluxSkyWalkingSpanIDInterceptor";
+ /**
+ * The un-versioned name shipped by apm-toolkit-webflux 9.7.0 and earlier. Still matched so that
+ * applications which never upgrade their toolkit keep working against a newer agent.
+ */
public static final String ENHANCE_CLASS = "org.apache.skywalking.apm.toolkit.webflux.WebFluxSkyWalkingTraceContext";
+ /**
+ * apm-toolkit-webflux-5.x, for Reactor 3.1-3.4 (Spring Boot 2.x).
+ */
+ public static final String ENHANCE_CLASS_V5 = "org.apache.skywalking.apm.toolkit.webflux.v5.WebFluxSkyWalkingTraceContext";
+ /**
+ * apm-toolkit-webflux-6.x, for Reactor 3.5+ (Spring Boot 3.x and 4.x).
+ */
+ public static final String ENHANCE_CLASS_V6 = "org.apache.skywalking.apm.toolkit.webflux.v6.WebFluxSkyWalkingTraceContext";
public static final String ENHANCE_TRACE_ID_METHOD = "traceId";
public static final String ENHANCE_SEGMENT_ID_METHOD = "segmentId";
public static final String ENHANCE_SPAN_ID_METHOD = "spanId";
@@ -46,7 +58,7 @@
*/
@Override
protected ClassMatch enhanceClass() {
- return NameMatch.byName(ENHANCE_CLASS);
+ return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS, ENHANCE_CLASS_V5, ENHANCE_CLASS_V6);
}
/**
diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/DispatcherHandlerHandleMethodInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/DispatcherHandlerHandleMethodInterceptor.java
index c86c6ea..1bba71a 100644
--- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/DispatcherHandlerHandleMethodInterceptor.java
+++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/DispatcherHandlerHandleMethodInterceptor.java
@@ -40,7 +40,6 @@
import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
-import java.util.List;
public class DispatcherHandlerHandleMethodInterceptor implements InstanceMethodsAroundInterceptor {
@@ -56,9 +55,12 @@
HttpHeaders headers = exchange.getRequest().getHeaders();
while (next.hasNext()) {
next = next.next();
- List<String> header = headers.get(next.getHeadKey());
- if (header != null && header.size() > 0) {
- next.setHeadValue(header.get(0));
+ // Use getFirst(String) rather than get(Object): Spring Framework 7 dropped the
+ // MultiValueMap contract from HttpHeaders, removing List get(Object). getFirst(String)
+ // is declared on HttpHeaders itself in Spring 5, 6 and 7 with an identical descriptor.
+ String headerValue = headers.getFirst(next.getHeadKey());
+ if (headerValue != null) {
+ next.setHeadValue(headerValue);
}
}
diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/define/DispatcherHandlerInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/define/DispatcherHandlerInstrumentation.java
index 1c3085c..6b858ee 100644
--- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/define/DispatcherHandlerInstrumentation.java
+++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v5/define/DispatcherHandlerInstrumentation.java
@@ -34,6 +34,12 @@
public class DispatcherHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
private static final String WEBFLUX_CONTEXT_WRITE_CLASS = "reactor.core.publisher.Mono";
private static final String WEBFLUX_CONTEXT_WRITE_METHOD = "subscriberContext";
+ /**
+ * Removed in Spring Framework 6.0, so this pins the 5.x plugin to Spring 5. Mono#subscriberContext
+ * alone is not enough: on Reactor 3.4 both it and Mono#contextWrite exist, so without this the
+ * 5.x and 6.x plugins would both match DispatcherHandler#handle on Spring Boot 2.4-2.7.
+ */
+ private static final String WEBFLUX_5_WITNESS_CLASS = "org.springframework.web.reactive.resource.GzipResourceResolver";
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
@@ -72,4 +78,9 @@
return Collections.singletonList(
new WitnessMethod(WEBFLUX_CONTEXT_WRITE_CLASS, named(WEBFLUX_CONTEXT_WRITE_METHOD)));
}
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {WEBFLUX_5_WITNESS_CLASS};
+ }
}
diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/DispatcherHandlerHandleMethodInterceptor.java b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/DispatcherHandlerHandleMethodInterceptor.java
index 0f2e2f2..31914cc 100644
--- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/DispatcherHandlerHandleMethodInterceptor.java
+++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/DispatcherHandlerHandleMethodInterceptor.java
@@ -40,7 +40,6 @@
import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
-import java.util.List;
public class DispatcherHandlerHandleMethodInterceptor implements InstanceMethodsAroundInterceptor {
@@ -56,9 +55,12 @@
HttpHeaders headers = exchange.getRequest().getHeaders();
while (next.hasNext()) {
next = next.next();
- List<String> header = headers.get(next.getHeadKey());
- if (header != null && header.size() > 0) {
- next.setHeadValue(header.get(0));
+ // Use getFirst(String) rather than get(Object): Spring Framework 7 dropped the
+ // MultiValueMap contract from HttpHeaders, removing List get(Object). getFirst(String)
+ // is declared on HttpHeaders itself in Spring 5, 6 and 7 with an identical descriptor.
+ String headerValue = headers.getFirst(next.getHeadKey());
+ if (headerValue != null) {
+ next.setHeadValue(headerValue);
}
}
diff --git a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/define/DispatcherHandlerInstrumentation.java b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/define/DispatcherHandlerInstrumentation.java
index 1460077..6d4991d 100644
--- a/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/define/DispatcherHandlerInstrumentation.java
+++ b/apm-sniffer/optional-plugins/optional-spring-plugins/spring-webflux-6.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/webflux/v6/define/DispatcherHandlerInstrumentation.java
@@ -37,6 +37,12 @@
private static final String WEBFLUX_CONTEXT_WRITE_METHOD = "contextWrite";
private static final String WEBFLUX_METHOD_INTERCEPTOR = "org.apache.skywalking.apm.plugin.spring.webflux.v6.DispatcherHandlerHandleMethodInterceptor";
private static final String WEBFLUX_ENHANCE_CLASS = "org.springframework.web.reactive.DispatcherHandler";
+ /**
+ * Added in Spring Framework 6.0 and still present in 7.x, absent in 5.x. The Mono#contextWrite
+ * witness below only bounds Reactor, not Spring, so this is what keeps the 6.x plugin off a
+ * Spring 5 application when both webflux plugins are dropped into /plugins.
+ */
+ private static final String WEBFLUX_6_WITNESS_CLASS = "org.springframework.web.reactive.DispatchExceptionHandler";
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
@@ -78,4 +84,9 @@
return Collections.singletonList(
new WitnessMethod(WEBFLUX_CONTEXT_WRITE_CLASS, named(WEBFLUX_CONTEXT_WRITE_METHOD)));
}
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {WEBFLUX_6_WITNESS_CLASS};
+ }
}
diff --git a/docs/en/setup/service-agent/java-agent/Application-toolkit-webflux.md b/docs/en/setup/service-agent/java-agent/Application-toolkit-webflux.md
index 0fa738c..8f305ca 100644
--- a/docs/en/setup/service-agent/java-agent/Application-toolkit-webflux.md
+++ b/docs/en/setup/service-agent/java-agent/Application-toolkit-webflux.md
@@ -2,20 +2,75 @@
These APIs provide advanced features to enhance interaction capabilities in Webflux cases.
-Add the toolkit to your project dependency, through Maven or Gradle
+## Choose the right toolkit
+
+The toolkit is split by Reactor generation, because `reactor.core.publisher.Signal#getContext()` was
+removed in Reactor 3.5.0 and its replacement, `Signal#getContextView()`, does not exist before
+Reactor 3.4.0. No single artifact can serve both.
+
+| Artifact | Java package | Reactor | Spring Boot |
+|---|---|---|---|
+| `apm-toolkit-webflux-5.x` | `org.apache.skywalking.apm.toolkit.webflux.v5` | 3.1.3 -> 3.4 | 2.x |
+| `apm-toolkit-webflux-6.x` | `org.apache.skywalking.apm.toolkit.webflux.v6` | 3.5 -> 3.8 | 3.x and 4.x |
+
+The two artifacts expose exactly the same API, so only the dependency coordinate and the import
+change. Add **one** of them — never both.
+
+For Spring Boot 2.x (Reactor 3.1.3 - 3.4):
```xml
<dependency>
<groupId>org.apache.skywalking</groupId>
- <artifactId>apm-toolkit-webflux</artifactId>
+ <artifactId>apm-toolkit-webflux-5.x</artifactId>
<version>${skywalking.version}</version>
</dependency>
```
+```java
+import org.apache.skywalking.apm.toolkit.webflux.v5.WebFluxSkyWalkingOperators;
+import org.apache.skywalking.apm.toolkit.webflux.v5.WebFluxSkyWalkingTraceContext;
+```
+
+For Spring Boot 3.x and 4.x (Reactor 3.5+):
+```xml
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>apm-toolkit-webflux-6.x</artifactId>
+ <version>${skywalking.version}</version>
+ </dependency>
+```
+```java
+import org.apache.skywalking.apm.toolkit.webflux.v6.WebFluxSkyWalkingOperators;
+import org.apache.skywalking.apm.toolkit.webflux.v6.WebFluxSkyWalkingTraceContext;
+```
+
+### Migrating from `apm-toolkit-webflux`
+
+Before 9.8.0 there was a single un-versioned `apm-toolkit-webflux` artifact, in package
+`org.apache.skywalking.apm.toolkit.webflux`. It is superseded by `apm-toolkit-webflux-5.x`, and its
+`continueTracing` overloads that read the Reactor `Signal` context never worked on Reactor 3.5+
+(Spring Boot 3.0 and later).
+
+To migrate, change the artifactId and the import: pick `-5.x` to stay on Spring Boot 2.x, or `-6.x`
+if you are on Spring Boot 3.x/4.x. Getting the migration itself wrong is caught at build time — a
+stale coordinate fails to resolve, and a stale import fails to compile.
+
+**Upgrade the agent first, or together with the toolkit.** Compatibility is only backward, not
+forward:
+
+| Toolkit | Agent | Result |
+|---|---|---|
+| old `apm-toolkit-webflux` (<= 9.7.0) | 9.8.0+ | works — the agent still matches the un-versioned classes |
+| `-5.x` / `-6.x` (9.8.0+) | <= 9.7.0 | **silently untraced** — the older agent does not know the `.v5` / `.v6` classes, so it never instruments them |
+
+The second row compiles and runs perfectly normally; the only symptom is that the toolkit calls
+stop producing spans. So do not roll out a 9.8.0+ toolkit against a 9.7.0 or older agent.
The following scenarios are supported for tracing assistance.
### Continue Tracing from Client
The `WebFluxSkyWalkingOperators#continueTracing` provides manual tracing continuous capabilities to adopt native Webflux APIs
+With `apm-toolkit-webflux-5.x` (Reactor 3.1.3 - 3.4). `Mono#subscriberContext` was removed in
+Reactor 3.5, so this form does not compile on Spring Boot 3.x/4.x:
```java
@GetMapping("/testcase/annotation/mono/onnext")
public Mono<String> monoOnNext(@RequestBody(required = false) String body) {
@@ -27,6 +82,7 @@
}
```
+The `ServerWebExchange` overload takes no Reactor context, so it is identical on both generations:
```java
@GetMapping("/login/userFunctions")
public Mono<Response<FunctionInfoResult>> functionInfo(ServerWebExchange exchange, @RequestParam String userId) {
@@ -41,6 +97,8 @@
}
```
+With `apm-toolkit-webflux-6.x` (Reactor 3.5+). `Mono#deferContextual` and `Context#of(ContextView)`
+were added in Reactor 3.4, so this form requires Reactor 3.4 or later:
```java
Mono.just("key").subscribeOn(Schedulers.boundedElastic())
.doOnEach(WebFluxSkyWalkingOperators.continueTracing(SignalType.ON_NEXT, () -> log.info("test log with tid")))
diff --git a/docs/en/setup/service-agent/java-agent/Optional-plugins.md b/docs/en/setup/service-agent/java-agent/Optional-plugins.md
index b8ab352..528c1b2 100644
--- a/docs/en/setup/service-agent/java-agent/Optional-plugins.md
+++ b/docs/en/setup/service-agent/java-agent/Optional-plugins.md
@@ -13,11 +13,12 @@
* Plugin of Gson serialization lib in optional plugin folder.
* Plugin of Zookeeper 3.4.x in optional plugin folder. The reason of being optional plugin is, many business irrelevant traces are generated, which cause extra payload to agents and backends. At the same time, those traces may be just heartbeat(s).
* [Customize enhance](Customize-enhance-trace.md) Trace methods based on description files, rather than write plugin or change source codes.
-* Plugin of [Spring Cloud Gateway 2.x and 3.x and 4.x](agent-optional-plugins/spring-gateway.md) in optional plugin folder. Please only activate this plugin when you install agent in Spring Gateway.
+* Plugin of [Spring Cloud Gateway 2.x, 3.x, 4.x and 5.x](agent-optional-plugins/spring-gateway.md) in optional plugin folder. Please only activate this plugin when you install agent in Spring Gateway.
* Plugin of Spring Transaction in optional plugin folder. The reason of being optional plugin is, many local span are generated, which also spend more CPU, memory and network.
* [Plugin of Kotlin coroutine](agent-optional-plugins/Kotlin-Coroutine-plugin.md) provides the tracing across coroutines automatically. As it will add local spans to all across routines scenarios, Please assess the performance impact.
* Plugin of quartz-scheduler-2.x in the optional plugin folder. The reason for being an optional plugin is, many task scheduling systems are based on quartz-scheduler, this will cause duplicate tracing and link different sub-tasks as they share the same quartz level trigger, such as ElasticJob.
-* Plugin of spring-webflux-5.x in the optional plugin folder. Please only activate this plugin when you use webflux alone as a web container. If you are using SpringMVC 5 or Spring Gateway, you don't need this plugin.
+* Plugin of spring-webflux-5.x in the optional plugin folder, for Spring Framework 5.x. Please only activate this plugin when you use webflux alone as a web container. If you are using SpringMVC 5 or Spring Gateway, you do not need this plugin.
+* Plugin of spring-webflux-6.x in the optional plugin folder, for Spring Framework 6.x and 7.x (Spring Boot 3.x and 4.x). Please only activate this plugin when you use webflux alone as a web container. If you are using SpringMVC 6 or Spring Gateway, you do not need this plugin.
* Plugin of mybatis-3.x in optional plugin folder. The reason of being optional plugin is, many local span are generated, which also spend more CPU, memory and network.
* Plugin of sentinel-1.x in the optional plugin folder. The reason for being an optional plugin is, the sentinel plugin generates a large number of local spans, which have a potential performance impact.
* Plugin of ehcache-2.x in the optional plugin folder. The reason for being an optional plugin is, this plugin enhanced cache framework, generates large number of local spans, which have a potential performance impact.
@@ -26,7 +27,7 @@
* Plugin of jackson serialization lib in optional plugin folder.
* Plugin of Apache ShenYu(incubating) Gateway 2.4.x in optional plugin folder. Please only activate this plugin when you install agent in Apache ShenYu Gateway.
* Plugin of sampler plugin with CPU policy in the optional plugin folder. Please only activate this plugin when you need to disable trace collecting when the agent process CPU usage is too high(over threshold).
-* Plugin for Spring 6.x and RestTemplate 6.x are in the optional plugin folder. Spring 6 requires Java 17 but SkyWalking is still compatible with Java 8. So, we put it in the optional plugin folder.
+* Plugin for Spring 6.x/7.x and RestTemplate 6.x/7.x are in the optional plugin folder. Spring 6 and 7 require Java 17 but SkyWalking is still compatible with Java 8. So, we put them in the optional plugin folder.
* Plugin of nacos-client 2.x lib in optional plugin folder. The reason is many business irrelevant traces are generated, which cause extra payload to agents and backends, also spend more CPU, memory and network.
* Plugin of netty-http 4.1.x lib in optional plugin folder. The reason is some frameworks use Netty HTTP as kernel, which could double the unnecessary spans and create incorrect RPC relative metrics.
diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md
index d686c4f..6e29b2f 100644
--- a/docs/en/setup/service-agent/java-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/java-agent/Supported-list.md
@@ -7,15 +7,15 @@
* [Tomcat](https://github.com/apache/tomcat) 8
* [Tomcat](https://github.com/apache/tomcat) 9
* [Tomcat](https://github.com/apache/tomcat) 10
- * [Spring Boot](https://github.com/spring-projects/spring-boot) Web 4.x
+ * [Spring Web](https://github.com/spring-projects/spring-framework) 4.x
* Spring MVC 3.x, 4.x 5.x with servlet 3.x
- * Spring MVC 6.x (Optional²)
+ * Spring MVC 6.x -> 7.x (Optional²)
* [Nutz Web Framework](https://github.com/nutzam/nutz) 1.x
* [Struts2 MVC](http://struts.apache.org/) 2.3.x -> 2.5.x, 7.x (Jakarta, JDK17)
* Resin 3 (Optional¹), See [SkySPM Plugin Repository](https://github.com/SkyAPM/java-plugin-extensions)
* Resin 4 (Optional¹), See [SkySPM Plugin Repository](https://github.com/SkyAPM/java-plugin-extensions)
* [Jetty Server](http://www.eclipse.org/jetty/) 9.x -> 12.x
- * [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x (Optional²) -> 6.x (Optional²)
+ * [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) 5.x (Optional²) -> 7.x (Optional²)
* [Undertow](http://undertow.io/) 1.3.0.Final -> 2.3.18.Final
* [RESTEasy](https://resteasy.dev/) 3.1.0.Final -> 6.2.4.Final
* [Play Framework](https://www.playframework.com/) 2.6.x -> 2.8.x
@@ -33,16 +33,16 @@
* [Okhttp](https://github.com/square/okhttp) 2.x -> 3.x -> 4.x
* [Apache httpcomponent HttpClient](http://hc.apache.org/) 2.0 -> 3.1, 4.2, 4.3, 5.0, 5.1
* [Spring RestTemplate](https://github.com/spring-projects/spring-framework) 4.x
- * [Spring RestTemplate](https://github.com/spring-projects/spring-framework) 6.x (Optional²)
+ * [Spring RestTemplate](https://github.com/spring-projects/spring-framework) 6.x -> 7.x (Optional²)
* [Jetty Client](http://www.eclipse.org/jetty/) 9.x -> 11.x
* [Apache httpcomponent AsyncClient](https://hc.apache.org/httpcomponents-asyncclient-4.1.x/) 4.x
* [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) 2.1+
- * [Spring Webflux WebClient](https://github.com/spring-projects/spring-framework/tree/main/spring-webflux) 5.x -> 6.x
+ * [Spring Webflux WebClient](https://github.com/spring-projects/spring-framework/tree/main/spring-webflux) 5.x -> 7.x
* JRE HttpURLConnection (Optional²)
* [Hutool-http](https://www.hutool.cn/) client 5.x
* [Micronaut HTTP Client](https://github.com/micronaut-projects/micronaut-core) 3.2.x -> 3.6.x
* HTTP Gateway
- * [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 4.3.x (Optional²)
+ * [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 5.0.x (Optional²)
* [Apache ShenYu](https://shenyu.apache.org) (Rich protocol support: `HTTP`,`Spring Cloud`,`gRPC`,`Dubbo`,`SOFARPC`,`Motan`,`Tars`) 2.4.x (Optional²)
* JDBC
* Mysql Driver 5.x, 6.x, 8.x, 9.x
diff --git a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md
index 6b84d91..2e4402b 100644
--- a/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md
+++ b/docs/en/setup/service-agent/java-agent/agent-optional-plugins/spring-gateway.md
@@ -1,6 +1,6 @@
# Spring Gateway Plugin
-Spring Gateway Plugin only support Spring Gateway 2.x, 3.x and 4.x. It has capabilities to create entry spans for
+Spring Gateway Plugin only support Spring Gateway 2.x, 3.x, 4.x and 5.x. It has capabilities to create entry spans for
incoming calls, continue tracing context propagation in Spring Gateway and create exit spans for outgoing calls.
About the filter extension of Gateway, it provides automatically support as much as possible, including GlobalFilter and GatewayFilter
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/gateway-5.x-scenario/config/expectedData.yaml
new file mode 100644
index 0000000..f07157b
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/config/expectedData.yaml
@@ -0,0 +1,146 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+# The typical track this scenario asserts:
+#
+# curl -> gateway-projectA (Spring Cloud Gateway 5.x on Spring Boot 4)
+# /provider/b/testcase Entry (spring-webflux, componentId 67)
+# SpringCloudGateway/GatewayFilter Local (componentId 61)
+# SpringCloudGateway/RoutingFilter Local (componentId 61)
+# SpringCloudGateway/send Local (componentId 61)
+# SpringCloudGateway/sendRequest Exit (componentId 61)
+# -> gateway-projectB
+# GET:/provider/b/testcase Entry (tomcat, componentId 1) + CrossProcess ref
+#
+# The componentId 67 entry span on projectA is produced by the webflux DispatcherHandler
+# interceptor that gateway-4.x shades, which is what apache/skywalking#14047 broke on Spring
+# Framework 7. The CrossProcess ref on projectB proves the gateway propagated context over the wire.
+segmentItems:
+ - serviceName: gateway-projectB-scenario
+ segmentSize: ge 1
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: GET:/provider/b/testcase
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ spanType: Entry
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:18070/provider/b/testcase'}
+ - {key: http.method, value: GET}
+ - {key: http.status_code, value: '200'}
+ logs: []
+ refs:
+ - {parentEndpoint: SpringCloudGateway/send, networkAddress: 'localhost:18070',
+ refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: not null, traceId: not null}
+
+ - serviceName: gateway-projectA-scenario
+ segmentSize: ge 4
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: /provider/b/testcase
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ spanType: Entry
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 67
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/provider/b/testcase'}
+ - {key: http.method, value: GET}
+ - {key: http.status_code, value: '200'}
+ logs: []
+ - segmentId: not null
+ spans:
+ - operationName: SpringCloudGateway/sendRequest
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Http
+ spanType: Exit
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 61
+ isError: false
+ peer: localhost:18070
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:18070/provider/b/testcase'}
+ - {key: http.status_code, value: '200'}
+ logs: []
+ - operationName: SpringCloudGateway/send
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ spanType: Local
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 61
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ logs: []
+ refs:
+ - {parentEndpoint: SpringCloudGateway/GatewayFilter, networkAddress: '',
+ refType: CrossThread, parentSpanId: 1, parentTraceSegmentId: not null,
+ parentServiceInstance: not null, parentService: not null, traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: SpringCloudGateway/RoutingFilter
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Unknown
+ spanType: Local
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 61
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ logs: []
+ refs:
+ - {parentEndpoint: /provider/b/testcase, networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null,
+ parentService: not null, traceId: not null}
+ - operationName: SpringCloudGateway/GatewayFilter
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ spanType: Local
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 61
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ logs: []
+ refs:
+ - {parentEndpoint: /provider/b/testcase, networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null,
+ parentService: not null, traceId: not null}
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/configuration.yml b/test/plugin/scenarios/gateway-5.x-scenario/configuration.yml
new file mode 100644
index 0000000..ff2a41c
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/configuration.yml
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+type: jvm
+entryService: http://localhost:8080/provider/b/testcase
+healthCheck: http://localhost:8080/provider/b/healthCheck
+startScript: ./bin/startup.sh
+runningMode: with_optional
+withPlugins: apm-spring-cloud-gateway-4.x-plugin-*.jar
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/bin/startup.sh b/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/bin/startup.sh
new file mode 100755
index 0000000..3697489
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/bin/startup.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+home="$(cd "$(dirname $0)"; pwd)"
+
+java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectA-scenario" ${home}/../libs/gateway-projectA-scenario.jar &
+sleep 1
+
+java -jar ${agent_opts} "-Dskywalking.agent.service_name=gateway-projectB-scenario" ${home}/../libs/gateway-projectB-scenario.jar &
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/pom.xml b/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/pom.xml
new file mode 100644
index 0000000..7cefa11
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>gateway-5.x-scenario</artifactId>
+ <version>5.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>gateway-dist</artifactId>
+
+ <build>
+ <finalName>gateway-5.x-scenario</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <finalName>gateway-5.x-scenario</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptors>
+ <descriptor>src/main/assembly/assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>../target/</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/src/main/assembly/assembly.xml b/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..4dc7aec
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-dist/src/main/assembly/assembly.xml
@@ -0,0 +1,44 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <id>gateway-dist</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <directory>./bin</directory>
+ <fileMode>0775</fileMode>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>../gateway-projectA-scenario/target/gateway-projectA-scenario.jar</source>
+ <outputDirectory>./libs</outputDirectory>
+ <fileMode>0775</fileMode>
+ </file>
+ <file>
+ <source>../gateway-projectB-scenario/target/gateway-projectB-scenario.jar</source>
+ <outputDirectory>./libs</outputDirectory>
+ <fileMode>0775</fileMode>
+ </file>
+ </files>
+</assembly>
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/pom.xml b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/pom.xml
new file mode 100644
index 0000000..1df72d0
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>gateway-5.x-scenario</artifactId>
+ <version>5.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>gateway-projectA-scenario</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
+ <version>${test.framework.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>gateway-projectA-scenario</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring.boot.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java
new file mode 100644
index 0000000..d4ba09e
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectA/Application.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 test.apache.skywalking.apm.testcase.sc.gateway.projectA;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml
new file mode 100644
index 0000000..bf0cb5c
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectA-scenario/src/main/resources/application.yml
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+server:
+ port: 8080
+spring:
+ cloud:
+ gateway:
+ server:
+ webflux:
+ httpclient:
+ connect-timeout: 2000
+ routes:
+ - id: provider_route
+ uri: http://localhost:18070
+ predicates:
+ - Path=/provider/b/*
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/pom.xml b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/pom.xml
new file mode 100644
index 0000000..6336942
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>gateway-5.x-scenario</artifactId>
+ <version>5.0.0</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>gateway-projectB-scenario</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>gateway-projectB-scenario</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring.boot.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java
new file mode 100644
index 0000000..23b6ba1
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/Application.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 test.apache.skywalking.apm.testcase.sc.gateway.projectB;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java
new file mode 100644
index 0000000..4295e9c
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/gateway/projectB/controller/TestController.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 test.apache.skywalking.apm.testcase.sc.gateway.projectB.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class TestController {
+
+ @RequestMapping("/provider/b/testcase")
+ public String testcase() {
+ return "1";
+ }
+
+ @RequestMapping("/provider/b/healthCheck")
+ public String healthCheck() {
+ return "Success";
+ }
+}
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties
new file mode 100644
index 0000000..1a46d23
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/gateway-projectB-scenario/src/main/resources/application.properties
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+server.port=18070
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/pom.xml b/test/plugin/scenarios/gateway-5.x-scenario/pom.xml
new file mode 100644
index 0000000..65f6b95
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>gateway-5.x-scenario</artifactId>
+ <packaging>pom</packaging>
+ <version>5.0.0</version>
+ <modules>
+ <module>gateway-projectA-scenario</module>
+ <module>gateway-projectB-scenario</module>
+ <module>gateway-dist</module>
+ </modules>
+
+ <name>skywalking-gateway-5.x-scenario</name>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <java.version>17</java.version>
+ <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
+ <!-- Spring Cloud Gateway version, overridden per support-version.list line by the runner. -->
+ <test.framework.version>5.0.3</test.framework.version>
+ <spring.boot.version>4.0.8</spring.boot.version>
+ <docker.image.version>${test.framework.version}</docker.image.version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${spring.boot.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <finalName>gateway-5.x-scenario</finalName>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${maven-compiler-plugin.version}</version>
+ <configuration>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/test/plugin/scenarios/gateway-5.x-scenario/support-version.list b/test/plugin/scenarios/gateway-5.x-scenario/support-version.list
new file mode 100644
index 0000000..e5229a2
--- /dev/null
+++ b/test/plugin/scenarios/gateway-5.x-scenario/support-version.list
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+# Spring Cloud Gateway server (WebFlux flavour) versions. 5.0.x targets Spring Boot 4 /
+# Spring Framework 7. One version per minor, latest patch.
+5.0.3
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/bin/startup.sh b/test/plugin/scenarios/webflux-6.x-7.x-scenario/bin/startup.sh
new file mode 100755
index 0000000..eaffb26
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/bin/startup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+home="$(cd "$(dirname $0)"; pwd)"
+
+java -jar ${agent_opts} ${home}/../libs/webflux-6.x-7.x-scenario.jar &
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/webflux-6.x-7.x-scenario/config/expectedData.yaml
new file mode 100644
index 0000000..2b55dc0
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/config/expectedData.yaml
@@ -0,0 +1,111 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+# The typical track this scenario asserts:
+#
+# GET /case/webflux Entry (spring-webflux, componentId 67)
+# -> WebClient GET /case/receive Exit (spring-webflux-webclient, componentId 99)
+# -> GET /case/receive Entry (spring-webflux, componentId 67) + CrossProcess ref
+# -> WebFluxOperators/onNext Local (webflux toolkit, componentId 67) + CrossThread ref
+#
+# The CrossProcess ref on the /case/receive entry span is the load-bearing assertion: it can only
+# be produced when DispatcherHandler successfully reads the sw8 header off the inbound request,
+# which is what apache/skywalking#14047 broke on Spring Framework 7.
+segmentItems:
+ - serviceName: webflux-6.x-7.x-scenario
+ segmentSize: ge 4
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: WebFluxOperators/onNext
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Unknown
+ spanType: Local
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 67
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ logs: []
+ refs:
+ - {parentEndpoint: /case/receive, networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null,
+ parentService: not null, traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /case/receive
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ spanType: Entry
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 67
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/case/receive'}
+ - {key: http.method, value: GET}
+ - {key: http.status_code, value: '200'}
+ logs: []
+ refs:
+ - {parentEndpoint: /case/receive, networkAddress: 'localhost:8080', refType: CrossProcess,
+ parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null,
+ parentService: not null, traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /case/receive
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ spanType: Exit
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 99
+ isError: false
+ peer: localhost:8080
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/case/receive'}
+ - {key: http.method, value: GET}
+ - {key: http.status_code, value: '200'}
+ logs: []
+ refs:
+ - {parentEndpoint: /case/webflux, networkAddress: '', refType: CrossThread,
+ parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not null,
+ parentService: not null, traceId: not null}
+ - segmentId: not null
+ spans:
+ - operationName: /case/webflux
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ spanType: Entry
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 67
+ isError: false
+ peer: ''
+ skipAnalysis: false
+ tags:
+ - {key: url, value: 'http://localhost:8080/case/webflux'}
+ - {key: http.method, value: GET}
+ - {key: http.status_code, value: '200'}
+ logs: []
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/configuration.yml b/test/plugin/scenarios/webflux-6.x-7.x-scenario/configuration.yml
new file mode 100644
index 0000000..19165fd
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/configuration.yml
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+type: jvm
+entryService: http://localhost:8080/case/webflux
+healthCheck: http://localhost:8080/case/healthCheck
+startScript: ./bin/startup.sh
+runningMode: with_optional
+withPlugins: apm-spring-webflux-6.x-plugin-*.jar
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/pom.xml b/test/plugin/scenarios/webflux-6.x-7.x-scenario/pom.xml
new file mode 100644
index 0000000..4863d9d
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/pom.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>webflux-6.x-7.x-scenario</artifactId>
+ <version>5.0.0</version>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <compiler.version>17</compiler.version>
+ <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
+ <!-- Spring Boot version, overridden per support-version.list line by the test runner. -->
+ <test.framework.version>3.0.13</test.framework.version>
+ <docker.image.version>${test.framework.version}</docker.image.version>
+ </properties>
+
+ <name>skywalking-webflux-6.x-7.x-scenario</name>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${test.framework.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-webflux</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>webflux-6.x-7.x-scenario</finalName>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${test.framework.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${maven-compiler-plugin.version}</version>
+ <configuration>
+ <source>${compiler.version}</source>
+ <target>${compiler.version}</target>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <finalName>webflux-6.x-7.x-scenario</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptors>
+ <descriptor>src/main/assembly/assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>./target/</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..52f67b1
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/assembly/assembly.xml
@@ -0,0 +1,39 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF 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.
+ -->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+ <id>webflux-6.x-7.x-scenario</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <directory>./bin</directory>
+ <fileMode>0775</fileMode>
+ </fileSet>
+ </fileSets>
+
+ <files>
+ <file>
+ <source>${project.build.directory}/webflux-6.x-7.x-scenario.jar</source>
+ <outputDirectory>./libs</outputDirectory>
+ <fileMode>0775</fileMode>
+ </file>
+ </files>
+</assembly>
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/webflux/Application.java b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/webflux/Application.java
new file mode 100644
index 0000000..aab807e
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/webflux/Application.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.skywalking.apm.testcase.webflux;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/webflux/controller/TestController.java b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/webflux/controller/TestController.java
new file mode 100644
index 0000000..2196183
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/webflux/controller/TestController.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.skywalking.apm.testcase.webflux.controller;
+
+import org.apache.skywalking.apm.toolkit.webflux.v6.WebFluxSkyWalkingOperators;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+import reactor.util.context.Context;
+
+@RestController
+@RequestMapping("/case")
+public class TestController {
+
+ @Value("${server.port:8080}")
+ private String serverPort;
+
+ /**
+ * Entry case. Calls {@link #receive()} over HTTP so the trace crosses a real request boundary:
+ * the WebClient plugin writes the sw8 header on the exit span, and the WebFlux DispatcherHandler
+ * interceptor has to read it back to build the entry span of the downstream segment. That
+ * downstream segment's cross-process ref is what proves header extraction works.
+ */
+ @GetMapping("/webflux")
+ public Mono<String> webflux() {
+ return WebClient.create()
+ .get()
+ .uri("http://localhost:" + serverPort + "/case/receive")
+ .retrieve()
+ .bodyToMono(String.class);
+ }
+
+ /**
+ * Downstream endpoint. Also drives the webflux toolkit through its Reactor 3.5+ package, so the
+ * toolkit activation is covered by the same request.
+ */
+ @GetMapping("/receive")
+ public Mono<String> receive() {
+ return Mono.deferContextual(ctx -> WebFluxSkyWalkingOperators.continueTracing(
+ Context.of(ctx), () -> Mono.just("Success")));
+ }
+
+ @RequestMapping("/healthCheck")
+ public Mono<String> healthCheck() {
+ return Mono.just("healthCheck");
+ }
+}
diff --git a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingOperators.java
similarity index 94%
copy from apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java
copy to test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingOperators.java
index 70ebacc..4cdd43e 100644
--- a/apm-application-toolkit/apm-toolkit-webflux/src/main/java/org/apache/skywalking/apm/toolkit/webflux/WebFluxSkyWalkingOperators.java
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/java/org/apache/skywalking/apm/toolkit/webflux/v6/WebFluxSkyWalkingOperators.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.toolkit.webflux;
+package org.apache.skywalking.apm.toolkit.webflux.v6;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Signal;
@@ -63,7 +63,7 @@
if (signalType != signal.getType()) {
return;
}
- continueTracing(signal.getContext(), () -> consumer.accept(signal));
+ continueTracing(Context.of(signal.getContextView()), () -> consumer.accept(signal));
};
}
@@ -75,7 +75,7 @@
*/
public static Consumer<Signal> continueTracing(Runnable runnable) {
return signal -> {
- Context context = signal.getContext();
+ Context context = Context.of(signal.getContextView());
continueTracing(context, runnable);
};
}
@@ -103,7 +103,7 @@
return callable.call();
} catch (Exception e) {
return sneakyThrow(e);
- }
+ }
}
/**
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/resources/application.properties b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/resources/application.properties
new file mode 100644
index 0000000..76e70d4
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/src/main/resources/application.properties
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+server.port=8080
diff --git a/test/plugin/scenarios/webflux-6.x-7.x-scenario/support-version.list b/test/plugin/scenarios/webflux-6.x-7.x-scenario/support-version.list
new file mode 100644
index 0000000..9691bd3
--- /dev/null
+++ b/test/plugin/scenarios/webflux-6.x-7.x-scenario/support-version.list
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+#
+
+# Spring Boot versions. 3.x -> Spring Framework 6.x, 4.x -> Spring Framework 7.x.
+# One version per minor, latest patch.
+3.0.13
+3.1.12
+3.2.12
+3.3.13
+3.4.13
+3.5.16
+4.0.8
+4.1.1