|
|
|
@ -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 |
|
|
|
return this.messageChannel.send(message).flatMap(result -> result ? |
|
|
|
.map(payload -> PayloadUtils.asDataBuffer(payload, this.strategies.dataBufferFactory())) |
|
|
|
Mono.empty() : Mono.error(new MessageDeliveryException("RSocket request not handled"))); |
|
|
|
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Flux<Payload> handleAndReply(Payload firstPayload, Flux<Payload> payloads) { |
|
|
|
|
|
|
|
|
|
|
|
MonoProcessor<Flux<Payload>> replyMono = MonoProcessor.create(); |
|
|
|
MonoProcessor<Flux<Payload>> replyMono = MonoProcessor.create(); |
|
|
|
MessageHeaders headers = createHeaders(firstPayload, replyMono); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Message<?> message = MessageBuilder.createMessage(payloadDataBuffers, headers); |
|
|
|
Message<?> message = MessageBuilder.createMessage( |
|
|
|
|
|
|
|
payloads.map(this::wrapPayloadData).doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release), |
|
|
|
|
|
|
|
createHeaders(firstPayload, replyMono)); |
|
|
|
|
|
|
|
|
|
|
|
return this.messageChannel.send(message).flatMap(result -> result ? |
|
|
|
return this.messageChannel.send(message).flatMapMany(result -> |
|
|
|
replyMono.isTerminated() ? replyMono : Mono.empty() : |
|
|
|
result && replyMono.isTerminated() ? replyMono.flatMapMany(Function.identity()) : |
|
|
|
Mono.error(new MessageDeliveryException("RSocket interaction not handled"))); |
|
|
|
Mono.error(new MessageDeliveryException("RSocket request not handled"))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private MessageHeaders createHeaders(Payload payload, MonoProcessor<?> replyMono) { |
|
|
|
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); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (replyMono != null) { |
|
|
|
headers.setHeader(RSocketPayloadReturnValueHandler.RESPONSE_HEADER, replyMono); |
|
|
|
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() { |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|