Skip to content

Commit a7f34a5

Browse files
authored
Bugfix: don't throw away the container class of parameterized types (#950)
1 parent 3d34e2b commit a7f34a5

File tree

10 files changed

+102
-24
lines changed

10 files changed

+102
-24
lines changed

services-api/src/main/java/io/scalecube/services/Reflect.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.HashSet;
2626
import java.util.Map;
2727
import java.util.Set;
28+
import java.util.concurrent.Future;
2829
import java.util.stream.Collectors;
2930
import java.util.stream.Stream;
3031
import org.reactivestreams.Publisher;
@@ -51,8 +52,8 @@ public static Type parameterizedReturnType(Method method) {
5152
}
5253

5354
Type type = method.getGenericReturnType();
54-
if (type instanceof ParameterizedType) {
55-
Type actualReturnType = ((ParameterizedType) type).getActualTypeArguments()[0];
55+
if (type instanceof ParameterizedType pt) {
56+
Type actualReturnType = isAsyncContainer(type) ? pt.getActualTypeArguments()[0] : pt;
5657

5758
if (ServiceMessage.class.equals(actualReturnType)) {
5859
return Object.class;
@@ -92,17 +93,13 @@ public static boolean isReturnTypeServiceMessage(Method method) {
9293
* @param method in inspection
9394
* @return type of method parameter
9495
*/
95-
public static Class<?> requestType(Method method) {
96+
public static Type requestType(Method method) {
9697
if (method.getParameterTypes().length > 0) {
9798
if (method.isAnnotationPresent(RequestType.class)) {
9899
return method.getAnnotation(RequestType.class).value();
99100
} else {
100101
if (method.getGenericParameterTypes()[0] instanceof ParameterizedType) {
101-
try {
102-
return Class.forName(parameterizedRequestType(method).getTypeName());
103-
} catch (ClassNotFoundException e) {
104-
return Object.class;
105-
}
102+
return parameterizedRequestType(method);
106103
} else if (ServiceMessage.class.equals(method.getParameterTypes()[0])) {
107104
return Object.class;
108105
} else {
@@ -146,8 +143,8 @@ public static boolean isRequestTypeServiceMessage(Method method) {
146143
public static Type parameterizedType(Object object) {
147144
if (object != null) {
148145
Type type = object.getClass().getGenericSuperclass();
149-
if (type instanceof ParameterizedType) {
150-
return ((ParameterizedType) type).getActualTypeArguments()[0];
146+
if (type instanceof ParameterizedType pt) {
147+
return isAsyncContainer(type) ? pt.getActualTypeArguments()[0] : pt;
151148
}
152149
}
153150
return Object.class;
@@ -161,14 +158,36 @@ public static Type parameterizedType(Object object) {
161158
public static Type parameterizedRequestType(Method method) {
162159
if (method != null && method.getGenericParameterTypes().length > 0) {
163160
Type type = method.getGenericParameterTypes()[0];
164-
if (type instanceof ParameterizedType) {
165-
return ((ParameterizedType) type).getActualTypeArguments()[0];
161+
if (type instanceof ParameterizedType pt) {
162+
return isAsyncContainer(type) ? pt.getActualTypeArguments()[0] : pt;
166163
}
167164
}
168165

169166
return Object.class;
170167
}
171168

169+
private static boolean isAsyncContainer(Type type) {
170+
Class<?> raw = toClass(type);
171+
172+
if (raw == null) {
173+
return false;
174+
}
175+
176+
return Publisher.class.isAssignableFrom(raw)
177+
|| Stream.class.isAssignableFrom(raw)
178+
|| Future.class.isAssignableFrom(raw);
179+
}
180+
181+
public static Class<?> toClass(Type type) {
182+
if (type instanceof ParameterizedType pt) {
183+
return (Class<?>) pt.getRawType();
184+
}
185+
if (type instanceof Class<?> c) {
186+
return c;
187+
}
188+
return null;
189+
}
190+
172191
/**
173192
* Extracts service name from service interface.
174193
*

services-api/src/main/java/io/scalecube/services/methods/MethodInfo.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.scalecube.services.methods;
22

3+
import static io.scalecube.services.Reflect.toClass;
34
import static io.scalecube.services.api.DynamicQualifier.isDynamicQualifier;
45

56
import io.scalecube.services.CommunicationMode;
@@ -23,7 +24,7 @@ public class MethodInfo {
2324
private final boolean isReturnTypeServiceMessage;
2425
private final CommunicationMode communicationMode;
2526
private final int parameterCount;
26-
private final Class<?> requestType;
27+
private final Type requestType;
2728
private final boolean isRequestTypeServiceMessage;
2829
private final Secured secured;
2930
private final Scheduler scheduler;
@@ -53,7 +54,7 @@ public MethodInfo(
5354
boolean isReturnTypeServiceMessage,
5455
CommunicationMode communicationMode,
5556
int parameterCount,
56-
Class<?> requestType,
57+
Type requestType,
5758
boolean isRequestTypeServiceMessage,
5859
Secured secured,
5960
Scheduler scheduler,
@@ -115,10 +116,10 @@ public boolean isRequestTypeServiceMessage() {
115116
}
116117

117118
public boolean isRequestTypeVoid() {
118-
return requestType.isAssignableFrom(Void.TYPE);
119+
return Void.TYPE.isAssignableFrom(toClass(requestType));
119120
}
120121

121-
public Class<?> requestType() {
122+
public Type requestType() {
122123
return requestType;
123124
}
124125

services-api/src/main/java/io/scalecube/services/transport/api/ServiceMessageDataDecoder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.scalecube.services.transport.api;
22

33
import io.scalecube.services.api.ServiceMessage;
4+
import java.lang.reflect.Type;
45
import java.util.ServiceLoader;
56
import java.util.stream.StreamSupport;
67

@@ -11,7 +12,7 @@ public interface ServiceMessageDataDecoder {
1112
.findFirst()
1213
.orElse(null);
1314

14-
ServiceMessage decodeData(ServiceMessage message, Class<?> dataType);
15+
ServiceMessage decodeData(ServiceMessage message, Type type);
1516

16-
ServiceMessage copyData(ServiceMessage message, Class<?> dataType);
17+
ServiceMessage copyData(ServiceMessage message, Type type);
1718
}

services-api/src/test/java/io/scalecube/services/methods/ServiceMethodInvokerTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.scalecube.services.exceptions.DefaultErrorMapper;
1818
import io.scalecube.services.transport.api.ServiceMessageDataDecoder;
1919
import java.lang.reflect.Method;
20+
import java.lang.reflect.Type;
2021
import java.util.List;
2122
import java.util.function.Consumer;
2223
import java.util.stream.Stream;
@@ -39,12 +40,12 @@ class ServiceMethodInvokerTest {
3940
private final ServiceMessageDataDecoder dataDecoder =
4041
new ServiceMessageDataDecoder() {
4142
@Override
42-
public ServiceMessage decodeData(ServiceMessage message, Class<?> dataType) {
43+
public ServiceMessage decodeData(ServiceMessage message, Type type) {
4344
return message;
4445
}
4546

4647
@Override
47-
public ServiceMessage copyData(ServiceMessage message, Class<?> dataType) {
48+
public ServiceMessage copyData(ServiceMessage message, Type type) {
4849
return message;
4950
}
5051
};

services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/ServiceMessageByteBufDataDecoder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
import io.scalecube.services.api.ServiceMessage;
44
import io.scalecube.services.transport.api.ServiceMessageDataDecoder;
5+
import java.lang.reflect.Type;
56

67
public class ServiceMessageByteBufDataDecoder implements ServiceMessageDataDecoder {
78

89
@Override
9-
public ServiceMessage decodeData(ServiceMessage message, Class<?> dataType) {
10+
public ServiceMessage decodeData(ServiceMessage message, Type dataType) {
1011
return ServiceMessageCodec.decodeData(message, dataType, false);
1112
}
1213

1314
@Override
14-
public ServiceMessage copyData(ServiceMessage message, Class<?> dataType) {
15+
public ServiceMessage copyData(ServiceMessage message, Type dataType) {
1516
return ServiceMessageCodec.decodeData(message, dataType, true);
1617
}
1718
}

services/src/main/java/io/scalecube/services/Microservices.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.scalecube.services.transport.api.ServerTransport;
2121
import io.scalecube.services.transport.api.ServiceMessageDataDecoder;
2222
import io.scalecube.services.transport.api.ServiceTransport;
23+
import java.lang.reflect.Type;
2324
import java.net.InetAddress;
2425
import java.net.UnknownHostException;
2526
import java.time.Duration;
@@ -766,12 +767,12 @@ private Context conclude() {
766767
.orElse(
767768
new ServiceMessageDataDecoder() {
768769
@Override
769-
public ServiceMessage decodeData(ServiceMessage message, Class<?> dataType) {
770+
public ServiceMessage decodeData(ServiceMessage message, Type type) {
770771
return message;
771772
}
772773

773774
@Override
774-
public ServiceMessage copyData(ServiceMessage message, Class<?> dataType) {
775+
public ServiceMessage copyData(ServiceMessage message, Type type) {
775776
return message;
776777
}
777778
});

services/src/test/java/io/scalecube/services/ServiceLocalTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import io.scalecube.services.sut.GreetingRequest;
1111
import io.scalecube.services.sut.GreetingResponse;
1212
import io.scalecube.services.sut.GreetingService;
13+
import io.scalecube.services.sut.GreetingService.Base;
14+
import io.scalecube.services.sut.GreetingService.MyPojo;
1315
import io.scalecube.services.sut.GreetingServiceImpl;
1416
import java.time.Duration;
1517
import java.util.concurrent.atomic.AtomicReference;
@@ -395,4 +397,19 @@ public void test_dynamic_qualifier() {
395397
private static GreetingService createProxy(Microservices gateway) {
396398
return gateway.call().api(GreetingService.class); // create proxy for GreetingService API
397399
}
400+
401+
@Test
402+
public void test_generics_in_request() {
403+
GreetingService service = createProxy(microservices);
404+
405+
final var pojo = new MyPojo("Joe", "NY");
406+
// call the service.
407+
final Mono<MyPojo> response =
408+
service.greetingsWithGenerics(new Base<MyPojo>().object(pojo).format("plain"));
409+
var result = response.block(TIMEOUT.plusMillis(500));
410+
411+
// print the greeting.
412+
System.out.println("test_generics_in_request : " + result);
413+
assertEquals(pojo, result);
414+
}
398415
}

services/src/test/java/io/scalecube/services/ServiceRemoteTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.scalecube.services.sut.GreetingRequest;
1818
import io.scalecube.services.sut.GreetingResponse;
1919
import io.scalecube.services.sut.GreetingService;
20+
import io.scalecube.services.sut.GreetingService.MyPojo;
2021
import io.scalecube.services.sut.GreetingServiceImpl;
2122
import io.scalecube.services.transport.rsocket.RSocketServiceTransport;
2223
import io.scalecube.transport.netty.websocket.WebsocketTransportFactory;
@@ -572,6 +573,17 @@ public void test_dynamic_qualifier() {
572573
.verifyComplete();
573574
}
574575

576+
@Test
577+
public void test_generics_in_request() {
578+
final var service = gateway.call().api(GreetingService.class);
579+
final var pojo = new GreetingService.MyPojo("Joe", "NY");
580+
581+
StepVerifier.create(
582+
service.greetingsWithGenerics(new GreetingService.Base<MyPojo>().object(pojo)))
583+
.assertNext(result -> assertEquals(pojo, result))
584+
.verifyComplete();
585+
}
586+
575587
private GreetingService createProxy() {
576588
return gateway.call().api(GreetingService.class); // create proxy for GreetingService API
577589
}

services/src/test/java/io/scalecube/services/sut/GreetingService.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,24 @@ Flux<ServiceMessage> bidiGreetingIllegalArgumentExceptionMessage(
103103

104104
@ServiceMethod("hello/:someVar")
105105
Mono<String> helloDynamicQualifier(Long value);
106+
107+
public static class Base<T> {
108+
T obj;
109+
String format;
110+
111+
public Base object(T obj) {
112+
this.obj = obj;
113+
return this;
114+
}
115+
116+
public Base format(String format) {
117+
this.format = format;
118+
return this;
119+
}
120+
}
121+
122+
@ServiceMethod
123+
Mono<MyPojo> greetingsWithGenerics(Base<MyPojo> request);
124+
125+
public record MyPojo(String name, String address) {}
106126
}

services/src/test/java/io/scalecube/services/sut/GreetingServiceImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,9 @@ public Mono<String> helloDynamicQualifier(Long value) {
207207
return RequestContext.deferContextual()
208208
.map(context -> context.pathParams().getString("someVar") + "@" + value);
209209
}
210+
211+
@Override
212+
public Mono<MyPojo> greetingsWithGenerics(Base<MyPojo> request) {
213+
return Mono.just(request.obj);
214+
}
210215
}

0 commit comments

Comments
 (0)