Add support for CBOR codecs in RSocket
This commit auto-configures CBOR (see https://cbor.io/) codecs in the RSocketStrategies, using Jackson binary format support. The required dependency is added to the rsocket starter. Binary codecs are well suited for RSocket payloads, so this codec is added first to the list of codecs (before the JSON one already supported). Closes gh-16830
This commit is contained in:
parent
08cb8368ef
commit
e8d39a1539
|
@ -42,6 +42,11 @@
|
|||
<artifactId>jackson-databind</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-cbor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.autoconfigure.rsocket;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.rsocket.RSocketFactory;
|
||||
|
||||
|
@ -34,8 +35,11 @@ import org.springframework.core.ReactiveAdapterRegistry;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.cbor.Jackson2CborDecoder;
|
||||
import org.springframework.http.codec.cbor.Jackson2CborEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
|
||||
/**
|
||||
|
@ -56,20 +60,41 @@ public class RSocketStrategiesAutoConfiguration {
|
|||
ObjectProvider<RSocketStrategiesCustomizer> customizers) {
|
||||
RSocketStrategies.Builder builder = RSocketStrategies.builder();
|
||||
builder.reactiveAdapterStrategy(ReactiveAdapterRegistry.getSharedInstance());
|
||||
customizers.stream().forEach((customizer) -> customizer.customize(builder));
|
||||
customizers.orderedStream()
|
||||
.forEach((customizer) -> customizer.customize(builder));
|
||||
builder.dataBufferFactory(
|
||||
new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
protected static class JacksonStrategyConfiguration {
|
||||
@ConditionalOnClass({ ObjectMapper.class, CBORFactory.class })
|
||||
protected static class JacksonCborStrategyConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(0)
|
||||
@ConditionalOnBean(Jackson2ObjectMapperBuilder.class)
|
||||
public RSocketStrategiesCustomizer jacksonCborStrategyCustomizer(
|
||||
Jackson2ObjectMapperBuilder builder) {
|
||||
return (strategy) -> {
|
||||
ObjectMapper objectMapper = builder.factory(new CBORFactory()).build();
|
||||
MediaType[] supportedTypes = new MediaType[] {
|
||||
new MediaType("application", "cbor") };
|
||||
strategy.decoder(new Jackson2CborDecoder(objectMapper, supportedTypes));
|
||||
strategy.encoder(new Jackson2CborEncoder(objectMapper, supportedTypes));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
protected static class JacksonJsonStrategyConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
@ConditionalOnBean(ObjectMapper.class)
|
||||
public RSocketStrategiesCustomizer jacksonStrategyCustomizer(
|
||||
public RSocketStrategiesCustomizer jacksonJsonStrategyCustomizer(
|
||||
ObjectMapper objectMapper) {
|
||||
return (strategy) -> {
|
||||
MediaType[] supportedTypes = new MediaType[] { MediaType.APPLICATION_JSON,
|
||||
|
|
|
@ -26,8 +26,11 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.http.codec.cbor.Jackson2CborDecoder;
|
||||
import org.springframework.http.codec.cbor.Jackson2CborEncoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -48,10 +51,16 @@ public class RSocketStrategiesAutoConfigurationTests {
|
|||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).getBeans(RSocketStrategies.class).hasSize(1);
|
||||
RSocketStrategies strategies = context.getBean(RSocketStrategies.class);
|
||||
assertThat(strategies.decoders()).hasSize(1)
|
||||
.hasOnlyElementsOfType(Jackson2JsonDecoder.class);
|
||||
assertThat(strategies.encoders()).hasSize(1)
|
||||
.hasOnlyElementsOfType(Jackson2JsonEncoder.class);
|
||||
assertThat(strategies.decoders()).hasSize(2);
|
||||
assertThat(strategies.decoders().get(0))
|
||||
.isInstanceOf(Jackson2CborDecoder.class);
|
||||
assertThat(strategies.decoders().get(1))
|
||||
.isInstanceOf(Jackson2JsonDecoder.class);
|
||||
assertThat(strategies.encoders()).hasSize(2);
|
||||
assertThat(strategies.encoders().get(0))
|
||||
.isInstanceOf(Jackson2CborEncoder.class);
|
||||
assertThat(strategies.encoders().get(1))
|
||||
.isInstanceOf(Jackson2JsonEncoder.class);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,9 +80,9 @@ public class RSocketStrategiesAutoConfigurationTests {
|
|||
assertThat(context).getBeans(RSocketStrategies.class).hasSize(1);
|
||||
RSocketStrategies strategies = context
|
||||
.getBean(RSocketStrategies.class);
|
||||
assertThat(strategies.decoders()).hasSize(2)
|
||||
assertThat(strategies.decoders()).hasSize(3)
|
||||
.hasAtLeastOneElementOfType(StringDecoder.class);
|
||||
assertThat(strategies.encoders()).hasSize(2)
|
||||
assertThat(strategies.encoders()).hasSize(3)
|
||||
.hasAtLeastOneElementOfType(CharSequenceEncoder.class);
|
||||
});
|
||||
}
|
||||
|
@ -86,6 +95,11 @@ public class RSocketStrategiesAutoConfigurationTests {
|
|||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
|
||||
return new Jackson2ObjectMapperBuilder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
@ -135,6 +135,11 @@
|
|||
<artifactId>jackson-datatype-joda</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-cbor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
|
|
|
@ -3483,6 +3483,22 @@ The following code shows a typical `@Controller`:
|
|||
}
|
||||
----
|
||||
|
||||
[[boot-features-rsocket-strategies-auto-configuration]]
|
||||
=== RSocket Strategies Auto-configuration
|
||||
Spring Boot auto-configures an `RSocketStrategies` bean that provides all the required
|
||||
infrastructure for encoding and decoding RSocket payloads. By default, the
|
||||
auto-configuration will try to configure the following (in order):
|
||||
|
||||
1. https://cbor.io/[CBOR] codecs with Jackson
|
||||
2. JSON codecs with Jackson
|
||||
|
||||
The `spring-boot-starter-rsocket` Starter provides both dependencies.
|
||||
|
||||
Developers can customize the `RSocketStrategies` component by creating beans that
|
||||
implement the `RSocketStrategiesCustomizer` interface. Note that their `@Order` is
|
||||
important, as it determines the order of codecs.
|
||||
|
||||
|
||||
[[boot-features-rsocket-server-auto-configuration]]
|
||||
=== RSocket server Auto-configuration
|
||||
Spring Boot provides auto-configuration for RSocket servers. The required dependencies
|
||||
|
|
|
@ -54,5 +54,9 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-cbor</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
Loading…
Reference in New Issue