MessagingAcceptor/RSocket refinements + upgrade to 0.11.17
See gh-21987
This commit is contained in:
parent
8bdd709683
commit
d6f4ec8c33
|
@ -7,7 +7,7 @@ dependencyManagement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def rsocketVersion = "0.11.15"
|
def rsocketVersion = "0.11.17"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile(project(":spring-beans"))
|
compile(project(":spring-beans"))
|
||||||
|
|
|
@ -149,9 +149,14 @@ final class DefaultRSocketRequester implements RSocketRequester {
|
||||||
.concatMap(value -> encodeValue(value, dataType, encoder))
|
.concatMap(value -> encodeValue(value, dataType, encoder))
|
||||||
.switchOnFirst((signal, inner) -> {
|
.switchOnFirst((signal, inner) -> {
|
||||||
DataBuffer data = signal.get();
|
DataBuffer data = signal.get();
|
||||||
return data != null ?
|
if (data != null) {
|
||||||
Flux.concat(Mono.just(firstPayload(data)), inner.skip(1).map(PayloadUtils::asPayload)) :
|
return Flux.concat(
|
||||||
inner.map(PayloadUtils::asPayload);
|
Mono.just(firstPayload(data)),
|
||||||
|
inner.skip(1).map(PayloadUtils::createPayload));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inner.map(PayloadUtils::createPayload);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.switchIfEmpty(emptyPayload());
|
.switchIfEmpty(emptyPayload());
|
||||||
return new DefaultResponseSpec(payloadFlux);
|
return new DefaultResponseSpec(payloadFlux);
|
||||||
|
@ -167,7 +172,7 @@ final class DefaultRSocketRequester implements RSocketRequester {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Payload firstPayload(DataBuffer data) {
|
private Payload firstPayload(DataBuffer data) {
|
||||||
return PayloadUtils.asPayload(getMetadata(), data);
|
return PayloadUtils.createPayload(getMetadata(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Payload> emptyPayload() {
|
private Mono<Payload> emptyPayload() {
|
||||||
|
@ -239,7 +244,7 @@ final class DefaultRSocketRequester implements RSocketRequester {
|
||||||
|
|
||||||
Decoder<?> decoder = strategies.decoder(elementType, dataMimeType);
|
Decoder<?> decoder = strategies.decoder(elementType, dataMimeType);
|
||||||
return (Mono<T>) decoder.decodeToMono(
|
return (Mono<T>) decoder.decodeToMono(
|
||||||
payloadMono.map(this::asDataBuffer), elementType, dataMimeType, EMPTY_HINTS);
|
payloadMono.map(this::wrapPayloadData), elementType, dataMimeType, EMPTY_HINTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -255,12 +260,12 @@ final class DefaultRSocketRequester implements RSocketRequester {
|
||||||
|
|
||||||
Decoder<?> decoder = strategies.decoder(elementType, dataMimeType);
|
Decoder<?> decoder = strategies.decoder(elementType, dataMimeType);
|
||||||
|
|
||||||
return payloadFlux.map(this::asDataBuffer).concatMap(dataBuffer ->
|
return payloadFlux.map(this::wrapPayloadData).concatMap(dataBuffer ->
|
||||||
(Mono<T>) decoder.decodeToMono(Mono.just(dataBuffer), elementType, dataMimeType, EMPTY_HINTS));
|
(Mono<T>) decoder.decodeToMono(Mono.just(dataBuffer), elementType, dataMimeType, EMPTY_HINTS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataBuffer asDataBuffer(Payload payload) {
|
private DataBuffer wrapPayloadData(Payload payload) {
|
||||||
return PayloadUtils.asDataBuffer(payload, strategies.dataBufferFactory());
|
return PayloadUtils.wrapPayloadData(payload, strategies.dataBufferFactory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.ReactiveMessageChannel;
|
import org.springframework.messaging.ReactiveMessageChannel;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.MimeType;
|
import org.springframework.util.MimeType;
|
||||||
import org.springframework.util.MimeTypeUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSocket acceptor for
|
* RSocket acceptor for
|
||||||
|
@ -79,10 +78,9 @@ public final class MessagingAcceptor implements SocketAcceptor, Function<RSocket
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the default content type for data payloads. For server
|
* Configure the default content type to use for data payloads.
|
||||||
* acceptors this is available from the {@link ConnectionSetupPayload} but
|
* <p>By default this is not set. However a server acceptor will use the
|
||||||
* for client acceptors it's not and must be provided here.
|
* content type from the {@link ConnectionSetupPayload}.
|
||||||
* <p>By default this is not set.
|
|
||||||
* @param defaultDataMimeType the MimeType to use
|
* @param defaultDataMimeType the MimeType to use
|
||||||
*/
|
*/
|
||||||
public void setDefaultDataMimeType(@Nullable MimeType defaultDataMimeType) {
|
public void setDefaultDataMimeType(@Nullable MimeType defaultDataMimeType) {
|
||||||
|
@ -92,21 +90,18 @@ public final class MessagingAcceptor implements SocketAcceptor, Function<RSocket
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<RSocket> accept(ConnectionSetupPayload setupPayload, RSocket sendingRSocket) {
|
public Mono<RSocket> accept(ConnectionSetupPayload setupPayload, RSocket sendingRSocket) {
|
||||||
|
MessagingRSocket rsocket = createRSocket(sendingRSocket);
|
||||||
MimeType mimeType = setupPayload.dataMimeType() != null ?
|
rsocket.handleConnectionSetupPayload(setupPayload).subscribe();
|
||||||
MimeTypeUtils.parseMimeType(setupPayload.dataMimeType()) : this.defaultDataMimeType;
|
return Mono.just(rsocket);
|
||||||
|
|
||||||
MessagingRSocket rsocket = createRSocket(sendingRSocket, mimeType);
|
|
||||||
return rsocket.afterConnectionEstablished(setupPayload).then(Mono.just(rsocket));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RSocket apply(RSocket sendingRSocket) {
|
public RSocket apply(RSocket sendingRSocket) {
|
||||||
return createRSocket(sendingRSocket, this.defaultDataMimeType);
|
return createRSocket(sendingRSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessagingRSocket createRSocket(RSocket sendingRSocket, @Nullable MimeType dataMimeType) {
|
private MessagingRSocket createRSocket(RSocket rsocket) {
|
||||||
return new MessagingRSocket(this.messageChannel, sendingRSocket, dataMimeType, this.rsocketStrategies);
|
return new MessagingRSocket(this.messageChannel, rsocket, this.defaultDataMimeType, this.rsocketStrategies);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.springframework.messaging.rsocket;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import io.rsocket.AbstractRSocket;
|
||||||
import io.rsocket.ConnectionSetupPayload;
|
import io.rsocket.ConnectionSetupPayload;
|
||||||
import io.rsocket.Payload;
|
import io.rsocket.Payload;
|
||||||
import io.rsocket.RSocket;
|
import io.rsocket.RSocket;
|
||||||
|
@ -40,6 +41,8 @@ import org.springframework.messaging.support.MessageBuilder;
|
||||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.MimeType;
|
import org.springframework.util.MimeType;
|
||||||
|
import org.springframework.util.MimeTypeUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package private implementation of {@link RSocket} that is is hooked into an
|
* Package private implementation of {@link RSocket} that is is hooked into an
|
||||||
|
@ -49,90 +52,96 @@ import org.springframework.util.MimeType;
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
class MessagingRSocket implements RSocket {
|
class MessagingRSocket extends AbstractRSocket {
|
||||||
|
|
||||||
private final ReactiveMessageChannel messageChannel;
|
private final ReactiveMessageChannel messageChannel;
|
||||||
|
|
||||||
private final RSocketRequester requester;
|
private final RSocketRequester requester;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final MimeType dataMimeType;
|
private MimeType dataMimeType;
|
||||||
|
|
||||||
private final RSocketStrategies strategies;
|
private final RSocketStrategies strategies;
|
||||||
|
|
||||||
|
|
||||||
MessagingRSocket(ReactiveMessageChannel messageChannel,
|
MessagingRSocket(ReactiveMessageChannel messageChannel,
|
||||||
RSocket sendingRSocket, @Nullable MimeType dataMimeType, RSocketStrategies strategies) {
|
RSocket sendingRSocket, @Nullable MimeType defaultDataMimeType, RSocketStrategies strategies) {
|
||||||
|
|
||||||
Assert.notNull(messageChannel, "'messageChannel' is required");
|
Assert.notNull(messageChannel, "'messageChannel' is required");
|
||||||
Assert.notNull(sendingRSocket, "'sendingRSocket' is required");
|
Assert.notNull(sendingRSocket, "'sendingRSocket' is required");
|
||||||
this.messageChannel = messageChannel;
|
this.messageChannel = messageChannel;
|
||||||
this.requester = RSocketRequester.create(sendingRSocket, dataMimeType, strategies);
|
this.requester = RSocketRequester.create(sendingRSocket, defaultDataMimeType, strategies);
|
||||||
this.dataMimeType = dataMimeType;
|
this.dataMimeType = defaultDataMimeType;
|
||||||
this.strategies = strategies;
|
this.strategies = strategies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Mono<Void> afterConnectionEstablished(ConnectionSetupPayload payload) {
|
|
||||||
return execute(payload).flatMap(flux -> flux.take(0).then());
|
public Mono<Void> handleConnectionSetupPayload(ConnectionSetupPayload payload) {
|
||||||
|
if (StringUtils.hasText(payload.dataMimeType())) {
|
||||||
|
this.dataMimeType = MimeTypeUtils.parseMimeType(payload.dataMimeType());
|
||||||
|
}
|
||||||
|
return handle(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> fireAndForget(Payload payload) {
|
public Mono<Void> fireAndForget(Payload payload) {
|
||||||
return execute(payload).flatMap(flux -> flux.take(0).then());
|
return handle(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Payload> requestResponse(Payload payload) {
|
public Mono<Payload> requestResponse(Payload payload) {
|
||||||
return execute(payload).flatMap(Flux::next);
|
return handleAndReply(payload, Flux.just(payload)).next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<Payload> requestStream(Payload payload) {
|
public Flux<Payload> requestStream(Payload payload) {
|
||||||
return execute(payload).flatMapMany(Function.identity());
|
return handleAndReply(payload, Flux.just(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<Payload> requestChannel(Publisher<Payload> payloads) {
|
public Flux<Payload> requestChannel(Publisher<Payload> payloads) {
|
||||||
return Flux.from(payloads)
|
return Flux.from(payloads)
|
||||||
.switchOnFirst((signal, inner) -> {
|
.switchOnFirst((signal, innerFlux) -> {
|
||||||
Payload first = signal.get();
|
Payload firstPayload = signal.get();
|
||||||
return first != null ? execute(first, inner).flatMapMany(Function.identity()) : inner;
|
return firstPayload == null ? innerFlux : handleAndReply(firstPayload, innerFlux);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> metadataPush(Payload payload) {
|
public Mono<Void> metadataPush(Payload payload) {
|
||||||
return null;
|
// This won't be very useful until createHeaders starting doing something more with metadata..
|
||||||
|
return handle(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Flux<Payload>> execute(Payload payload) {
|
|
||||||
return execute(payload, Flux.just(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mono<Flux<Payload>> execute(Payload firstPayload, Flux<Payload> payloads) {
|
private Mono<Void> handle(Payload payload) {
|
||||||
|
|
||||||
// TODO:
|
Message<?> message = MessageBuilder.createMessage(
|
||||||
// Since we do retain(), we need to ensure buffers are released if not consumed,
|
Mono.fromCallable(() -> wrapPayloadData(payload)),
|
||||||
// e.g. error before Flux subscribed to, no handler found, @MessageMapping ignores payload, etc.
|
createHeaders(payload, null));
|
||||||
|
|
||||||
Flux<DataBuffer> payloadDataBuffers = payloads
|
|
||||||
.map(payload -> PayloadUtils.asDataBuffer(payload, this.strategies.dataBufferFactory()))
|
|
||||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
|
||||||
|
|
||||||
MonoProcessor<Flux<Payload>> replyMono = MonoProcessor.create();
|
|
||||||
MessageHeaders headers = createHeaders(firstPayload, replyMono);
|
|
||||||
|
|
||||||
Message<?> message = MessageBuilder.createMessage(payloadDataBuffers, headers);
|
|
||||||
|
|
||||||
return this.messageChannel.send(message).flatMap(result -> result ?
|
return this.messageChannel.send(message).flatMap(result -> result ?
|
||||||
replyMono.isTerminated() ? replyMono : Mono.empty() :
|
Mono.empty() : Mono.error(new MessageDeliveryException("RSocket request not handled")));
|
||||||
Mono.error(new MessageDeliveryException("RSocket interaction not handled")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageHeaders createHeaders(Payload payload, MonoProcessor<?> replyMono) {
|
private Flux<Payload> handleAndReply(Payload firstPayload, Flux<Payload> payloads) {
|
||||||
|
|
||||||
|
MonoProcessor<Flux<Payload>> replyMono = MonoProcessor.create();
|
||||||
|
|
||||||
|
Message<?> message = MessageBuilder.createMessage(
|
||||||
|
payloads.map(this::wrapPayloadData).doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release),
|
||||||
|
createHeaders(firstPayload, replyMono));
|
||||||
|
|
||||||
|
return this.messageChannel.send(message).flatMapMany(result ->
|
||||||
|
result && replyMono.isTerminated() ? replyMono.flatMapMany(Function.identity()) :
|
||||||
|
Mono.error(new MessageDeliveryException("RSocket request not handled")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageHeaders createHeaders(Payload payload, @Nullable MonoProcessor<?> replyMono) {
|
||||||
|
|
||||||
|
// TODO:
|
||||||
// For now treat the metadata as a simple string with routing information.
|
// For now treat the metadata as a simple string with routing information.
|
||||||
// We'll have to get more sophisticated once the routing extension is completed.
|
// We'll have to get more sophisticated once the routing extension is completed.
|
||||||
// https://github.com/rsocket/rsocket-java/issues/568
|
// https://github.com/rsocket/rsocket-java/issues/568
|
||||||
|
@ -147,7 +156,10 @@ class MessagingRSocket implements RSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.setHeader(RSocketRequesterMethodArgumentResolver.RSOCKET_REQUESTER_HEADER, this.requester);
|
headers.setHeader(RSocketRequesterMethodArgumentResolver.RSOCKET_REQUESTER_HEADER, this.requester);
|
||||||
headers.setHeader(RSocketPayloadReturnValueHandler.RESPONSE_HEADER, replyMono);
|
|
||||||
|
if (replyMono != null) {
|
||||||
|
headers.setHeader(RSocketPayloadReturnValueHandler.RESPONSE_HEADER, replyMono);
|
||||||
|
}
|
||||||
|
|
||||||
DataBufferFactory bufferFactory = this.strategies.dataBufferFactory();
|
DataBufferFactory bufferFactory = this.strategies.dataBufferFactory();
|
||||||
headers.setHeader(HandlerMethodReturnValueHandler.DATA_BUFFER_FACTORY_HEADER, bufferFactory);
|
headers.setHeader(HandlerMethodReturnValueHandler.DATA_BUFFER_FACTORY_HEADER, bufferFactory);
|
||||||
|
@ -155,13 +167,8 @@ class MessagingRSocket implements RSocket {
|
||||||
return headers.getMessageHeaders();
|
return headers.getMessageHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private DataBuffer wrapPayloadData(Payload payload) {
|
||||||
public Mono<Void> onClose() {
|
return PayloadUtils.wrapPayloadData(payload, this.strategies.dataBufferFactory());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.messaging.rsocket;
|
package org.springframework.messaging.rsocket;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.rsocket.Payload;
|
import io.rsocket.Payload;
|
||||||
import io.rsocket.util.ByteBufPayload;
|
import io.rsocket.util.ByteBufPayload;
|
||||||
import io.rsocket.util.DefaultPayload;
|
import io.rsocket.util.DefaultPayload;
|
||||||
|
@ -44,7 +41,7 @@ abstract class PayloadUtils {
|
||||||
* @param bufferFactory the BufferFactory to use to wrap
|
* @param bufferFactory the BufferFactory to use to wrap
|
||||||
* @return the DataBuffer wrapper
|
* @return the DataBuffer wrapper
|
||||||
*/
|
*/
|
||||||
public static DataBuffer asDataBuffer(Payload payload, DataBufferFactory bufferFactory) {
|
public static DataBuffer wrapPayloadData(Payload payload, DataBufferFactory bufferFactory) {
|
||||||
if (bufferFactory instanceof NettyDataBufferFactory) {
|
if (bufferFactory instanceof NettyDataBufferFactory) {
|
||||||
return ((NettyDataBufferFactory) bufferFactory).wrap(payload.retain().sliceData());
|
return ((NettyDataBufferFactory) bufferFactory).wrap(payload.retain().sliceData());
|
||||||
}
|
}
|
||||||
|
@ -59,12 +56,16 @@ abstract class PayloadUtils {
|
||||||
* @param data the data part for the payload
|
* @param data the data part for the payload
|
||||||
* @return the created Payload
|
* @return the created Payload
|
||||||
*/
|
*/
|
||||||
public static Payload asPayload(DataBuffer metadata, DataBuffer data) {
|
public static Payload createPayload(DataBuffer metadata, DataBuffer data) {
|
||||||
if (metadata instanceof NettyDataBuffer && data instanceof NettyDataBuffer) {
|
if (metadata instanceof NettyDataBuffer && data instanceof NettyDataBuffer) {
|
||||||
return ByteBufPayload.create(getByteBuf(data), getByteBuf(metadata));
|
return ByteBufPayload.create(
|
||||||
|
((NettyDataBuffer) data).getNativeBuffer(),
|
||||||
|
((NettyDataBuffer) metadata).getNativeBuffer());
|
||||||
}
|
}
|
||||||
else if (metadata instanceof DefaultDataBuffer && data instanceof DefaultDataBuffer) {
|
else if (metadata instanceof DefaultDataBuffer && data instanceof DefaultDataBuffer) {
|
||||||
return DefaultPayload.create(getByteBuffer(data), getByteBuffer(metadata));
|
return DefaultPayload.create(
|
||||||
|
((DefaultDataBuffer) data).getNativeBuffer(),
|
||||||
|
((DefaultDataBuffer) metadata).getNativeBuffer());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return DefaultPayload.create(data.asByteBuffer(), metadata.asByteBuffer());
|
return DefaultPayload.create(data.asByteBuffer(), metadata.asByteBuffer());
|
||||||
|
@ -76,24 +77,16 @@ abstract class PayloadUtils {
|
||||||
* @param data the data part for the payload
|
* @param data the data part for the payload
|
||||||
* @return the created Payload
|
* @return the created Payload
|
||||||
*/
|
*/
|
||||||
public static Payload asPayload(DataBuffer data) {
|
public static Payload createPayload(DataBuffer data) {
|
||||||
if (data instanceof NettyDataBuffer) {
|
if (data instanceof NettyDataBuffer) {
|
||||||
return ByteBufPayload.create(getByteBuf(data));
|
return ByteBufPayload.create(((NettyDataBuffer) data).getNativeBuffer());
|
||||||
}
|
}
|
||||||
else if (data instanceof DefaultDataBuffer) {
|
else if (data instanceof DefaultDataBuffer) {
|
||||||
return DefaultPayload.create(getByteBuffer(data));
|
return DefaultPayload.create(((DefaultDataBuffer) data).getNativeBuffer());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return DefaultPayload.create(data.asByteBuffer());
|
return DefaultPayload.create(data.asByteBuffer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf getByteBuf(DataBuffer dataBuffer) {
|
|
||||||
return ((NettyDataBuffer) dataBuffer).getNativeBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static
|
|
||||||
ByteBuffer getByteBuffer(DataBuffer dataBuffer) {
|
|
||||||
return ((DefaultDataBuffer) dataBuffer).getNativeBuffer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,10 @@ import java.util.List;
|
||||||
import org.springframework.core.codec.Decoder;
|
import org.springframework.core.codec.Decoder;
|
||||||
import org.springframework.core.codec.Encoder;
|
import org.springframework.core.codec.Encoder;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.messaging.Message;
|
|
||||||
import org.springframework.messaging.MessageDeliveryException;
|
|
||||||
import org.springframework.messaging.ReactiveSubscribableChannel;
|
import org.springframework.messaging.ReactiveSubscribableChannel;
|
||||||
import org.springframework.messaging.handler.annotation.support.reactive.MessageMappingMessageHandler;
|
import org.springframework.messaging.handler.annotation.support.reactive.MessageMappingMessageHandler;
|
||||||
import org.springframework.messaging.handler.invocation.reactive.HandlerMethodReturnValueHandler;
|
import org.springframework.messaging.handler.invocation.reactive.HandlerMethodReturnValueHandler;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSocket-specific extension of {@link MessageMappingMessageHandler}.
|
* RSocket-specific extension of {@link MessageMappingMessageHandler}.
|
||||||
|
@ -124,14 +121,4 @@ public class RSocketMessageHandler extends MessageMappingMessageHandler {
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void handleNoMatch(@Nullable String destination, Message<?> message) {
|
|
||||||
// Ignore empty destination, probably the ConnectionSetupPayload
|
|
||||||
if (!StringUtils.isEmpty(destination)) {
|
|
||||||
super.handleNoMatch(destination, message);
|
|
||||||
throw new MessageDeliveryException("No handler for '" + destination + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class RSocketPayloadReturnValueHandler extends AbstractEncoderMethodRetur
|
||||||
Assert.isInstanceOf(MonoProcessor.class, headerValue, "Expected MonoProcessor");
|
Assert.isInstanceOf(MonoProcessor.class, headerValue, "Expected MonoProcessor");
|
||||||
|
|
||||||
MonoProcessor<Flux<Payload>> monoProcessor = (MonoProcessor<Flux<Payload>>) headerValue;
|
MonoProcessor<Flux<Payload>> monoProcessor = (MonoProcessor<Flux<Payload>>) headerValue;
|
||||||
monoProcessor.onNext(encodedContent.map(PayloadUtils::asPayload));
|
monoProcessor.onNext(encodedContent.map(PayloadUtils::createPayload));
|
||||||
monoProcessor.onComplete();
|
monoProcessor.onComplete();
|
||||||
|
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
|
|
|
@ -199,7 +199,7 @@ public class DefaultRSocketRequesterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Payload toPayload(String value) {
|
private Payload toPayload(String value) {
|
||||||
return PayloadUtils.asPayload(bufferFactory.wrap(value.getBytes(StandardCharsets.UTF_8)));
|
return PayloadUtils.createPayload(bufferFactory.wrap(value.getBytes(StandardCharsets.UTF_8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.codec.CharSequenceEncoder;
|
import org.springframework.core.codec.CharSequenceEncoder;
|
||||||
import org.springframework.core.codec.StringDecoder;
|
import org.springframework.core.codec.StringDecoder;
|
||||||
|
import org.springframework.messaging.MessageDeliveryException;
|
||||||
import org.springframework.messaging.ReactiveMessageChannel;
|
import org.springframework.messaging.ReactiveMessageChannel;
|
||||||
import org.springframework.messaging.ReactiveSubscribableChannel;
|
import org.springframework.messaging.ReactiveSubscribableChannel;
|
||||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
|
@ -169,6 +170,12 @@ public class RSocketClientToServerIntegrationTests {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noMatchingRoute() {
|
||||||
|
Mono<String> result = requester.route("invalid").data("anything").retrieveMono(String.class);
|
||||||
|
StepVerifier.create(result).verifyErrorMessage("RSocket request not handled");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
static class ServerController {
|
static class ServerController {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.rsocket.Closeable;
|
import io.rsocket.Closeable;
|
||||||
import io.rsocket.Payload;
|
|
||||||
import io.rsocket.RSocket;
|
import io.rsocket.RSocket;
|
||||||
import io.rsocket.RSocketFactory;
|
import io.rsocket.RSocketFactory;
|
||||||
import io.rsocket.transport.netty.client.TcpClientTransport;
|
import io.rsocket.transport.netty.client.TcpClientTransport;
|
||||||
|
@ -140,13 +139,22 @@ public class RSocketServerToClientIntegrationTests {
|
||||||
volatile MonoProcessor<Void> result;
|
volatile MonoProcessor<Void> result;
|
||||||
|
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
this.result = MonoProcessor.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void await(Duration duration) {
|
||||||
|
this.result.block(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@MessageMapping("connect.echo")
|
@MessageMapping("connect.echo")
|
||||||
void echo(RSocketRequester requester) {
|
void echo(RSocketRequester requester) {
|
||||||
runTest(() -> {
|
runTest(() -> {
|
||||||
Flux<String> result = Flux.range(1, 3).concatMap(i ->
|
Flux<String> flux = Flux.range(1, 3).concatMap(i ->
|
||||||
requester.route("echo").data("Hello " + i).retrieveMono(String.class));
|
requester.route("echo").data("Hello " + i).retrieveMono(String.class));
|
||||||
|
|
||||||
StepVerifier.create(result)
|
StepVerifier.create(flux)
|
||||||
.expectNext("Hello 1")
|
.expectNext("Hello 1")
|
||||||
.expectNext("Hello 2")
|
.expectNext("Hello 2")
|
||||||
.expectNext("Hello 3")
|
.expectNext("Hello 3")
|
||||||
|
@ -157,10 +165,10 @@ public class RSocketServerToClientIntegrationTests {
|
||||||
@MessageMapping("connect.echo-async")
|
@MessageMapping("connect.echo-async")
|
||||||
void echoAsync(RSocketRequester requester) {
|
void echoAsync(RSocketRequester requester) {
|
||||||
runTest(() -> {
|
runTest(() -> {
|
||||||
Flux<String> result = Flux.range(1, 3).concatMap(i ->
|
Flux<String> flux = Flux.range(1, 3).concatMap(i ->
|
||||||
requester.route("echo-async").data("Hello " + i).retrieveMono(String.class));
|
requester.route("echo-async").data("Hello " + i).retrieveMono(String.class));
|
||||||
|
|
||||||
StepVerifier.create(result)
|
StepVerifier.create(flux)
|
||||||
.expectNext("Hello 1 async")
|
.expectNext("Hello 1 async")
|
||||||
.expectNext("Hello 2 async")
|
.expectNext("Hello 2 async")
|
||||||
.expectNext("Hello 3 async")
|
.expectNext("Hello 3 async")
|
||||||
|
@ -171,9 +179,9 @@ public class RSocketServerToClientIntegrationTests {
|
||||||
@MessageMapping("connect.echo-stream")
|
@MessageMapping("connect.echo-stream")
|
||||||
void echoStream(RSocketRequester requester) {
|
void echoStream(RSocketRequester requester) {
|
||||||
runTest(() -> {
|
runTest(() -> {
|
||||||
Flux<String> result = requester.route("echo-stream").data("Hello").retrieveFlux(String.class);
|
Flux<String> flux = requester.route("echo-stream").data("Hello").retrieveFlux(String.class);
|
||||||
|
|
||||||
StepVerifier.create(result)
|
StepVerifier.create(flux)
|
||||||
.expectNext("Hello 0")
|
.expectNext("Hello 0")
|
||||||
.expectNextCount(5)
|
.expectNextCount(5)
|
||||||
.expectNext("Hello 6")
|
.expectNext("Hello 6")
|
||||||
|
@ -186,11 +194,11 @@ public class RSocketServerToClientIntegrationTests {
|
||||||
@MessageMapping("connect.echo-channel")
|
@MessageMapping("connect.echo-channel")
|
||||||
void echoChannel(RSocketRequester requester) {
|
void echoChannel(RSocketRequester requester) {
|
||||||
runTest(() -> {
|
runTest(() -> {
|
||||||
Flux<String> result = requester.route("echo-channel")
|
Flux<String> flux = requester.route("echo-channel")
|
||||||
.data(Flux.range(1, 10).map(i -> "Hello " + i), String.class)
|
.data(Flux.range(1, 10).map(i -> "Hello " + i), String.class)
|
||||||
.retrieveFlux(String.class);
|
.retrieveFlux(String.class);
|
||||||
|
|
||||||
StepVerifier.create(result)
|
StepVerifier.create(flux)
|
||||||
.expectNext("Hello 1 async")
|
.expectNext("Hello 1 async")
|
||||||
.expectNextCount(7)
|
.expectNextCount(7)
|
||||||
.expectNext("Hello 9 async")
|
.expectNext("Hello 9 async")
|
||||||
|
@ -207,19 +215,6 @@ public class RSocketServerToClientIntegrationTests {
|
||||||
.subscribeOn(Schedulers.elastic())
|
.subscribeOn(Schedulers.elastic())
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Payload payload(String destination, String data) {
|
|
||||||
return DefaultPayload.create(data, destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
this.result = MonoProcessor.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void await(Duration duration) {
|
|
||||||
this.result.block(duration);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue