MetadataExtractor refactoring
Remove RSocketStrategies argument from the contract to avoid having to pass them every time especially by application components, like an implementation of a Spring Security matcher. Decouple DefaultMetadataExtractor from RSocketStrategies in favor of a decoders property and an internal DataBufferFactory, which does not need to be the shared one as we're only wrapping ByteBufs.
This commit is contained in:
parent
7c45381600
commit
fab0a5d504
|
|
@ -15,22 +15,26 @@
|
|||
*/
|
||||
package org.springframework.messaging.rsocket;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.rsocket.Payload;
|
||||
import io.rsocket.metadata.CompositeMetadata;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.codec.Decoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.NettyDataBuffer;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
|
|
@ -47,15 +51,53 @@ import org.springframework.util.MimeType;
|
|||
*/
|
||||
public class DefaultMetadataExtractor implements MetadataExtractor {
|
||||
|
||||
private final Map<String, EntryProcessor<?>> entryProcessors = new HashMap<>();
|
||||
private final List<Decoder<?>> decoders = new ArrayList<>();
|
||||
|
||||
private final Map<String, MetadataProcessor<?>> processors = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor with {@link RSocketStrategies}.
|
||||
* Configure the decoders to use for de-serializing metadata entries.
|
||||
* <p>By default this is not set.
|
||||
* <p>When this extractor is passed into {@link RSocketStrategies.Builder} or
|
||||
* {@link org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler
|
||||
* RSocketMessageHandler}, the decoders may be left not set, and they will
|
||||
* be initialized from the decoders already configured there.
|
||||
*/
|
||||
public DefaultMetadataExtractor() {
|
||||
// TODO: remove when rsocket-core API available
|
||||
metadataToExtract(MetadataExtractor.ROUTING, String.class, ROUTE_KEY);
|
||||
public void setDecoders(List<? extends Decoder<?>> decoders) {
|
||||
this.decoders.clear();
|
||||
if (!decoders.isEmpty()) {
|
||||
this.decoders.addAll(decoders);
|
||||
updateProcessors();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void updateProcessors() {
|
||||
for (MetadataProcessor<?> info : this.processors.values()) {
|
||||
Decoder<T> decoder = decoderFor(info.mimeType(), info.targetType());
|
||||
Assert.isTrue(decoder != null, "No decoder for " + info);
|
||||
info = ((MetadataProcessor<T>) info).setDecoder(decoder);
|
||||
this.processors.put(info.mimeType().toString(), info);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Decoder<T> decoderFor(MimeType mimeType, ResolvableType type) {
|
||||
for (Decoder<?> decoder : this.decoders) {
|
||||
if (decoder.canDecode(type, mimeType)) {
|
||||
return (Decoder<T>) decoder;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link #setDecoders(List) configured} decoders.
|
||||
*/
|
||||
public List<? extends Decoder<?>> getDecoders() {
|
||||
return this.decoders;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -97,11 +139,9 @@ public class DefaultMetadataExtractor implements MetadataExtractor {
|
|||
* @param <T> the target value type
|
||||
*/
|
||||
public <T> void metadataToExtract(
|
||||
MimeType mimeType, Class<T> targetType,
|
||||
BiConsumer<T, Map<String, Object>> mapper) {
|
||||
MimeType mimeType, Class<T> targetType, BiConsumer<T, Map<String, Object>> mapper) {
|
||||
|
||||
EntryProcessor<T> spec = new EntryProcessor<>(mimeType, targetType, mapper);
|
||||
this.entryProcessors.put(mimeType.toString(), spec);
|
||||
metadataToExtract(mimeType, mapper, ResolvableType.forClass(targetType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,45 +157,52 @@ public class DefaultMetadataExtractor implements MetadataExtractor {
|
|||
MimeType mimeType, ParameterizedTypeReference<T> targetType,
|
||||
BiConsumer<T, Map<String, Object>> mapper) {
|
||||
|
||||
EntryProcessor<T> spec = new EntryProcessor<>(mimeType, targetType, mapper);
|
||||
this.entryProcessors.put(mimeType.toString(), spec);
|
||||
metadataToExtract(mimeType, mapper, ResolvableType.forType(targetType));
|
||||
}
|
||||
|
||||
private <T> void metadataToExtract(
|
||||
MimeType mimeType, BiConsumer<T, Map<String, Object>> mapper, ResolvableType elementType) {
|
||||
|
||||
Decoder<T> decoder = decoderFor(mimeType, elementType);
|
||||
Assert.isTrue(this.decoders.isEmpty() || decoder != null, () -> "No decoder for " + mimeType);
|
||||
MetadataProcessor<T> info = new MetadataProcessor<>(mimeType, elementType, mapper, decoder);
|
||||
this.processors.put(mimeType.toString(), info);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> extract(Payload payload, MimeType metadataMimeType, RSocketStrategies strategies) {
|
||||
public Map<String, Object> extract(Payload payload, MimeType metadataMimeType) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
if (metadataMimeType.equals(COMPOSITE_METADATA)) {
|
||||
for (CompositeMetadata.Entry entry : new CompositeMetadata(payload.metadata(), false)) {
|
||||
processEntry(entry.getContent(), entry.getMimeType(), result, strategies);
|
||||
processEntry(entry.getContent(), entry.getMimeType(), result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
processEntry(payload.metadata(), metadataMimeType.toString(), result, strategies);
|
||||
processEntry(payload.metadata(), metadataMimeType.toString(), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void processEntry(ByteBuf content,
|
||||
@Nullable String mimeType, Map<String, Object> result, RSocketStrategies strategies) {
|
||||
|
||||
EntryProcessor<?> entryProcessor = this.entryProcessors.get(mimeType);
|
||||
if (entryProcessor != null) {
|
||||
content.retain();
|
||||
entryProcessor.process(content, result, strategies);
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void processEntry(ByteBuf content, @Nullable String mimeType, Map<String, Object> result) {
|
||||
MetadataProcessor<T> info = (MetadataProcessor<T>) this.processors.get(mimeType);
|
||||
if (info != null) {
|
||||
info.process(content, result);
|
||||
return;
|
||||
}
|
||||
if (MetadataExtractor.ROUTING.toString().equals(mimeType)) {
|
||||
// TODO: use rsocket-core API when available
|
||||
result.put(MetadataExtractor.ROUTE_KEY, content.toString(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helps to decode a metadata entry and add the resulting value to the
|
||||
* output map.
|
||||
*/
|
||||
private class EntryProcessor<T> {
|
||||
private static class MetadataProcessor<T> {
|
||||
|
||||
private final static NettyDataBufferFactory bufferFactory =
|
||||
new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT);
|
||||
|
||||
|
||||
private final MimeType mimeType;
|
||||
|
||||
|
|
@ -163,41 +210,54 @@ public class DefaultMetadataExtractor implements MetadataExtractor {
|
|||
|
||||
private final BiConsumer<T, Map<String, Object>> accumulator;
|
||||
|
||||
@Nullable
|
||||
private final Decoder<T> decoder;
|
||||
|
||||
public EntryProcessor(
|
||||
MimeType mimeType, Class<T> targetType,
|
||||
BiConsumer<T, Map<String, Object>> accumulator) {
|
||||
|
||||
this(mimeType, ResolvableType.forClass(targetType), accumulator);
|
||||
}
|
||||
|
||||
public EntryProcessor(
|
||||
MimeType mimeType, ParameterizedTypeReference<T> targetType,
|
||||
BiConsumer<T, Map<String, Object>> accumulator) {
|
||||
|
||||
this(mimeType, ResolvableType.forType(targetType), accumulator);
|
||||
}
|
||||
|
||||
private EntryProcessor(
|
||||
MimeType mimeType, ResolvableType targetType,
|
||||
BiConsumer<T, Map<String, Object>> accumulator) {
|
||||
MetadataProcessor(MimeType mimeType, ResolvableType targetType,
|
||||
BiConsumer<T, Map<String, Object>> accumulator, @Nullable Decoder<T> decoder) {
|
||||
|
||||
this.mimeType = mimeType;
|
||||
this.targetType = targetType;
|
||||
this.accumulator = accumulator;
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
MetadataProcessor(MetadataProcessor<T> other, Decoder<T> decoder) {
|
||||
this.mimeType = other.mimeType;
|
||||
this.targetType = other.targetType;
|
||||
this.accumulator = other.accumulator;
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
|
||||
public void process(ByteBuf byteBuf, Map<String, Object> result, RSocketStrategies strategies) {
|
||||
DataBufferFactory factory = strategies.dataBufferFactory();
|
||||
DataBuffer buffer = factory instanceof NettyDataBufferFactory ?
|
||||
((NettyDataBufferFactory) factory).wrap(byteBuf) :
|
||||
factory.wrap(byteBuf.nioBuffer());
|
||||
public MimeType mimeType() {
|
||||
return this.mimeType;
|
||||
}
|
||||
|
||||
Decoder<T> decoder = strategies.decoder(this.targetType, this.mimeType);
|
||||
T value = decoder.decode(buffer, this.targetType, this.mimeType, Collections.emptyMap());
|
||||
public ResolvableType targetType() {
|
||||
return this.targetType;
|
||||
}
|
||||
|
||||
public MetadataProcessor<T> setDecoder(Decoder<T> decoder) {
|
||||
return this.decoder != decoder ? new MetadataProcessor<>(this, decoder) : this;
|
||||
}
|
||||
|
||||
|
||||
public void process(ByteBuf content, Map<String, Object> result) {
|
||||
if (this.decoder == null) {
|
||||
throw new IllegalStateException("No decoder for " + this);
|
||||
}
|
||||
NettyDataBuffer dataBuffer = bufferFactory.wrap(content.retain());
|
||||
T value = this.decoder.decode(dataBuffer, this.targetType, this.mimeType, Collections.emptyMap());
|
||||
this.accumulator.accept(value, result);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MetadataProcessor mimeType=" + this.mimeType + ", targetType=" + this.targetType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ final class DefaultRSocketStrategies implements RSocketStrategies {
|
|||
return new DefaultRSocketStrategies(
|
||||
this.encoders, this.decoders,
|
||||
this.routeMatcher != null ? this.routeMatcher : initRouteMatcher(),
|
||||
this.metadataExtractor != null ? this.metadataExtractor : initMetadataExtractor(),
|
||||
getOrInitMetadataExtractor(),
|
||||
this.bufferFactory != null ? this.bufferFactory : initBufferFactory(),
|
||||
this.adapterRegistry != null ? this.adapterRegistry : initReactiveAdapterRegistry());
|
||||
}
|
||||
|
|
@ -220,10 +220,22 @@ final class DefaultRSocketStrategies implements RSocketStrategies {
|
|||
return new SimpleRouteMatcher(pathMatcher);
|
||||
}
|
||||
|
||||
private MetadataExtractor initMetadataExtractor() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.metadataToExtract(MimeTypeUtils.TEXT_PLAIN, String.class, MetadataExtractor.ROUTE_KEY);
|
||||
return extractor;
|
||||
private MetadataExtractor getOrInitMetadataExtractor() {
|
||||
if (this.metadataExtractor != null) {
|
||||
if (this.metadataExtractor instanceof DefaultMetadataExtractor) {
|
||||
DefaultMetadataExtractor extractor = (DefaultMetadataExtractor) this.metadataExtractor;
|
||||
if (extractor.getDecoders().isEmpty()) {
|
||||
extractor.setDecoders(this.decoders);
|
||||
}
|
||||
}
|
||||
return this.metadataExtractor;
|
||||
}
|
||||
else {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.setDecoders(this.decoders);
|
||||
extractor.metadataToExtract(MimeTypeUtils.TEXT_PLAIN, String.class, MetadataExtractor.ROUTE_KEY);
|
||||
return extractor;
|
||||
}
|
||||
}
|
||||
|
||||
private DataBufferFactory initBufferFactory() {
|
||||
|
|
|
|||
|
|
@ -58,9 +58,8 @@ public interface MetadataExtractor {
|
|||
* @param payload the payload whose metadata should be read
|
||||
* @param metadataMimeType the mime type of the metadata; this is what was
|
||||
* specified by the client at the start of the RSocket connection.
|
||||
* @param strategies for access to codecs and a DataBufferFactory
|
||||
* @return a map of 0 or more decoded metadata values with assigned names
|
||||
*/
|
||||
Map<String, Object> extract(Payload payload, MimeType metadataMimeType, RSocketStrategies strategies);
|
||||
Map<String, Object> extract(Payload payload, MimeType metadataMimeType);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,6 +190,9 @@ public interface RSocketStrategies {
|
|||
* <p>By default this is {@link DefaultMetadataExtractor} extracting a
|
||||
* route from {@code "message/x.rsocket.routing.v0"} or
|
||||
* {@code "text/plain"} metadata entries.
|
||||
* <p>If the extractor is a {@code DefaultMetadataExtractor}, its
|
||||
* {@code decoders} property will be set, if not already set, to the
|
||||
* {@link #decoder(Decoder[]) decoders} configured here.
|
||||
*/
|
||||
Builder metadataExtractor(@Nullable MetadataExtractor metadataExtractor);
|
||||
|
||||
|
|
|
|||
|
|
@ -192,8 +192,7 @@ class MessagingRSocket extends AbstractRSocket {
|
|||
MessageHeaderAccessor headers = new MessageHeaderAccessor();
|
||||
headers.setLeaveMutable(true);
|
||||
|
||||
Map<String, Object> metadataValues =
|
||||
this.metadataExtractor.extract(payload, this.metadataMimeType, this.strategies);
|
||||
Map<String, Object> metadataValues = this.metadataExtractor.extract(payload, this.metadataMimeType);
|
||||
|
||||
metadataValues.putIfAbsent(MetadataExtractor.ROUTE_KEY, "");
|
||||
for (Map.Entry<String, Object> entry : metadataValues.entrySet()) {
|
||||
|
|
|
|||
|
|
@ -174,6 +174,9 @@ public class RSocketMessageHandler extends MessageMappingMessageHandler {
|
|||
* other metadata.
|
||||
* <p>By default this is {@link DefaultMetadataExtractor} extracting a
|
||||
* route from {@code "message/x.rsocket.routing.v0"} or {@code "text/plain"}.
|
||||
* <p>If the extractor is a {@code DefaultMetadataExtractor}, its
|
||||
* {@code decoders} property will be set, if not already set, to the
|
||||
* {@link #setDecoders(List)} configured here.
|
||||
* @param extractor the extractor to use
|
||||
*/
|
||||
public void setMetadataExtractor(MetadataExtractor extractor) {
|
||||
|
|
@ -238,6 +241,7 @@ public class RSocketMessageHandler extends MessageMappingMessageHandler {
|
|||
|
||||
if (getMetadataExtractor() == null) {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.setDecoders(getDecoders());
|
||||
extractor.metadataToExtract(MimeTypeUtils.TEXT_PLAIN, String.class, MetadataExtractor.ROUTE_KEY);
|
||||
setMetadataExtractor(extractor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
package org.springframework.messaging.rsocket;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
|
|
@ -28,13 +29,15 @@ import org.mockito.ArgumentCaptor;
|
|||
import org.mockito.BDDMockito;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.core.codec.ByteArrayDecoder;
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
import static org.springframework.messaging.rsocket.MetadataExtractor.COMPOSITE_METADATA;
|
||||
import static org.springframework.messaging.rsocket.MetadataExtractor.ROUTE_KEY;
|
||||
import static org.springframework.messaging.rsocket.MetadataExtractor.ROUTING;
|
||||
|
|
@ -61,8 +64,6 @@ public class DefaultMetadataExtractorTests {
|
|||
@Before
|
||||
public void setUp() {
|
||||
this.strategies = RSocketStrategies.builder()
|
||||
.decoder(StringDecoder.allMimeTypes())
|
||||
.encoder(CharSequenceEncoder.allMimeTypes())
|
||||
.dataBufferFactory(new LeakAwareNettyDataBufferFactory(PooledByteBufAllocator.DEFAULT))
|
||||
.build();
|
||||
|
||||
|
|
@ -71,6 +72,7 @@ public class DefaultMetadataExtractorTests {
|
|||
BDDMockito.when(this.rsocket.fireAndForget(captor.capture())).thenReturn(Mono.empty());
|
||||
|
||||
this.extractor = new DefaultMetadataExtractor();
|
||||
this.extractor.setDecoders(Collections.singletonList(StringDecoder.allMimeTypes()));
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -82,7 +84,6 @@ public class DefaultMetadataExtractorTests {
|
|||
|
||||
@Test
|
||||
public void compositeMetadataWithDefaultSettings() {
|
||||
|
||||
requester(COMPOSITE_METADATA).route("toA")
|
||||
.metadata("text data", TEXT_PLAIN)
|
||||
.metadata("html data", TEXT_HTML)
|
||||
|
|
@ -91,7 +92,7 @@ public class DefaultMetadataExtractorTests {
|
|||
.send().block();
|
||||
|
||||
Payload payload = this.captor.getValue();
|
||||
Map<String, Object> result = this.extractor.extract(payload, COMPOSITE_METADATA, this.strategies);
|
||||
Map<String, Object> result = this.extractor.extract(payload, COMPOSITE_METADATA);
|
||||
payload.release();
|
||||
|
||||
assertThat(result).hasSize(1).containsEntry(ROUTE_KEY, "toA");
|
||||
|
|
@ -99,7 +100,6 @@ public class DefaultMetadataExtractorTests {
|
|||
|
||||
@Test
|
||||
public void compositeMetadataWithMimeTypeRegistrations() {
|
||||
|
||||
this.extractor.metadataToExtract(TEXT_PLAIN, String.class, "text-entry");
|
||||
this.extractor.metadataToExtract(TEXT_HTML, String.class, "html-entry");
|
||||
this.extractor.metadataToExtract(TEXT_XML, String.class, "xml-entry");
|
||||
|
|
@ -113,7 +113,7 @@ public class DefaultMetadataExtractorTests {
|
|||
.block();
|
||||
|
||||
Payload payload = this.captor.getValue();
|
||||
Map<String, Object> result = this.extractor.extract(payload, COMPOSITE_METADATA, this.strategies);
|
||||
Map<String, Object> result = this.extractor.extract(payload, COMPOSITE_METADATA);
|
||||
payload.release();
|
||||
|
||||
assertThat(result).hasSize(4)
|
||||
|
|
@ -125,10 +125,9 @@ public class DefaultMetadataExtractorTests {
|
|||
|
||||
@Test
|
||||
public void route() {
|
||||
|
||||
requester(ROUTING).route("toA").data("data").send().block();
|
||||
Payload payload = this.captor.getValue();
|
||||
Map<String, Object> result = this.extractor.extract(payload, ROUTING, this.strategies);
|
||||
Map<String, Object> result = this.extractor.extract(payload, ROUTING);
|
||||
payload.release();
|
||||
|
||||
assertThat(result).hasSize(1).containsEntry(ROUTE_KEY, "toA");
|
||||
|
|
@ -136,12 +135,11 @@ public class DefaultMetadataExtractorTests {
|
|||
|
||||
@Test
|
||||
public void routeAsText() {
|
||||
|
||||
this.extractor.metadataToExtract(TEXT_PLAIN, String.class, ROUTE_KEY);
|
||||
|
||||
requester(TEXT_PLAIN).route("toA").data("data").send().block();
|
||||
Payload payload = this.captor.getValue();
|
||||
Map<String, Object> result = this.extractor.extract(payload, TEXT_PLAIN, this.strategies);
|
||||
Map<String, Object> result = this.extractor.extract(payload, TEXT_PLAIN);
|
||||
payload.release();
|
||||
|
||||
assertThat(result).hasSize(1).containsEntry(ROUTE_KEY, "toA");
|
||||
|
|
@ -149,7 +147,6 @@ public class DefaultMetadataExtractorTests {
|
|||
|
||||
@Test
|
||||
public void routeWithCustomFormatting() {
|
||||
|
||||
this.extractor.metadataToExtract(TEXT_PLAIN, String.class, (text, result) -> {
|
||||
String[] items = text.split(":");
|
||||
Assert.isTrue(items.length == 2, "Expected two items");
|
||||
|
|
@ -159,7 +156,7 @@ public class DefaultMetadataExtractorTests {
|
|||
|
||||
requester(TEXT_PLAIN).metadata("toA:text data", null).data("data").send().block();
|
||||
Payload payload = this.captor.getValue();
|
||||
Map<String, Object> result = this.extractor.extract(payload, TEXT_PLAIN, this.strategies);
|
||||
Map<String, Object> result = this.extractor.extract(payload, TEXT_PLAIN);
|
||||
payload.release();
|
||||
|
||||
assertThat(result).hasSize(2)
|
||||
|
|
@ -167,6 +164,59 @@ public class DefaultMetadataExtractorTests {
|
|||
.containsEntry("entry1", "text data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMetadataToExtractBeforeDecoders() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.metadataToExtract(TEXT_PLAIN, String.class, "key");
|
||||
extractor.setDecoders(Collections.singletonList(StringDecoder.allMimeTypes()));
|
||||
|
||||
requester(TEXT_PLAIN).metadata("meta entry", null).data("data").send().block();
|
||||
Payload payload = this.captor.getValue();
|
||||
Map<String, Object> result = extractor.extract(payload, TEXT_PLAIN);
|
||||
payload.release();
|
||||
|
||||
assertThat(result).hasSize(1).containsEntry("key", "meta entry");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDecoderExceptionWhenSettingDecoders() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.metadataToExtract(TEXT_PLAIN, String.class, "key");
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> extractor.setDecoders(Collections.singletonList(new ByteArrayDecoder())))
|
||||
.withMessage("No decoder for MetadataProcessor mimeType=text/plain, targetType=java.lang.String");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDecoderExceptionWhenRegisteringMetadataToExtract() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.setDecoders(Collections.singletonList(new ByteArrayDecoder()));
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> extractor.metadataToExtract(TEXT_PLAIN, String.class, "key"))
|
||||
.withMessage("No decoder for text/plain");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodersNotSet() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.metadataToExtract(TEXT_PLAIN, String.class, "key");
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> {
|
||||
requester(TEXT_PLAIN).metadata("meta entry", null).data("data").send().block();
|
||||
Payload payload = this.captor.getValue();
|
||||
try {
|
||||
extractor.extract(payload, TEXT_PLAIN);
|
||||
}
|
||||
finally {
|
||||
payload.release();
|
||||
}
|
||||
})
|
||||
.withMessage("No decoder for MetadataProcessor mimeType=text/plain, targetType=java.lang.String");
|
||||
}
|
||||
|
||||
|
||||
private RSocketRequester requester(MimeType metadataMimeType) {
|
||||
return RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, metadataMimeType, this.strategies);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.springframework.messaging.rsocket;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
|
|
@ -87,6 +89,39 @@ public class DefaultRSocketStrategiesTests {
|
|||
assertThat(strategies.reactiveAdapterRegistry()).isSameAs(registry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExtractorInitializedWithDecoders() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
|
||||
RSocketStrategies strategies = RSocketStrategies.builder()
|
||||
.decoders(decoders -> {
|
||||
decoders.clear();
|
||||
decoders.add(new ByteArrayDecoder());
|
||||
decoders.add(new ByteBufferDecoder());
|
||||
})
|
||||
.metadataExtractor(extractor)
|
||||
.build();
|
||||
|
||||
assertThat(((DefaultMetadataExtractor) strategies.metadataExtractor()).getDecoders()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExtractorWithExplicitlySetDecoders() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.setDecoders(Collections.singletonList(StringDecoder.allMimeTypes()));
|
||||
|
||||
RSocketStrategies strategies = RSocketStrategies.builder()
|
||||
.decoders(decoders -> {
|
||||
decoders.clear();
|
||||
decoders.add(new ByteArrayDecoder());
|
||||
decoders.add(new ByteBufferDecoder());
|
||||
})
|
||||
.metadataExtractor(extractor)
|
||||
.build();
|
||||
|
||||
assertThat(((DefaultMetadataExtractor) strategies.metadataExtractor()).getDecoders()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyConstructor() {
|
||||
RSocketStrategies strategies1 = RSocketStrategies.create();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.messaging.rsocket.annotation.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -158,6 +159,31 @@ public class RSocketMessageHandlerTests {
|
|||
assertThat(strategies.reactiveAdapterRegistry()).isSameAs(handler.getReactiveAdapterRegistry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExtractorInitializedWithDecoders() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
|
||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
||||
handler.setDecoders(Arrays.asList(new ByteArrayDecoder(), new ByteBufferDecoder()));
|
||||
handler.setMetadataExtractor(extractor);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
assertThat(((DefaultMetadataExtractor) handler.getMetadataExtractor()).getDecoders()).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataExtractorWithExplicitlySetDecoders() {
|
||||
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
|
||||
extractor.setDecoders(Collections.singletonList(StringDecoder.allMimeTypes()));
|
||||
|
||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
||||
handler.setDecoders(Arrays.asList(new ByteArrayDecoder(), new ByteBufferDecoder()));
|
||||
handler.setMetadataExtractor(extractor);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
assertThat(((DefaultMetadataExtractor) handler.getMetadataExtractor()).getDecoders()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mappings() {
|
||||
testMapping(new SimpleController(), "path");
|
||||
|
|
|
|||
Loading…
Reference in New Issue