GH-3207: RSocket inbound: decode each flux item (#3208)

* GH-3207: RSocket inbound: decode each flux item

Fixes https://github.com/spring-projects/spring-integration/issues/3207

Previously an incoming RSocket Publisher has been decoded as a single unit
leading to extra work on the client side, e.g. a delimiter has to be provided
to treat each payload item as independent

* To have a consistency with Spring Messaging and its `PayloadMethodArgumentResolver`
change an `RSocketInboundGateway` to process inbound payloads as `Flux` and decode
each item independently.
* Change `RSocketDslTests` to remove delimiters and make it consistent with the regular
`RSocketRequester` client

* * Add `decodeFluxAsUnit` option into `RSocketInboundGateway`
* Document the change
This commit is contained in:
Artem Bilan
2020-03-10 16:07:01 -04:00
committed by GitHub
parent 1511dd8748
commit c45cc66a84
9 changed files with 103 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 the original author or authors.
* Copyright 2019-2020 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.
@@ -37,7 +37,6 @@ public class RSocketInboundGatewayParser extends AbstractInboundGatewayParser {
private static final List<String> NON_ELIGIBLE_ATTRIBUTES =
Arrays.asList("path",
"interaction-models",
"rsocket-strategies",
"rsocket-connector",
"request-element-type");
@@ -61,7 +60,6 @@ public class RSocketInboundGatewayParser extends AbstractInboundGatewayParser {
"rSocketStrategies");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "rsocket-connector",
"RSocketConnector");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "interaction-models");
}
}

View File

@@ -23,6 +23,8 @@ import org.springframework.integration.rsocket.RSocketInteractionModel;
import org.springframework.integration.rsocket.inbound.RSocketInboundGateway;
import org.springframework.messaging.rsocket.RSocketStrategies;
import reactor.core.publisher.Flux;
/**
* The {@link MessagingGatewaySpec} implementation for the {@link RSocketInboundGateway}.
*
@@ -82,4 +84,16 @@ public class RSocketInboundGatewaySpec extends MessagingGatewaySpec<RSocketInbou
return this;
}
/**
* Configure an option to decode an incoming {@link Flux} as a single unit or each its event separately.
* @param decodeFluxAsUnit decode incoming {@link Flux} as a single unit or each event separately.
* @return the spec
* @since 5.3
* @see RSocketInboundGateway#setDecodeFluxAsUnit(boolean)
*/
public RSocketInboundGatewaySpec decodeFluxAsUnit(boolean decodeFluxAsUnit) {
this.target.setDecodeFluxAsUnit(decodeFluxAsUnit);
return this;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019 the original author or authors.
* Copyright 2019-2020 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.
@@ -85,6 +85,8 @@ public class RSocketInboundGateway extends MessagingGatewaySupport implements In
@Nullable
private ResolvableType requestElementType;
private boolean decodeFluxAsUnit;
/**
* Instantiate based on the provided path patterns to map this endpoint for incoming RSocket requests.
* @param pathArg the mapping patterns to use.
@@ -160,6 +162,20 @@ public class RSocketInboundGateway extends MessagingGatewaySupport implements In
this.requestElementType = requestElementType;
}
/**
* Configure an option to decode an incoming {@link Flux} as a single unit or each its event separately.
* Defaults to {@code false} for consistency with Spring Messaging {@code @MessageMapping}.
* The target {@link Flux} decoding logic depends on the {@link Decoder} selected.
* For example a {@link org.springframework.core.codec.StringDecoder} requires a new line separator to
* be present in the stream to indicate a byte buffer end.
* @param decodeFluxAsUnit decode incoming {@link Flux} as a single unit or each event separately.
* @since 5.3
* @see Decoder#decode(Publisher, ResolvableType, MimeType, java.util.Map)
*/
public void setDecodeFluxAsUnit(boolean decodeFluxAsUnit) {
this.decodeFluxAsUnit = decodeFluxAsUnit;
}
@Override
protected void onInit() {
super.onInit();
@@ -219,14 +235,17 @@ public class RSocketInboundGateway extends MessagingGatewaySupport implements In
@SuppressWarnings("unchecked")
@Nullable
private Object decodePayload(Message<?> requestMessage) {
ResolvableType elementType = this.requestElementType;
ResolvableType elementType;
MimeType mimeType = requestMessage.getHeaders().get(MessageHeaders.CONTENT_TYPE, MimeType.class);
if (elementType == null) {
if (this.requestElementType == null) {
elementType =
mimeType != null && "text".equals(mimeType.getType())
? ResolvableType.forClass(String.class)
: ResolvableType.forClass(byte[].class);
}
else {
elementType = this.requestElementType;
}
Object payload = requestMessage.getPayload();
@@ -235,9 +254,18 @@ public class RSocketInboundGateway extends MessagingGatewaySupport implements In
if (payload instanceof DataBuffer) {
return decoder.decode((DataBuffer) payload, elementType, mimeType, null);
}
else {
else if (this.decodeFluxAsUnit) {
return decoder.decode((Publisher<DataBuffer>) payload, elementType, mimeType, null);
}
else {
return Flux.from((Publisher<DataBuffer>) payload)
.handle((buffer, synchronousSink) -> {
Object value = decoder.decode(buffer, elementType, mimeType, null);
if (value != null) {
synchronousSink.next(value);
}
});
}
}
private Flux<DataBuffer> createReply(Object reply, Message<?> requestMessage) {

View File

@@ -93,6 +93,16 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="decode-flux-as-unit" default="false">
<xsd:annotation>
<xsd:documentation>
Decode incoming Flux as a single unit or each event separately.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string"/>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>