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
This commit is contained in:
@@ -35,6 +35,7 @@ import org.reactivestreams.Subscription;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
|
||||
import org.springframework.graphql.server.WebGraphQlHandler;
|
||||
import org.springframework.graphql.server.WebGraphQlResponse;
|
||||
import org.springframework.graphql.server.WebSocketGraphQlInterceptor;
|
||||
@@ -59,6 +60,7 @@ import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RegisterReflectionForBinding(GraphQlWebSocketMessage.class)
|
||||
public class GraphQlWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GraphQlWebSocketHandler.class);
|
||||
|
||||
@@ -46,6 +46,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
|
||||
import org.springframework.graphql.execution.ErrorType;
|
||||
import org.springframework.graphql.execution.SubscriptionPublisherException;
|
||||
import org.springframework.graphql.server.WebGraphQlHandler;
|
||||
@@ -84,6 +85,7 @@ import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@RegisterReflectionForBinding(GraphQlWebSocketMessage.class)
|
||||
public class GraphQlWebSocketHandler extends TextWebSocketHandler implements SubProtocolCapable {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GraphQlWebSocketHandler.class);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"name":"org.springframework.graphql.server.support.GraphQlWebSocketMessage",
|
||||
"allDeclaredFields":true,
|
||||
"allDeclaredMethods":true,
|
||||
"allDeclaredConstructors":true,
|
||||
"condition": {
|
||||
"typeReachable": "org.springframework.graphql.client.WebSocketGraphQlTransport"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -32,6 +32,10 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
@@ -353,6 +357,23 @@ public class GraphQlWebSocketHandlerTests extends WebSocketHandlerTestSupport {
|
||||
.verify(TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerBindingReflectionOnWebSocketMessage() {
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
new ReflectiveRuntimeHintsRegistrar().registerRuntimeHints(runtimeHints, GraphQlWebSocketHandler.class);
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection.onType(GraphQlWebSocketMessage.class)).accepts(runtimeHints);
|
||||
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "id")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getId")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setId")).accepts(runtimeHints);
|
||||
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "type")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getType")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setType")).accepts(runtimeHints);
|
||||
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "payload")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getPayload")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setPayload")).accepts(runtimeHints);
|
||||
}
|
||||
|
||||
private TestWebSocketSession handle(Flux<WebSocketMessage> input, WebGraphQlInterceptor... interceptors) {
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
|
||||
initHandler(interceptors),
|
||||
|
||||
@@ -36,6 +36,10 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
import org.springframework.graphql.execution.ErrorType;
|
||||
@@ -406,6 +410,23 @@ public class GraphQlWebSocketHandlerTests extends WebSocketHandlerTestSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerBindingReflectionOnWebSocketMessage() {
|
||||
RuntimeHints runtimeHints = new RuntimeHints();
|
||||
new ReflectiveRuntimeHintsRegistrar().registerRuntimeHints(runtimeHints, GraphQlWebSocketHandler.class);
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection.onType(GraphQlWebSocketMessage.class)).accepts(runtimeHints);
|
||||
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "id")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getId")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setId")).accepts(runtimeHints);
|
||||
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "type")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getType")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setType")).accepts(runtimeHints);
|
||||
assertThat(reflection.onField(GraphQlWebSocketMessage.class, "payload")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "getPayload")).accepts(runtimeHints);
|
||||
assertThat(reflection.onMethod(GraphQlWebSocketMessage.class, "setPayload")).accepts(runtimeHints);
|
||||
}
|
||||
|
||||
private void handle(GraphQlWebSocketHandler handler, TextMessage... textMessages) throws Exception {
|
||||
handler.afterConnectionEstablished(this.session);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user