Factor out MetadataEncoder from RSocketRequester

To be re-used also for creating metadata for the setup payload.

See: gh-23368
This commit is contained in:
Rossen Stoyanchev
2019-07-29 21:13:39 +01:00
parent c76370d7d8
commit 55946bf319
6 changed files with 475 additions and 230 deletions

View File

@@ -21,19 +21,15 @@ import java.util.Map;
import io.netty.buffer.PooledByteBufAllocator;
import io.rsocket.Payload;
import io.rsocket.RSocket;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.BDDMockito;
import reactor.core.publisher.Mono;
import org.springframework.core.codec.ByteArrayDecoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.io.buffer.DataBuffer;
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;
@@ -53,23 +49,13 @@ public class DefaultMetadataExtractorTests {
private RSocketStrategies strategies;
private ArgumentCaptor<Payload> captor;
private RSocket rsocket;
private DefaultMetadataExtractor extractor;
@Before
public void setUp() {
this.strategies = RSocketStrategies.builder()
.dataBufferFactory(new LeakAwareNettyDataBufferFactory(PooledByteBufAllocator.DEFAULT))
.build();
this.rsocket = BDDMockito.mock(RSocket.class);
this.captor = ArgumentCaptor.forClass(Payload.class);
BDDMockito.when(this.rsocket.fireAndForget(captor.capture())).thenReturn(Mono.empty());
DataBufferFactory bufferFactory = new LeakAwareNettyDataBufferFactory(PooledByteBufAllocator.DEFAULT);
this.strategies = RSocketStrategies.builder().dataBufferFactory(bufferFactory).build();
this.extractor = new DefaultMetadataExtractor(StringDecoder.allMimeTypes());
}
@@ -82,14 +68,14 @@ public class DefaultMetadataExtractorTests {
@Test
public void compositeMetadataWithDefaultSettings() {
requester(COMPOSITE_METADATA).route("toA")
MetadataEncoder metadataEncoder = new MetadataEncoder(COMPOSITE_METADATA, this.strategies)
.route("toA")
.metadata("text data", TEXT_PLAIN)
.metadata("html data", TEXT_HTML)
.metadata("xml data", TEXT_XML)
.data("data")
.send().block();
.metadata("xml data", TEXT_XML);
Payload payload = this.captor.getValue();
DataBuffer metadata = metadataEncoder.encode();
Payload payload = createPayload(metadata);
Map<String, Object> result = this.extractor.extract(payload, COMPOSITE_METADATA);
payload.release();
@@ -102,15 +88,14 @@ public class DefaultMetadataExtractorTests {
this.extractor.metadataToExtract(TEXT_HTML, String.class, "html-entry");
this.extractor.metadataToExtract(TEXT_XML, String.class, "xml-entry");
requester(COMPOSITE_METADATA).route("toA")
MetadataEncoder metadataEncoder = new MetadataEncoder(COMPOSITE_METADATA, this.strategies)
.route("toA")
.metadata("text data", TEXT_PLAIN)
.metadata("html data", TEXT_HTML)
.metadata("xml data", TEXT_XML)
.data("data")
.send()
.block();
.metadata("xml data", TEXT_XML);
Payload payload = this.captor.getValue();
DataBuffer metadata = metadataEncoder.encode();
Payload payload = createPayload(metadata);
Map<String, Object> result = this.extractor.extract(payload, COMPOSITE_METADATA);
payload.release();
@@ -123,8 +108,9 @@ public class DefaultMetadataExtractorTests {
@Test
public void route() {
requester(ROUTING).route("toA").data("data").send().block();
Payload payload = this.captor.getValue();
MetadataEncoder metadataEncoder = new MetadataEncoder(ROUTING, this.strategies).route("toA");
DataBuffer metadata = metadataEncoder.encode();
Payload payload = createPayload(metadata);
Map<String, Object> result = this.extractor.extract(payload, ROUTING);
payload.release();
@@ -135,8 +121,9 @@ public class DefaultMetadataExtractorTests {
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();
MetadataEncoder metadataEncoder = new MetadataEncoder(TEXT_PLAIN, this.strategies).route("toA");
DataBuffer metadata = metadataEncoder.encode();
Payload payload = createPayload(metadata);
Map<String, Object> result = this.extractor.extract(payload, TEXT_PLAIN);
payload.release();
@@ -152,8 +139,9 @@ public class DefaultMetadataExtractorTests {
result.put("entry1", items[1]);
});
requester(TEXT_PLAIN).metadata("toA:text data", null).data("data").send().block();
Payload payload = this.captor.getValue();
MetadataEncoder encoder = new MetadataEncoder(TEXT_PLAIN, this.strategies).metadata("toA:text data", null);
DataBuffer metadata = encoder.encode();
Payload payload = createPayload(metadata);
Map<String, Object> result = this.extractor.extract(payload, TEXT_PLAIN);
payload.release();
@@ -174,8 +162,8 @@ public class DefaultMetadataExtractorTests {
}
private RSocketRequester requester(MimeType metadataMimeType) {
return RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, metadataMimeType, this.strategies);
private Payload createPayload(DataBuffer metadata) {
return PayloadUtils.createPayload(metadata, this.strategies.dataBufferFactory().allocateBuffer());
}
}

View File

@@ -19,7 +19,6 @@ package org.springframework.messaging.rsocket;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
@@ -29,7 +28,6 @@ import io.reactivex.Observable;
import io.reactivex.Single;
import io.rsocket.AbstractRSocket;
import io.rsocket.Payload;
import io.rsocket.metadata.CompositeMetadata;
import org.junit.Before;
import org.junit.Test;
import org.reactivestreams.Publisher;
@@ -37,8 +35,6 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.codec.CharSequenceEncoder;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.lang.Nullable;
import org.springframework.messaging.rsocket.RSocketRequester.RequestSpec;
@@ -47,7 +43,6 @@ import org.springframework.messaging.rsocket.RSocketRequester.ResponseSpec;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.util.MimeTypeUtils.TEXT_PLAIN;
/**
@@ -64,17 +59,13 @@ public class DefaultRSocketRequesterTests {
private RSocketRequester requester;
private RSocketStrategies strategies;
private final RSocketStrategies strategies = RSocketStrategies.create();
private final DefaultDataBufferFactory bufferFactory = new DefaultDataBufferFactory();
@Before
public void setUp() {
this.strategies = RSocketStrategies.builder()
.decoder(StringDecoder.allMimeTypes())
.encoder(CharSequenceEncoder.allMimeTypes())
.build();
this.rsocket = new TestRSocket();
this.requester = RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, TEXT_PLAIN, this.strategies);
}
@@ -141,86 +132,6 @@ public class DefaultRSocketRequesterTests {
}
}
@Test
public void metadataCompositeWithRoute() {
RSocketRequester requester = RSocketRequester.wrap(
this.rsocket, TEXT_PLAIN, MetadataExtractor.COMPOSITE_METADATA, this.strategies);
requester.route("toA").data("bodyA").send().block(Duration.ofSeconds(5));
CompositeMetadata entries = new CompositeMetadata(this.rsocket.getSavedPayload().metadata(), false);
Iterator<CompositeMetadata.Entry> iterator = entries.iterator();
assertThat(iterator.hasNext()).isTrue();
CompositeMetadata.Entry entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(MetadataExtractor.ROUTING.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("toA");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void metadataCompositeWithRouteAndTextEntry() {
RSocketRequester requester = RSocketRequester.wrap(
this.rsocket, TEXT_PLAIN, MetadataExtractor.COMPOSITE_METADATA, this.strategies);
requester.route("toA")
.metadata("My metadata", TEXT_PLAIN).data("bodyA")
.send()
.block(Duration.ofSeconds(5));
CompositeMetadata entries = new CompositeMetadata(this.rsocket.getSavedPayload().metadata(), false);
Iterator<CompositeMetadata.Entry> iterator = entries.iterator();
assertThat(iterator.hasNext()).isTrue();
CompositeMetadata.Entry entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(MetadataExtractor.ROUTING.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("toA");
assertThat(iterator.hasNext()).isTrue();
entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(TEXT_PLAIN.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("My metadata");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void metadataRouteAsText() {
RSocketRequester requester = RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, TEXT_PLAIN, this.strategies);
requester.route("toA").data("bodyA").send().block(Duration.ofSeconds(5));
assertThat(this.rsocket.getSavedPayload().getMetadataUtf8()).isEqualTo("toA");
}
@Test
public void metadataAsText() {
RSocketRequester requester = RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, TEXT_PLAIN, this.strategies);
requester.metadata("toA", null).data("bodyA").send().block(Duration.ofSeconds(5));
assertThat(this.rsocket.getSavedPayload().getMetadataUtf8()).isEqualTo("toA");
}
@Test
public void metadataMimeTypeMismatch() {
RSocketRequester requester = RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, TEXT_PLAIN, this.strategies);
assertThatThrownBy(() -> requester.metadata("toA", MetadataExtractor.ROUTING).data("bodyA").send().block())
.hasMessageStartingWith("Connection configured for metadata mime type");
}
@Test
public void routeWithVars() {
RSocketRequester requester = RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, TEXT_PLAIN, this.strategies);
requester.route("a.{b}.{c}", "BBB", "C.C.C").data("body").send().block();
assertThat(this.rsocket.getSavedPayload().getMetadataUtf8()).isEqualTo("a.BBB.C%2EC%2EC");
}
@Test
public void supportedMetadataMimeTypes() {
RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, MetadataExtractor.COMPOSITE_METADATA, this.strategies);
RSocketRequester.wrap(this.rsocket, TEXT_PLAIN, MetadataExtractor.ROUTING, this.strategies);
}
@Test
public void retrieveMono() {
String value = "bodyA";

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.rsocket;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.rsocket.metadata.CompositeMetadata;
import org.junit.Test;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
*
* @author Rossen Stoyanchev
* @since 5.2
*/
public class MetadataEncoderTests {
private final RSocketStrategies strategies = RSocketStrategies.create();
@Test
public void compositeMetadataWithRoute() {
DataBuffer buffer = new MetadataEncoder(MetadataExtractor.COMPOSITE_METADATA, this.strategies)
.route("toA")
.encode();
CompositeMetadata entries = new CompositeMetadata(((NettyDataBuffer) buffer).getNativeBuffer(), false);
Iterator<CompositeMetadata.Entry> iterator = entries.iterator();
assertThat(iterator.hasNext()).isTrue();
CompositeMetadata.Entry entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(MetadataExtractor.ROUTING.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("toA");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void compositeMetadataWithRouteAndText() {
DataBuffer buffer = new MetadataEncoder(MetadataExtractor.COMPOSITE_METADATA, this.strategies)
.route("toA")
.metadata("My metadata", MimeTypeUtils.TEXT_PLAIN)
.encode();
CompositeMetadata entries = new CompositeMetadata(((NettyDataBuffer) buffer).getNativeBuffer(), false);
Iterator<CompositeMetadata.Entry> iterator = entries.iterator();
assertThat(iterator.hasNext()).isTrue();
CompositeMetadata.Entry entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(MetadataExtractor.ROUTING.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("toA");
assertThat(iterator.hasNext()).isTrue();
entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(MimeTypeUtils.TEXT_PLAIN.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("My metadata");
assertThat(iterator.hasNext()).isFalse();
}
@Test
public void routeWithRoutingMimeType() {
DataBuffer buffer =
new MetadataEncoder(MetadataExtractor.ROUTING, this.strategies)
.route("toA")
.encode();
assertThat(dumpString(buffer)).isEqualTo("toA");
}
@Test
public void routeWithTextPlainMimeType() {
DataBuffer buffer =
new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies)
.route("toA")
.encode();
assertThat(dumpString(buffer)).isEqualTo("toA");
}
@Test
public void routeWithVars() {
DataBuffer buffer =
new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies)
.route("a.{b}.{c}", "BBB", "C.C.C")
.encode();
assertThat(dumpString(buffer)).isEqualTo("a.BBB.C%2EC%2EC");
}
@Test
public void metadataWithTextPlainMimeType() {
DataBuffer buffer =
new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies)
.metadata("toA", null)
.encode();
assertThat(dumpString(buffer)).isEqualTo("toA");
}
@Test
public void compositeRequiredForMultipleEntries() {
// Route, metadata
MetadataEncoder encoder1 = new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies);
encoder1.route("toA");
assertThatThrownBy(() -> encoder1.metadata("My metadata", MimeTypeUtils.TEXT_PLAIN))
.hasMessage("Composite metadata required for multiple metadata entries.");
// Metadata, route
MetadataEncoder encoder2 = new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies);
encoder2.metadata("My metadata", MimeTypeUtils.TEXT_PLAIN);
assertThatThrownBy(() -> encoder2.route("toA"))
.hasMessage("Composite metadata required for multiple metadata entries.");
// Route and metadata
MetadataEncoder encoder3 = new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies);
Map<Object, MimeType> metadata = Collections.singletonMap("My metadata", MimeTypeUtils.TEXT_PLAIN);
assertThatThrownBy(() -> encoder3.metadataAndOrRoute(metadata, "toA", new Object[0]))
.hasMessage("Composite metadata required for multiple metadata entries.");
}
@Test
public void mimeTypeRequiredForCompositeEntries() {
MetadataEncoder encoder = new MetadataEncoder(MetadataExtractor.COMPOSITE_METADATA, this.strategies);
assertThatThrownBy(() -> encoder.metadata("toA", null))
.hasMessage("MimeType is required for composite metadata entries.");
}
@Test
public void mimeTypeDoesNotMatchConnectionMetadataMimeType() {
MetadataEncoder encoder = new MetadataEncoder(MimeTypeUtils.TEXT_PLAIN, this.strategies);
assertThatThrownBy(() -> encoder.metadata("toA", MimeTypeUtils.APPLICATION_JSON))
.hasMessage("Mime type is optional (may be null) " +
"but was provided and does not match the connection metadata mime type.");
}
@Test
public void defaultDataBufferFactory() {
DefaultDataBufferFactory bufferFactory = new DefaultDataBufferFactory();
RSocketStrategies strategies = RSocketStrategies.builder().dataBufferFactory(bufferFactory).build();
DataBuffer buffer = new MetadataEncoder(MetadataExtractor.COMPOSITE_METADATA, strategies)
.route("toA")
.encode();
ByteBuf byteBuf = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT)
.wrap(buffer.asByteBuffer())
.getNativeBuffer();
CompositeMetadata entries = new CompositeMetadata(byteBuf, false);
Iterator<CompositeMetadata.Entry> iterator = entries.iterator();
assertThat(iterator.hasNext()).isTrue();
CompositeMetadata.Entry entry = iterator.next();
assertThat(entry.getMimeType()).isEqualTo(MetadataExtractor.ROUTING.toString());
assertThat(entry.getContent().toString(StandardCharsets.UTF_8)).isEqualTo("toA");
assertThat(iterator.hasNext()).isFalse();
}
private String dumpString(DataBuffer buffer) {
return DataBufferTestUtils.dumpString(buffer, StandardCharsets.UTF_8);
}
}