Skip to content

Commit 2264b77

Browse files
wilkinsonaphilwebb
authored andcommitted
Consider Jackson 2 in GraphQL RSocket auto-configuration
See gh-47688
1 parent 50a73b8 commit 2264b77

File tree

4 files changed

+117
-4
lines changed

4 files changed

+117
-4
lines changed

module/spring-boot-graphql/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ dependencies {
2828
api(project(":core:spring-boot"))
2929
api("org.springframework.graphql:spring-graphql")
3030

31-
implementation(project(":module:spring-boot-jackson"))
3231

3332
optional(project(":core:spring-boot-autoconfigure"))
3433
optional(project(":module:spring-boot-http-converter"))
34+
optional(project(":module:spring-boot-jackson"))
35+
optional(project(":module:spring-boot-jackson2"))
3536
optional(project(":module:spring-boot-micrometer-observation"))
3637
optional(project(":module:spring-boot-rsocket"))
3738
optional(project(":module:spring-boot-security"))

module/spring-boot-graphql/src/main/java/org/springframework/boot/graphql/autoconfigure/rsocket/GraphQlRSocketAutoConfiguration.java

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.graphql.autoconfigure.rsocket;
1818

19+
import com.fasterxml.jackson.databind.ObjectMapper;
1920
import graphql.GraphQL;
2021
import io.rsocket.core.RSocketServer;
2122
import reactor.netty.http.server.HttpServer;
@@ -24,12 +25,17 @@
2425
import org.springframework.beans.factory.ObjectProvider;
2526
import org.springframework.boot.autoconfigure.AutoConfiguration;
2627
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
28+
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
2729
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2830
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2931
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
32+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
3033
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3134
import org.springframework.boot.graphql.autoconfigure.GraphQlAutoConfiguration;
3235
import org.springframework.context.annotation.Bean;
36+
import org.springframework.context.annotation.Conditional;
37+
import org.springframework.context.annotation.Configuration;
38+
import org.springframework.core.codec.Encoder;
3339
import org.springframework.graphql.ExecutionGraphQlService;
3440
import org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer;
3541
import org.springframework.graphql.execution.GraphQlSource;
@@ -46,7 +52,9 @@
4652
* @since 4.0.0
4753
*/
4854
@AutoConfiguration(after = GraphQlAutoConfiguration.class,
49-
afterName = "org.springframework.boot.rsocket.autoconfigure.RSocketMessagingAutoConfiguration")
55+
afterName = { "org.springframework.boot.rsocket.autoconfigure.RSocketMessagingAutoConfiguration",
56+
"org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration",
57+
"org.springframework.boot.jackson2.autoconfigure.Jackson2AutoConfiguration" })
5058
@ConditionalOnClass({ GraphQL.class, GraphQlSource.class, RSocketServer.class, HttpServer.class })
5159
@ConditionalOnBean({ RSocketMessageHandler.class, AnnotatedControllerConfigurer.class })
5260
@ConditionalOnProperty("spring.graphql.rsocket.mapping")
@@ -55,9 +63,9 @@ public final class GraphQlRSocketAutoConfiguration {
5563
@Bean
5664
@ConditionalOnMissingBean
5765
GraphQlRSocketHandler graphQlRSocketHandler(ExecutionGraphQlService graphQlService,
58-
ObjectProvider<RSocketGraphQlInterceptor> interceptors, JsonMapper jsonMapper) {
66+
ObjectProvider<RSocketGraphQlInterceptor> interceptors, JsonEncoderSupplier jsonEncoderSupplier) {
5967
return new GraphQlRSocketHandler(graphQlService, interceptors.orderedStream().toList(),
60-
new JacksonJsonEncoder(jsonMapper));
68+
jsonEncoderSupplier.jsonEncoder());
6169
}
6270

6371
@Bean
@@ -66,4 +74,55 @@ GraphQlRSocketController graphQlRSocketController(GraphQlRSocketHandler handler)
6674
return new GraphQlRSocketController(handler);
6775
}
6876

77+
interface JsonEncoderSupplier {
78+
79+
Encoder<?> jsonEncoder();
80+
81+
}
82+
83+
@Configuration(proxyBeanMethods = false)
84+
@ConditionalOnBean(JsonMapper.class)
85+
@ConditionalOnProperty(name = "spring.graphql.rsocket.preferred-json-mapper", havingValue = "jackson",
86+
matchIfMissing = true)
87+
static class JacksonJsonEncoderSupplierConfiguration {
88+
89+
@Bean
90+
JsonEncoderSupplier jacksonJsonEncoderSupplier(JsonMapper jsonMapper) {
91+
return () -> new JacksonJsonEncoder(jsonMapper);
92+
}
93+
94+
}
95+
96+
@Configuration(proxyBeanMethods = false)
97+
@ConditionalOnBean(ObjectMapper.class)
98+
@Conditional(NoJacksonOrJackson2Preferred.class)
99+
@Deprecated(since = "4.0.0", forRemoval = true)
100+
@SuppressWarnings("removal")
101+
static class Jackson2JsonEncoderSupplierConfiguration {
102+
103+
@Bean
104+
JsonEncoderSupplier jackson2JsonEncoderSupplier(ObjectMapper objectMapper) {
105+
return () -> new org.springframework.http.codec.json.Jackson2JsonEncoder(objectMapper);
106+
}
107+
108+
}
109+
110+
static class NoJacksonOrJackson2Preferred extends AnyNestedCondition {
111+
112+
NoJacksonOrJackson2Preferred() {
113+
super(ConfigurationPhase.PARSE_CONFIGURATION);
114+
}
115+
116+
@ConditionalOnMissingClass("tools.jackson.databind.json.JsonMapper")
117+
static class NoJackson {
118+
119+
}
120+
121+
@ConditionalOnProperty(name = "spring.graphql.rsocket.preferred-json-mapper", havingValue = "jackson2")
122+
static class Jackson2Preferred {
123+
124+
}
125+
126+
}
127+
69128
}

