Support for RSocket composite metadata

Closes gh-22798
This commit is contained in:
Rossen Stoyanchev
2019-06-12 15:30:07 -04:00
parent 9fb973d4f3
commit 14e2c6803e
9 changed files with 352 additions and 112 deletions

View File

@@ -19,6 +19,7 @@ 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;
@@ -28,6 +29,7 @@ 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;
@@ -61,37 +63,41 @@ public class DefaultRSocketRequesterTests {
private RSocketRequester requester;
private RSocketStrategies strategies;
private final DefaultDataBufferFactory bufferFactory = new DefaultDataBufferFactory();
@Before
public void setUp() {
RSocketStrategies strategies = RSocketStrategies.builder()
this.strategies = RSocketStrategies.builder()
.decoder(StringDecoder.allMimeTypes())
.encoder(CharSequenceEncoder.allMimeTypes())
.build();
this.rsocket = new TestRSocket();
this.requester = RSocketRequester.wrap(this.rsocket, MimeTypeUtils.TEXT_PLAIN, strategies);
this.requester = RSocketRequester.wrap(this.rsocket,
MimeTypeUtils.TEXT_PLAIN, DefaultRSocketRequester.ROUTING,
this.strategies);
}
@Test
public void singlePayload() {
public void sendMono() {
// data(Object)
testSinglePayload(spec -> spec.data("bodyA"), "bodyA");
testSinglePayload(spec -> spec.data(Mono.delay(MILLIS_10).map(l -> "bodyA")), "bodyA");
testSinglePayload(spec -> spec.data(Mono.delay(MILLIS_10).then()), "");
testSinglePayload(spec -> spec.data(Single.timer(10, MILLISECONDS).map(l -> "bodyA")), "bodyA");
testSinglePayload(spec -> spec.data(Completable.complete()), "");
testSendMono(spec -> spec.data("bodyA"), "bodyA");
testSendMono(spec -> spec.data(Mono.delay(MILLIS_10).map(l -> "bodyA")), "bodyA");
testSendMono(spec -> spec.data(Mono.delay(MILLIS_10).then()), "");
testSendMono(spec -> spec.data(Single.timer(10, MILLISECONDS).map(l -> "bodyA")), "bodyA");
testSendMono(spec -> spec.data(Completable.complete()), "");
// data(Publisher<T>, Class<T>)
testSinglePayload(spec -> spec.data(Mono.delay(MILLIS_10).map(l -> "bodyA"), String.class), "bodyA");
testSinglePayload(spec -> spec.data(Mono.delay(MILLIS_10).map(l -> "bodyA"), Object.class), "bodyA");
testSinglePayload(spec -> spec.data(Mono.delay(MILLIS_10).then(), Void.class), "");
testSendMono(spec -> spec.data(Mono.delay(MILLIS_10).map(l -> "bodyA"), String.class), "bodyA");
testSendMono(spec -> spec.data(Mono.delay(MILLIS_10).map(l -> "bodyA"), Object.class), "bodyA");
testSendMono(spec -> spec.data(Mono.delay(MILLIS_10).then(), Void.class), "");
}
private void testSinglePayload(Function<RequestSpec, ResponseSpec> mapper, String expectedValue) {
private void testSendMono(Function<RequestSpec, ResponseSpec> mapper, String expectedValue) {
mapper.apply(this.requester.route("toA")).send().block(Duration.ofSeconds(5));
assertThat(this.rsocket.getSavedMethodName()).isEqualTo("fireAndForget");
@@ -100,22 +106,22 @@ public class DefaultRSocketRequesterTests {
}
@Test
public void multiPayload() {
public void sendFlux() {
String[] values = new String[] {"bodyA", "bodyB", "bodyC"};
Flux<String> stringFlux = Flux.fromArray(values).delayElements(MILLIS_10);
// data(Object)
testMultiPayload(spec -> spec.data(stringFlux), values);
testMultiPayload(spec -> spec.data(Flux.empty()), "");
testMultiPayload(spec -> spec.data(Observable.fromArray(values).delay(10, MILLISECONDS)), values);
testMultiPayload(spec -> spec.data(Observable.empty()), "");
testSendFlux(spec -> spec.data(stringFlux), values);
testSendFlux(spec -> spec.data(Flux.empty()), "");
testSendFlux(spec -> spec.data(Observable.fromArray(values).delay(10, MILLISECONDS)), values);
testSendFlux(spec -> spec.data(Observable.empty()), "");
// data(Publisher<T>, Class<T>)
testMultiPayload(spec -> spec.data(stringFlux, String.class), values);
testMultiPayload(spec -> spec.data(stringFlux.cast(Object.class), Object.class), values);
testSendFlux(spec -> spec.data(stringFlux, String.class), values);
testSendFlux(spec -> spec.data(stringFlux.cast(Object.class), Object.class), values);
}
private void testMultiPayload(Function<RequestSpec, ResponseSpec> mapper, String... expectedValues) {
private void testSendFlux(Function<RequestSpec, ResponseSpec> mapper, String... expectedValues) {
this.rsocket.reset();
mapper.apply(this.requester.route("toA")).retrieveFlux(String.class).blockLast(Duration.ofSeconds(5));
@@ -129,19 +135,50 @@ public class DefaultRSocketRequesterTests {
assertThat(payloads.get(0).getDataUtf8()).isEqualTo("");
}
else {
assertThat(payloads.stream().map(Payload::getMetadataUtf8).toArray(String[]::new)).isEqualTo(new String[] {"toA", "", ""});
assertThat(payloads.stream().map(Payload::getDataUtf8).toArray(String[]::new)).isEqualTo(expectedValues);
assertThat(payloads.stream().map(Payload::getMetadataUtf8).toArray(String[]::new))
.isEqualTo(new String[] {"toA", "", ""});
assertThat(payloads.stream().map(Payload::getDataUtf8).toArray(String[]::new))
.isEqualTo(expectedValues);
}
}
@Test
public void send() {
String value = "bodyA";
this.requester.route("toA").data(value).send().block(Duration.ofSeconds(5));
public void sendCompositeMetadata() {
RSocketRequester requester = RSocketRequester.wrap(this.rsocket,
MimeTypeUtils.TEXT_PLAIN, DefaultRSocketRequester.COMPOSITE_METADATA,
this.strategies);
assertThat(this.rsocket.getSavedMethodName()).isEqualTo("fireAndForget");
assertThat(this.rsocket.getSavedPayload().getMetadataUtf8()).isEqualTo("toA");
assertThat(this.rsocket.getSavedPayload().getDataUtf8()).isEqualTo("bodyA");
requester.route("toA")
.metadata("My metadata", MimeTypeUtils.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(DefaultRSocketRequester.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 supportedMetadataMimeTypes() {
RSocketRequester.wrap(this.rsocket, MimeTypeUtils.TEXT_PLAIN,
DefaultRSocketRequester.COMPOSITE_METADATA, this.strategies);
RSocketRequester.wrap(this.rsocket, MimeTypeUtils.TEXT_PLAIN,
DefaultRSocketRequester.ROUTING, this.strategies);
assertThatIllegalArgumentException().isThrownBy(() -> RSocketRequester.wrap(
this.rsocket, MimeTypeUtils.TEXT_PLAIN, MimeTypeUtils.TEXT_PLAIN, this.strategies));
}
@Test
@@ -188,10 +225,10 @@ public class DefaultRSocketRequesterTests {
}
@Test
public void rejectFluxToMono() {
assertThatIllegalArgumentException().isThrownBy(() ->
this.requester.route("").data(Flux.just("a", "b")).retrieveMono(String.class))
.withMessage("No RSocket interaction model for Flux request to Mono response.");
public void fluxToMonoIsRejected() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.requester.route("").data(Flux.just("a", "b")).retrieveMono(String.class))
.withMessage("No RSocket interaction model for Flux request to Mono response.");
}
private Payload toPayload(String value) {

View File

@@ -101,7 +101,9 @@ public class RSocketClientToServerIntegrationTests {
.verify(Duration.ofSeconds(5));
assertThat(interceptor.getRSocketCount()).isEqualTo(1);
assertThat(interceptor.getFireAndForgetCount(0)).as("Fire and forget requests did not actually complete handling on the server side").isEqualTo(3);
assertThat(interceptor.getFireAndForgetCount(0))
.as("Fire and forget requests did not actually complete handling on the server side")
.isEqualTo(3);
}
@Test

View File

@@ -106,8 +106,9 @@ public class RSocketServerToClientIntegrationTests {
RSocket rsocket = null;
try {
rsocket = RSocketFactory.connect()
.setupPayload(DefaultPayload.create("", destination))
.metadataMimeType("message/x.rsocket.routing.v0")
.dataMimeType("text/plain")
.setupPayload(DefaultPayload.create("", destination))
.frameDecoder(PayloadDecoder.ZERO_COPY)
.acceptor(context.getBean("clientAcceptor", MessageHandlerAcceptor.class))
.transport(TcpClientTransport.create("localhost", 7000))