diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java index c39e6d53a8c3..020e6614cc72 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java @@ -21,6 +21,7 @@ import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.threadpool.ThreadlessExecutor; import org.apache.dubbo.common.utils.SystemPropertyConfigUtils; +import org.apache.dubbo.rpc.RpcContext.RestoreContext; import org.apache.dubbo.rpc.model.ConsumerMethodModel; import org.apache.dubbo.rpc.protocol.dubbo.FutureAdapter; @@ -81,6 +82,16 @@ public AsyncRpcResult(CompletableFuture future, Invocation invocati && !future.isDone()) { async = true; this.storedContext = RpcContext.clearAndStoreContext(); + + // this is to fix https://github.com/apache/dubbo/issues/13666 + this.responseFuture = this.responseFuture.thenApply((appResponse) -> { + RestoreContext tempStoredContext = RpcContext.clearAndStoreContext(); + this.storedContext.restore(); + appResponse.addObjectAttachments( + RpcContext.getServerResponseContext().getObjectAttachments()); + tempStoredContext.restore(); + return appResponse; + }); } else { async = false; } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContextAttachment.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContextAttachment.java index e0c70c38acda..47ea5c5c1bab 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContextAttachment.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContextAttachment.java @@ -245,9 +245,10 @@ public Object get(String key) { * @return a copy of RpcContextAttachment with deep copied attachments */ public RpcContextAttachment copyOf(boolean needCopy) { - if (!isValid()) { - return null; - } + // to fix https://github.com/apache/dubbo/issues/13666 + // if (!isValid()) { + // return null; + // } if (needCopy) { RpcContextAttachment copy = new RpcContextAttachment(); diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/AsyncContextService.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/AsyncContextService.java new file mode 100644 index 000000000000..410ab506f16d --- /dev/null +++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/api/AsyncContextService.java @@ -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. + */ +package org.apache.dubbo.test.common.api; + +import java.util.concurrent.CompletableFuture; + +public interface AsyncContextService { + CompletableFuture getServerSideContext(); +} diff --git a/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/AsyncContextServiceImpl.java b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/AsyncContextServiceImpl.java new file mode 100644 index 000000000000..30d4565471ed --- /dev/null +++ b/dubbo-test/dubbo-test-common/src/main/java/org/apache/dubbo/test/common/impl/AsyncContextServiceImpl.java @@ -0,0 +1,40 @@ +/* + * 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.dubbo.test.common.impl; + +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.rpc.RpcContextAttachment; +import org.apache.dubbo.test.common.api.AsyncContextService; + +import java.util.concurrent.CompletableFuture; + +public class AsyncContextServiceImpl implements AsyncContextService { + + @Override + public CompletableFuture getServerSideContext() { + RpcContextAttachment serverResponseContext = RpcContext.getServerResponseContext(); + return CompletableFuture.supplyAsync(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + serverResponseContext.setAttachment("theToken", "Hello, Dubbo"); + return "finish"; + }); + } +} diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringRpcContextTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringRpcContextTest.java new file mode 100644 index 000000000000..4692f1efb653 --- /dev/null +++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringRpcContextTest.java @@ -0,0 +1,88 @@ +/* + * 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.dubbo.test.spring; + +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.rpc.RpcContext; +import org.apache.dubbo.test.common.SysProps; +import org.apache.dubbo.test.common.api.AsyncContextService; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; + +public class SpringRpcContextTest { + private static ClassPathXmlApplicationContext providerContext; + + @BeforeAll + public static void beforeAll() { + DubboBootstrap.reset(); + } + + @AfterAll + public static void afterAll() { + DubboBootstrap.reset(); + providerContext.close(); + } + + private void startProvider() { + providerContext = new ClassPathXmlApplicationContext("/spring/dubbo-demo-provider.xml"); + } + + @Test + public void test() { + SysProps.setProperty(SHUTDOWN_WAIT_KEY, "10000"); + startProvider(); + ClassPathXmlApplicationContext applicationContext = null; + try { + applicationContext = new ClassPathXmlApplicationContext("/spring/dubbo-demo.xml"); + AsyncContextService asyncContextService = + applicationContext.getBean("asyncContextService", AsyncContextService.class); + + AtomicBoolean contextCorrect = new AtomicBoolean(true); + + for (int i = 0; i < 5; i++) { + CompletableFuture serverSideContext = asyncContextService.getServerSideContext(); + serverSideContext.whenComplete((s, throwable) -> { + if (!"Hello, Dubbo" + .equals(RpcContext.getClientResponseContext().getAttachment("theToken"))) { + contextCorrect.set(false); + } + RpcContext.removeContext(); + }); + serverSideContext.get(); + } + Assertions.assertTrue(contextCorrect.get()); + + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } finally { + SysProps.clear(); + if (applicationContext != null) { + applicationContext.close(); + } + } + } +} diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo-provider.xml b/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo-provider.xml index a6b945835614..d00a875e9739 100644 --- a/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo-provider.xml +++ b/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo-provider.xml @@ -33,10 +33,12 @@ + + diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml b/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml index 5b67af72efd0..4e34b4ce3bfa 100644 --- a/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml +++ b/dubbo-test/dubbo-test-spring/src/main/resources/spring/dubbo-demo.xml @@ -40,4 +40,6 @@ + +