Skip to content

Commit ed0e11e

Browse files
skt-shinyruozrlw
andauthored
Fix Triple method resolution for overloaded wrapper methods (#16325)
* Fix Triple method resolution for overloaded wrapper methods Fixes #14939 Signed-off-by: bubu <1654388696@qq.com> * Address response type comparison review * Handle ambiguous Triple reflection resolution --------- Signed-off-by: bubu <1654388696@qq.com> Co-authored-by: zrlw <zrlw@sina.com>
1 parent 2626e28 commit ed0e11e

2 files changed

Lines changed: 289 additions & 43 deletions

File tree

dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DescriptorUtils.java

Lines changed: 134 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.apache.dubbo.common.URL;
2020
import org.apache.dubbo.common.constants.CommonConstants;
21+
import org.apache.dubbo.common.stream.StreamObserver;
2122
import org.apache.dubbo.common.utils.CollectionUtils;
2223
import org.apache.dubbo.remoting.http12.exception.UnimplementedException;
2324
import org.apache.dubbo.rpc.Invoker;
@@ -30,6 +31,7 @@
3031

3132
import java.io.IOException;
3233
import java.io.InputStream;
34+
import java.lang.reflect.Type;
3335
import java.util.Arrays;
3436
import java.util.List;
3537

@@ -86,62 +88,151 @@ public static MethodDescriptor findMethodDescriptor(
8688

8789
public static MethodDescriptor findReflectionMethodDescriptor(
8890
ServiceDescriptor serviceDescriptor, String methodName) {
89-
MethodDescriptor methodDescriptor = null;
91+
MethodDescriptor methodDescriptor = findWellKnownMethodDescriptor(methodName);
92+
if (methodDescriptor != null) {
93+
return methodDescriptor;
94+
}
95+
96+
List<MethodDescriptor> methodDescriptors = serviceDescriptor.getMethods(methodName);
97+
methodDescriptor = findSingleOrGeneratedUnaryMethodDescriptor(methodDescriptors);
98+
if (methodDescriptor == null && CollectionUtils.isNotEmpty(methodDescriptors)) {
99+
throw new UnimplementedException("method:" + methodName);
100+
}
101+
return methodDescriptor;
102+
}
103+
104+
public static MethodDescriptor findTripleMethodDescriptor(
105+
ServiceDescriptor serviceDescriptor, String methodName, InputStream rawMessage) throws IOException {
106+
MethodDescriptor methodDescriptor = findWellKnownMethodDescriptor(methodName);
107+
if (methodDescriptor != null) {
108+
return methodDescriptor;
109+
}
110+
111+
List<MethodDescriptor> methodDescriptors = serviceDescriptor.getMethods(methodName);
112+
if (CollectionUtils.isEmpty(methodDescriptors)) {
113+
throw new UnimplementedException("method:" + methodName);
114+
}
115+
if (methodDescriptors.size() == 1) {
116+
return methodDescriptors.get(0);
117+
}
118+
119+
TripleRequestWrapper request = parseRequestWrapper(rawMessage);
120+
List<String> argTypes = request == null ? null : request.getArgTypes();
121+
if (argTypes != null) {
122+
MethodDescriptor matchedDescriptor =
123+
findMethodDescriptorByParamTypes(methodDescriptors, argTypes.toArray(new String[0]));
124+
if (matchedDescriptor != null) {
125+
return matchedDescriptor;
126+
}
127+
if (CollectionUtils.isNotEmpty(argTypes)) {
128+
throw new UnimplementedException("method:" + methodName);
129+
}
130+
}
131+
132+
MethodDescriptor generatedUnaryDescriptor = findGeneratedUnaryMethodDescriptor(methodDescriptors);
133+
if (generatedUnaryDescriptor != null) {
134+
return generatedUnaryDescriptor;
135+
}
136+
throw new UnimplementedException("method:" + methodName);
137+
}
138+
139+
private static MethodDescriptor findSingleOrGeneratedUnaryMethodDescriptor(
140+
List<MethodDescriptor> methodDescriptors) {
141+
if (CollectionUtils.isEmpty(methodDescriptors)) {
142+
return null;
143+
}
144+
// In most cases there is only one method
145+
if (methodDescriptors.size() == 1) {
146+
return methodDescriptors.get(0);
147+
}
148+
return findGeneratedUnaryMethodDescriptor(methodDescriptors);
149+
}
150+
151+
private static TripleRequestWrapper parseRequestWrapper(InputStream rawMessage) throws IOException {
152+
rawMessage.mark(Integer.MAX_VALUE);
153+
try {
154+
return TripleRequestWrapper.parseFrom(rawMessage);
155+
} finally {
156+
rawMessage.reset();
157+
}
158+
}
159+
160+
private static MethodDescriptor findWellKnownMethodDescriptor(String methodName) {
90161
if (isGeneric(methodName)) {
91-
// There should be one and only one
92-
methodDescriptor = ServiceDescriptorInternalCache.genericService()
162+
return ServiceDescriptorInternalCache.genericService()
93163
.getMethods(methodName)
94164
.get(0);
95-
} else if (isEcho(methodName)) {
96-
// There should be one and only one
165+
}
166+
if (isEcho(methodName)) {
97167
return ServiceDescriptorInternalCache.echoService()
98168
.getMethods(methodName)
99169
.get(0);
100-
} else {
101-
List<MethodDescriptor> methodDescriptors = serviceDescriptor.getMethods(methodName);
102-
if (CollectionUtils.isEmpty(methodDescriptors)) {
103-
return null;
104-
}
105-
// In most cases there is only one method
106-
if (methodDescriptors.size() == 1) {
107-
methodDescriptor = methodDescriptors.get(0);
108-
}
109-
// generated unary method ,use unary type
110-
// Response foo(Request)
111-
// void foo(Request,StreamObserver<Response>)
112-
if (methodDescriptors.size() == 2) {
113-
if (methodDescriptors.get(1).getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) {
114-
methodDescriptor = methodDescriptors.get(0);
115-
} else if (methodDescriptors.get(0).getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) {
116-
methodDescriptor = methodDescriptors.get(1);
117-
}
118-
}
119170
}
120-
return methodDescriptor;
171+
return null;
121172
}
122173

123-
public static MethodDescriptor findTripleMethodDescriptor(
124-
ServiceDescriptor serviceDescriptor, String methodName, InputStream rawMessage) throws IOException {
125-
MethodDescriptor methodDescriptor = findReflectionMethodDescriptor(serviceDescriptor, methodName);
126-
if (methodDescriptor == null) {
127-
rawMessage.mark(Integer.MAX_VALUE);
128-
List<MethodDescriptor> methodDescriptors = serviceDescriptor.getMethods(methodName);
129-
TripleRequestWrapper request = TripleRequestWrapper.parseFrom(rawMessage);
130-
String[] paramTypes = request.getArgTypes().toArray(new String[0]);
174+
private static MethodDescriptor findMethodDescriptorByParamTypes(
175+
List<MethodDescriptor> methodDescriptors, String[] paramTypes) {
176+
for (MethodDescriptor descriptor : methodDescriptors) {
131177
// wrapper mode the method can overload so maybe list
132-
for (MethodDescriptor descriptor : methodDescriptors) {
133-
// params type is array
134-
if (Arrays.equals(descriptor.getCompatibleParamSignatures(), paramTypes)) {
135-
methodDescriptor = descriptor;
136-
break;
137-
}
178+
if (Arrays.equals(descriptor.getCompatibleParamSignatures(), paramTypes)) {
179+
return descriptor;
138180
}
139-
if (methodDescriptor == null) {
140-
throw new UnimplementedException("method:" + methodName);
181+
}
182+
return null;
183+
}
184+
185+
private static MethodDescriptor findGeneratedUnaryMethodDescriptor(List<MethodDescriptor> methodDescriptors) {
186+
// Generated unary methods may expose two Java methods for the same RPC:
187+
// Response foo(Request)
188+
// void foo(Request, StreamObserver<Response>)
189+
if (methodDescriptors.size() != 2) {
190+
return null;
191+
}
192+
193+
MethodDescriptor unaryMethodDescriptor = null;
194+
MethodDescriptor serverStreamMethodDescriptor = null;
195+
for (MethodDescriptor descriptor : methodDescriptors) {
196+
if (descriptor.getRpcType() == MethodDescriptor.RpcType.UNARY) {
197+
unaryMethodDescriptor = descriptor;
198+
} else if (descriptor.getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) {
199+
serverStreamMethodDescriptor = descriptor;
141200
}
142-
rawMessage.reset();
143201
}
144-
return methodDescriptor;
202+
if (unaryMethodDescriptor == null || serverStreamMethodDescriptor == null) {
203+
return null;
204+
}
205+
if (!Arrays.equals(
206+
unaryMethodDescriptor.getParameterClasses(),
207+
getServerStreamRequestTypes(serverStreamMethodDescriptor))) {
208+
return null;
209+
}
210+
if (!isSameResponseType(unaryMethodDescriptor, serverStreamMethodDescriptor)) {
211+
return null;
212+
}
213+
return unaryMethodDescriptor;
214+
}
215+
216+
private static Class<?>[] getServerStreamRequestTypes(MethodDescriptor serverStreamMethodDescriptor) {
217+
Class<?>[] parameterClasses = serverStreamMethodDescriptor.getParameterClasses();
218+
if (parameterClasses.length == 0
219+
|| !StreamObserver.class.isAssignableFrom(parameterClasses[parameterClasses.length - 1])) {
220+
return null;
221+
}
222+
return Arrays.copyOf(parameterClasses, parameterClasses.length - 1);
223+
}
224+
225+
private static boolean isSameResponseType(
226+
MethodDescriptor unaryMethodDescriptor, MethodDescriptor serverStreamMethodDescriptor) {
227+
Type[] returnTypes = unaryMethodDescriptor.getReturnTypes();
228+
if (returnTypes.length == 0 || !(returnTypes[0] instanceof Class)) {
229+
return false;
230+
}
231+
Class<?> actualResponseType = serverStreamMethodDescriptor.getActualResponseType();
232+
if (actualResponseType == null) {
233+
return false;
234+
}
235+
return returnTypes[0].equals(actualResponseType);
145236
}
146237

147238
private static boolean isGeneric(String methodName) {
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.dubbo.rpc.protocol.tri;
18+
19+
import org.apache.dubbo.common.stream.StreamObserver;
20+
import org.apache.dubbo.remoting.http12.exception.UnimplementedException;
21+
import org.apache.dubbo.rpc.model.MethodDescriptor;
22+
import org.apache.dubbo.rpc.model.ReflectionServiceDescriptor;
23+
24+
import java.io.ByteArrayInputStream;
25+
import java.io.IOException;
26+
27+
import org.junit.jupiter.api.Test;
28+
29+
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.junit.jupiter.api.Assertions.assertThrows;
31+
32+
class DescriptorUtilsTest {
33+
34+
@Test
35+
void shouldSelectServerStreamMethodByWrapperArgTypesWhenUnaryMethodHasSameName() throws Exception {
36+
ReflectionServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(OverloadedService.class);
37+
TripleCustomerProtocolWrapper.TripleRequestWrapper request =
38+
TripleCustomerProtocolWrapper.TripleRequestWrapper.Builder.newBuilder()
39+
.setSerializeType("hessian4")
40+
.addArgTypes(String.class.getName())
41+
.addArgTypes(StreamObserver.class.getName())
42+
.build();
43+
44+
MethodDescriptor methodDescriptor = DescriptorUtils.findTripleMethodDescriptor(
45+
serviceDescriptor, "sync", new ByteArrayInputStream(request.toByteArray()));
46+
47+
assertEquals(MethodDescriptor.RpcType.SERVER_STREAM, methodDescriptor.getRpcType());
48+
}
49+
50+
@Test
51+
void shouldSelectUnaryMethodByWrapperArgTypesWhenServerStreamMethodHasSameName() throws Exception {
52+
ReflectionServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(OverloadedService.class);
53+
TripleCustomerProtocolWrapper.TripleRequestWrapper request =
54+
TripleCustomerProtocolWrapper.TripleRequestWrapper.Builder.newBuilder()
55+
.setSerializeType("hessian4")
56+
.addArgTypes(String.class.getName())
57+
.build();
58+
59+
MethodDescriptor methodDescriptor = DescriptorUtils.findTripleMethodDescriptor(
60+
serviceDescriptor, "sync", new ByteArrayInputStream(request.toByteArray()));
61+
62+
assertEquals(MethodDescriptor.RpcType.UNARY, methodDescriptor.getRpcType());
63+
}
64+
65+
@Test
66+
void shouldSelectNoArgMethodByEmptyWrapperArgTypesWhenMethodIsOverloaded() throws Exception {
67+
ReflectionServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(NoArgOverloadedService.class);
68+
TripleCustomerProtocolWrapper.TripleRequestWrapper request =
69+
TripleCustomerProtocolWrapper.TripleRequestWrapper.Builder.newBuilder()
70+
.setSerializeType("hessian4")
71+
.build();
72+
73+
MethodDescriptor methodDescriptor = DescriptorUtils.findTripleMethodDescriptor(
74+
serviceDescriptor, "overload", new ByteArrayInputStream(request.toByteArray()));
75+
76+
assertEquals(0, methodDescriptor.getParameterClasses().length);
77+
}
78+
79+
@Test
80+
void shouldRejectOverloadedMethodsWithoutMatchingWrapperArgTypes() {
81+
ReflectionServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(OverloadedService.class);
82+
TripleCustomerProtocolWrapper.TripleRequestWrapper request =
83+
TripleCustomerProtocolWrapper.TripleRequestWrapper.Builder.newBuilder()
84+
.setSerializeType("hessian4")
85+
.build();
86+
87+
assertThrows(
88+
UnimplementedException.class,
89+
() -> DescriptorUtils.findTripleMethodDescriptor(
90+
serviceDescriptor, "sync", new ByteArrayInputStream(request.toByteArray())));
91+
}
92+
93+
@Test
94+
void shouldFallbackToUnaryMethodForGeneratedPairWithoutWrapperArgTypes() throws Exception {
95+
ReflectionServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(GeneratedUnaryService.class);
96+
TripleCustomerProtocolWrapper.TripleRequestWrapper request =
97+
TripleCustomerProtocolWrapper.TripleRequestWrapper.Builder.newBuilder()
98+
.setSerializeType("hessian4")
99+
.build();
100+
101+
MethodDescriptor methodDescriptor = DescriptorUtils.findTripleMethodDescriptor(
102+
serviceDescriptor, "generated", new ByteArrayInputStream(request.toByteArray()));
103+
104+
assertEquals(MethodDescriptor.RpcType.UNARY, methodDescriptor.getRpcType());
105+
}
106+
107+
@Test
108+
void shouldRejectAmbiguousTwoMethodReflectionOverload() {
109+
ReflectionServiceDescriptor serviceDescriptor =
110+
new ReflectionServiceDescriptor(AmbiguousReflectionService.class);
111+
112+
assertThrows(
113+
UnimplementedException.class,
114+
() -> DescriptorUtils.findReflectionMethodDescriptor(serviceDescriptor, "ambiguous"));
115+
}
116+
117+
@Test
118+
void shouldSurfaceCorruptWrapperForGeneratedPair() {
119+
ReflectionServiceDescriptor serviceDescriptor = new ReflectionServiceDescriptor(GeneratedUnaryService.class);
120+
byte[] corruptWrapper = new byte[] {0x0A, 0x05, 'h', 'e'};
121+
122+
assertThrows(
123+
IOException.class,
124+
() -> DescriptorUtils.findTripleMethodDescriptor(
125+
serviceDescriptor, "generated", new ByteArrayInputStream(corruptWrapper)));
126+
}
127+
128+
private interface OverloadedService {
129+
130+
DataWrapper<String> sync(String value);
131+
132+
void sync(String value, StreamObserver<String> response);
133+
}
134+
135+
private interface NoArgOverloadedService {
136+
137+
String overload();
138+
139+
String overload(String value);
140+
}
141+
142+
private interface GeneratedUnaryService {
143+
144+
String generated(String value);
145+
146+
void generated(String value, StreamObserver<String> response);
147+
}
148+
149+
private interface AmbiguousReflectionService {
150+
151+
String ambiguous(String value);
152+
153+
void ambiguous(String value, StreamObserver<Integer> response);
154+
}
155+
}

0 commit comments

Comments
 (0)