Skip to content

Commit eb9b1f3

Browse files
committed
Add reflection hint for GraphQlWebSocketMessage
Prior to this commit, GraphQL applications using the WebSocket transport would fail with an `java.lang.IllegalArgumentException: No JSON Encoder` as the JSON codec was reflecting on the `GraphQlWebSocketMessage` to check that it can handle this type, during startup time. `GraphQlWebSocketMessage` is the message type being used for sending and receiving GraphQL requests. This commit registers binding reflection on this type as soon as a `GraphQlWebSocketHandler` (mvc or webflux) is registered as a bean in the application. This type is also used on the client side in the client `WebSocketGraphQlTransport`. Because clients cannot be reliably detected here in the application context or via classpath checks, this commit also adds a JSON metadata file that adds reflection on the type, if and only if the `WebSocketGraphQlTransport` is reachable by the static analysis performed by GraalVM. Fixes gh-560
1 parent a43c928 commit eb9b1f3

File tree

5 files changed

+57
-0
lines changed

5 files changed

+57
-0
lines changed

spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import reactor.core.publisher.Flux;
3636
import reactor.core.publisher.Mono;
3737

38+
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
3839
import org.springframework.graphql.server.WebGraphQlHandler;
3940
import org.springframework.graphql.server.WebGraphQlResponse;
4041
import org.springframework.graphql.server.WebSocketGraphQlInterceptor;
@@ -59,6 +60,7 @@
5960
* @author Rossen Stoyanchev
6061
* @since 1.0.0
6162
*/
63+
@RegisterReflectionForBinding(GraphQlWebSocketMessage.class)
6264
public class GraphQlWebSocketHandler implements WebSocketHandler {
6365

6466
private static final Log logger = LogFactory.getLog(GraphQlWebSocketHandler.class);

spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import reactor.core.scheduler.Scheduler;
4747
import reactor.core.scheduler.Schedulers;
4848

49+
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
4950
import org.springframework.graphql.execution.ErrorType;
5051
import org.springframework.graphql.execution.SubscriptionPublisherException;
5152
import org.springframework.graphql.server.WebGraphQlHandler;
@@ -84,6 +85,7 @@
8485
* @author Rossen Stoyanchev
8586
* @since 1.0.0
8687
*/
88+
@RegisterReflectionForBinding(GraphQlWebSocketMessage.class)
8789
public class GraphQlWebSocketHandler extends TextWebSocketHandler implements SubProtocolCapable {
8890

8991
private static final Log logger = LogFactory.getLog(GraphQlWebSocketHandler.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[
2+
{
3+
"name":"org.springframework.graphql.server.support.GraphQlWebSocketMessage",
4+
"allDeclaredFields":true,
5+
"allDeclaredMethods":true,
6+
"allDeclaredConstructors":true,
7+
"condition": {
8+
"typeReachable": "org.springframework.graphql.client.WebSocketGraphQlTransport"
9+
}
10+
}
11+
]

spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandlerTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
import reactor.core.publisher.Sinks;
3333
import reactor.test.StepVerifier;
3434

35+
import org.springframework.aot.hint.RuntimeHints;
36+
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
37+
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
38+
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
3539
import org.springframework.core.ResolvableType;
3640
import org.springframework.core.io.buffer.DataBuffer;
3741
import org.springframework.core.io.buffer.DataBufferUtils;
@@ -353,6 +357,23 @@ void subscriptionErrorPayloadIsArray() {
353357
.verify(TIMEOUT);
354358
}
355359

360+
@Test
361+
void registerBindingReflectionOnWebSocketMessage() {
362+
RuntimeHints runtimeHints = new RuntimeHints();
363+
new ReflectiveRuntimeHintsRegistrar().registerRuntimeHints(runtimeHints, GraphQlWebSocketHandler.class);
364+
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
365+
assertThat(reflection.onType(GraphQlWebSocketMessage.class)).accepts(runtimeHints);
366+
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "id")).accepts(runtimeHints);
367+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getId")).accepts(runtimeHints);
368+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setId")).accepts(runtimeHints);
369+
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "type")).accepts(runtimeHints);
370+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getType")).accepts(runtimeHints);
371+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setType")).accepts(runtimeHints);
372+
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "payload")).accepts(runtimeHints);
373+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getPayload")).accepts(runtimeHints);
374+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setPayload")).accepts(runtimeHints);
375+
}
376+
356377
private TestWebSocketSession handle(Flux<WebSocketMessage> input, WebGraphQlInterceptor... interceptors) {
357378
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
358379
initHandler(interceptors),

spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandlerTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
import reactor.core.publisher.Mono;
3737
import reactor.test.StepVerifier;
3838

39+
import org.springframework.aot.hint.RuntimeHints;
40+
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
41+
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
42+
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
3943
import org.springframework.graphql.GraphQlSetup;
4044
import org.springframework.graphql.TestThreadLocalAccessor;
4145
import org.springframework.graphql.execution.ErrorType;
@@ -406,6 +410,23 @@ void contextPropagation() throws Exception {
406410
}
407411
}
408412

413+
@Test
414+
void registerBindingReflectionOnWebSocketMessage() {
415+
RuntimeHints runtimeHints = new RuntimeHints();
416+
new ReflectiveRuntimeHintsRegistrar().registerRuntimeHints(runtimeHints, GraphQlWebSocketHandler.class);
417+
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
418+
assertThat(reflection.onType(GraphQlWebSocketMessage.class)).accepts(runtimeHints);
419+
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "id")).accepts(runtimeHints);
420+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getId")).accepts(runtimeHints);
421+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setId")).accepts(runtimeHints);
422+
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "type")).accepts(runtimeHints);
423+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getType")).accepts(runtimeHints);
424+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setType")).accepts(runtimeHints);
425+
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "payload")).accepts(runtimeHints);
426+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getPayload")).accepts(runtimeHints);
427+
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setPayload")).accepts(runtimeHints);
428+
}
429+
409430
private void handle(GraphQlWebSocketHandler handler, TextMessage... textMessages) throws Exception {
410431
handler.afterConnectionEstablished(this.session);
411432

0 commit comments

Comments
 (0)