diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ContextLocalPropagationTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ContextLocalPropagationTest.java index 47cc39884f7b0..ef42ce9477633 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ContextLocalPropagationTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/ContextLocalPropagationTest.java @@ -21,6 +21,7 @@ import io.quarkus.test.QuarkusUnitTest; import io.smallrye.common.vertx.ContextLocals; +import io.smallrye.mutiny.Uni; import io.vertx.core.Context; import io.vertx.core.Vertx; @@ -33,13 +34,21 @@ public class ContextLocalPropagationTest { "http://localhost:${quarkus.http.test-port:8081}"); @Test - void testQueryParamsWithPrimitiveArrays() { + void testClientFilterSeesParentContext() { when().get("test/invokeClient") .then() .statusCode(200) .body(is("test/foo/bar")); } + @Test + void testClientUniInheritsParentContext() { + when().get("test/client-inherits-context") + .then() + .statusCode(200) + .body(is("test/set-in-parent")); + } + @Path("test") public static class Resource { @@ -58,11 +67,25 @@ public String invokeClient() { return result + "/" + fromRequest.orElse("none") + "/" + fromResponse.orElse("none"); } + @Path("client-inherits-context") + @GET + public Uni clientInheritsContext() { + ContextLocals.put("parent-value", "set-in-parent"); + return client.getUni() + .map(result -> result + "/" + ContextLocals.get("parent-value").orElse("none")); + } + @Path("toClient") @GET public String toClient() { return "test"; } + + @Path("toClient2") + @GET + public String toClient2() { + return "test"; + } } @Path("test") @@ -74,6 +97,10 @@ public interface Client { @GET @Path("toClient") String get(); + + @GET + @Path("toClient2") + Uni getUni(); } public static class RequestFilter implements ClientRequestFilter { @@ -110,10 +137,10 @@ private static Context determineRestClientContext() { // We will need a proper solution soon, but as we need to have a proper way to // set contextual information in Quarkus 3.20 (LTS), we can't risk breaking // client code everywhere, so for now we will tell people to check the context - Optional maybeParentContext = ContextLocals.get("__PARENT_CONTEXT__"); + var maybeParentContext = ContextLocals.getParentContext(); Context effectiveContext; - if (maybeParentContext.isPresent()) { - effectiveContext = (Context) maybeParentContext.get(); + if (maybeParentContext != null) { + effectiveContext = maybeParentContext; } else { effectiveContext = Vertx.currentContext(); } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientRequestContextImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientRequestContextImpl.java index 4c41bddddaf03..346ddedc3f182 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientRequestContextImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientRequestContextImpl.java @@ -65,19 +65,7 @@ public ClientRequestContextImpl(RestClientRequestContext restClientRequestContex // Always create a duplicated context because each REST Client invocation must have its own context // A separate context allows integrations like OTel to create a separate Span for each invocation (expected) Context current = client.vertx.getOrCreateContext(); - this.context = VertxContext.createNewDuplicatedContext(current); - // If an interceptor wants to access the parent context, it can do so by using the "__PARENT_CONTEXT__" key. - // This approach is compatible with the next-to-be nested context support (from SmallRye Common). - // The "__PARENT_CONTEXT__" key will continue to reference the _parent_ context. - // So, currently, an interceptor needing to read from the original context will need to use: - // Context parent = ContextLocals.get("__PARENT_CONTEXT__"); - // V v = parent.getLocal("some-key"); - // To write to the original context, it will need to use: - // Context parent = ContextLocals.get("__PARENT_CONTEXT__"); - // parent.putLocal("some-key", someValue); - // Note that unlike with nested contexts, multiple child contexts can write to the same parent key, and may - // lead to a race condition. - this.context.putLocal("__PARENT_CONTEXT__", current); + this.context = VertxContext.newNestedContext(current); restClientRequestContext.properties.put(VERTX_CONTEXT_PROPERTY, context); }