module/spring-boot-graphql/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
"since": "3.5.0"
3838
}
3939
},
40+
{
41+
"name": "spring.graphql.rsocket.preferred-json-mapper",
42+
"type": "java.lang.String",
43+
"defaultValue": "jackson",
44+
"description": "Preferred JSON mapper to use. By default, auto-detected according to the environment. Supported values are 'jackson' and 'jackson2' (deprecated)."
45+
},
4046
{
4147
"name": "spring.graphql.schema.file-extensions",
4248
"defaultValue": ".graphqls,.gqls"
@@ -95,6 +101,22 @@
95101
"name": "any"
96102
}
97103
]
104+
},
105+
{
106+
"name": "spring.graphql.rsocket.preferred-json-mapper",
107+
"values": [
108+
{
109+
"value": "jackson"
110+
},
111+
{
112+
"value": "jackson2"
113+
}
114+
],
115+
"providers": [
116+
{
117+
"name": "any"
118+
}
119+
]
98120
}
99121
]
100122
}

module/spring-boot-graphql/src/test/java/org/springframework/boot/graphql/autoconfigure/rsocket/GraphQlRSocketAutoConfigurationTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,22 @@
2222

2323
import graphql.schema.idl.TypeRuntimeWiring;
2424
import org.junit.jupiter.api.Test;
25+
import tools.jackson.databind.json.JsonMapper;
2526

2627
import org.springframework.boot.autoconfigure.AutoConfigurations;
2728
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
29+
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
2830
import org.springframework.boot.graphql.autoconfigure.GraphQlAutoConfiguration;
2931
import org.springframework.boot.graphql.autoconfigure.GraphQlTestDataFetchers;
3032
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
33+
import org.springframework.boot.logging.LogLevel;
3134
import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory;
3235
import org.springframework.boot.reactor.netty.NettyRouteProvider;
3336
import org.springframework.boot.rsocket.autoconfigure.RSocketMessagingAutoConfiguration;
3437
import org.springframework.boot.rsocket.autoconfigure.RSocketServerAutoConfiguration;
3538
import org.springframework.boot.rsocket.autoconfigure.RSocketStrategiesAutoConfiguration;
3639
import org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer;
40+
import org.springframework.boot.test.context.FilteredClassLoader;
3741
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
3842
import org.springframework.boot.testsupport.classpath.resources.WithResource;
3943
import org.springframework.boot.web.server.context.ServerPortInfoApplicationContextInitializer;
@@ -99,6 +103,33 @@ void simpleQueryShouldWorkWithWebSocketServer() {
99103
testWithRSocketWebSocket(this::assertThatSimpleQueryWorks);
100104
}
101105

106+
@Test
107+
void usesJacksonByDefault() {
108+
this.contextRunner.run((context) -> assertThat(context).hasBean("jacksonJsonEncoderSupplier"));
109+
}
110+
111+
@Test
112+
@Deprecated(since = "4.0.0", forRemoval = true)
113+
@SuppressWarnings("removal")
114+
void usesJackson2WhenItIsPreferred() {
115+
this.contextRunner.withPropertyValues("spring.graphql.rsocket.preferred-json-mapper=jackson2")
116+
.withConfiguration(AutoConfigurations
117+
.of(org.springframework.boot.jackson2.autoconfigure.Jackson2AutoConfiguration.class))
118+
.run((context) -> assertThat(context).hasBean("jackson2JsonEncoderSupplier"));
119+
}
120+
121+
@Test
122+
@Deprecated(since = "4.0.0", forRemoval = true)
123+
@SuppressWarnings("removal")
124+
void usesJackson2WhenJacksonIsAbsent() {
125+
this.contextRunner
126+
.withClassLoader(new FilteredClassLoader(Thread.currentThread().getContextClassLoader(), JsonMapper.class))
127+
.withConfiguration(AutoConfigurations
128+
.of(org.springframework.boot.jackson2.autoconfigure.Jackson2AutoConfiguration.class))
129+
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
130+
.run((context) -> assertThat(context).hasBean("jackson2JsonEncoderSupplier"));
131+
}
132+
102133
private void assertThatSimpleQueryWorks(RSocketGraphQlClient client) {
103134
String document = "{ bookById(id: \"book-1\"){ id name pageCount author } }";
104135
String bookName = client.document(document)

0 commit comments

Comments
 (0)