From 305d290e5fa8491803f9fc4f0d1088a316eaf3f5 Mon Sep 17 00:00:00 2001 From: Albumen Kevin Date: Tue, 8 Nov 2022 11:34:05 +0800 Subject: [PATCH] Fix dubbo get wrong return type when generic impl invoke (#10891) --- .../apache/dubbo/rpc/support/RpcUtils.java | 14 ++- .../dubbo/rpc/support/RpcUtilsTest.java | 111 +++++++++++++++++- .../protocol/dubbo/DecodeableRpcResult.java | 10 +- .../dubbo/common/serialize/ObjectInput.java | 1 - 4 files changed, 125 insertions(+), 11 deletions(-) diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java index 8f864efb46..a1cc84ecb9 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java @@ -78,10 +78,22 @@ public class RpcUtils { && invocation.getInvoker().getUrl() != null && invocation.getInvoker().getInterface() != GenericService.class && !invocation.getMethodName().startsWith("$")) { + Type[] returnTypes = null; + if (invocation instanceof RpcInvocation) { + returnTypes = ((RpcInvocation) invocation).getReturnTypes(); + if (returnTypes != null) { + return returnTypes; + } + } String service = invocation.getInvoker().getUrl().getServiceInterface(); if (StringUtils.isNotEmpty(service)) { Method method = getMethodByService(invocation, service); - return ReflectUtils.getReturnTypes(method); + if (method != null) { + returnTypes = ReflectUtils.getReturnTypes(method); + } + } + if (returnTypes != null) { + return returnTypes; } } } catch (Throwable t) { diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java index ed5271191f..a6748a74d8 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java +++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java @@ -35,6 +35,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE; import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -153,7 +154,7 @@ public class RpcUtilsTest { } @Test - public void testGetReturnTypes() throws Exception { + public void testGetReturnTypesUseCache() throws Exception { Class demoServiceClass = DemoService.class; String serviceName = demoServiceClass.getName(); Invoker invoker = mock(Invoker.class); @@ -216,6 +217,114 @@ public class RpcUtilsTest { Assertions.assertArrayEquals(types5, inv5.getReturnTypes()); } + @Test + public void testGetReturnTypesWithoutCache() throws Exception { + Class demoServiceClass = DemoService.class; + String serviceName = demoServiceClass.getName(); + Invoker invoker = mock(Invoker.class); + given(invoker.getUrl()).willReturn(URL.valueOf( + "test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService")); + + RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv.setReturnTypes(null); + Type[] types = RpcUtils.getReturnTypes(inv); + Assertions.assertNotNull(types); + Assertions.assertEquals(2, types.length); + Assertions.assertEquals(String.class, types[0]); + Assertions.assertEquals(String.class, types[1]); + + RpcInvocation inv1 = + new RpcInvocation("testReturnType1", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv1.setReturnTypes(null); + java.lang.reflect.Type[] types1 = RpcUtils.getReturnTypes(inv1); + Assertions.assertNotNull(types1); + Assertions.assertEquals(2, types1.length); + Assertions.assertEquals(List.class, types1[0]); + Assertions.assertEquals(demoServiceClass.getMethod("testReturnType1", String.class).getGenericReturnType(), types1[1]); + + RpcInvocation inv2 = + new RpcInvocation("testReturnType2", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv2.setReturnTypes(null); + java.lang.reflect.Type[] types2 = RpcUtils.getReturnTypes(inv2); + Assertions.assertNotNull(types2); + Assertions.assertEquals(2, types2.length); + Assertions.assertEquals(String.class, types2[0]); + Assertions.assertEquals(String.class, types2[1]); + + RpcInvocation inv3 = + new RpcInvocation("testReturnType3", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv3.setReturnTypes(null); + java.lang.reflect.Type[] types3 = RpcUtils.getReturnTypes(inv3); + Assertions.assertNotNull(types3); + Assertions.assertEquals(2, types3.length); + Assertions.assertEquals(List.class, types3[0]); + java.lang.reflect.Type genericReturnType3 = demoServiceClass.getMethod("testReturnType3", String.class).getGenericReturnType(); + Assertions.assertEquals(((ParameterizedType) genericReturnType3).getActualTypeArguments()[0], types3[1]); + + RpcInvocation inv4 = + new RpcInvocation("testReturnType4", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv4.setReturnTypes(null); + java.lang.reflect.Type[] types4 = RpcUtils.getReturnTypes(inv4); + Assertions.assertNotNull(types4); + Assertions.assertEquals(2, types4.length); + Assertions.assertNull(types4[0]); + Assertions.assertNull(types4[1]); + + RpcInvocation inv5 = + new RpcInvocation("testReturnType5", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv5.setReturnTypes(null); + java.lang.reflect.Type[] types5 = RpcUtils.getReturnTypes(inv5); + Assertions.assertNotNull(types5); + Assertions.assertEquals(2, types5.length); + Assertions.assertEquals(Map.class, types5[0]); + java.lang.reflect.Type genericReturnType5 = demoServiceClass.getMethod("testReturnType5", String.class).getGenericReturnType(); + Assertions.assertEquals(((ParameterizedType) genericReturnType5).getActualTypeArguments()[0], types5[1]); + } + + + @Test + public void testGetReturnTypesWhenGeneric() throws Exception { + Class demoServiceClass = DemoService.class; + String serviceName = demoServiceClass.getName(); + Invoker invoker = mock(Invoker.class); + given(invoker.getUrl()).willReturn(URL.valueOf( + "test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService")); + + RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv.setMethodName($INVOKE); + Type[] types = RpcUtils.getReturnTypes(inv); + Assertions.assertNull(types); + + RpcInvocation inv1 = + new RpcInvocation("testReturnType1", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv1.setMethodName($INVOKE); + java.lang.reflect.Type[] types1 = RpcUtils.getReturnTypes(inv1); + Assertions.assertNull(types1); + + RpcInvocation inv2 = + new RpcInvocation("testReturnType2", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv2.setMethodName($INVOKE); + java.lang.reflect.Type[] types2 = RpcUtils.getReturnTypes(inv2); + Assertions.assertNull(types2); + + RpcInvocation inv3 = + new RpcInvocation("testReturnType3", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv3.setMethodName($INVOKE); + java.lang.reflect.Type[] types3 = RpcUtils.getReturnTypes(inv3); + Assertions.assertNull(types3); + + RpcInvocation inv4 = + new RpcInvocation("testReturnType4", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv4.setMethodName($INVOKE); + java.lang.reflect.Type[] types4 = RpcUtils.getReturnTypes(inv4); + Assertions.assertNull(types4); + + RpcInvocation inv5 = + new RpcInvocation("testReturnType5", serviceName, "", new Class[] {String.class}, null, null, invoker, null); + inv5.setMethodName($INVOKE); + java.lang.reflect.Type[] types5 = RpcUtils.getReturnTypes(inv5); + Assertions.assertNull(types5); + } @Test public void testGetParameterTypes() { Class demoServiceClass = DemoService.class; diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java index 145f3a2fb9..36e9421200 100644 --- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java +++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DecodeableRpcResult.java @@ -32,7 +32,6 @@ import org.apache.dubbo.remoting.exchange.Response; import org.apache.dubbo.remoting.transport.CodecSupport; import org.apache.dubbo.rpc.AppResponse; import org.apache.dubbo.rpc.Invocation; -import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.support.RpcUtils; import java.io.IOException; @@ -151,15 +150,10 @@ public class DecodeableRpcResult extends AppResponse implements Codec, Decodeabl private void handleValue(ObjectInput in) throws IOException { try { - Type[] returnTypes; - if (invocation instanceof RpcInvocation) { - returnTypes = ((RpcInvocation) invocation).getReturnTypes(); - } else { - returnTypes = RpcUtils.getReturnTypes(invocation); - } + Type[] returnTypes = RpcUtils.getReturnTypes(invocation); Object value; if (ArrayUtils.isEmpty(returnTypes)) { - // This almost never happens? + // happens when generic invoke or void return value = in.readObject(); } else if (returnTypes.length == 1) { value = in.readObject((Class) returnTypes[0]); diff --git a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java index 0c68993523..4c3ce7c736 100644 --- a/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java +++ b/dubbo-serialization/dubbo-serialization-api/src/main/java/org/apache/dubbo/common/serialize/ObjectInput.java @@ -32,7 +32,6 @@ public interface ObjectInput extends DataInput { * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if an ClassNotFoundException occurs */ - @Deprecated Object readObject() throws IOException, ClassNotFoundException; /**