Skip to content

Commit 2bb5105

Browse files
committed
Merge branch 'rs'
2 parents c3c152f + e19e36a commit 2bb5105

File tree

11 files changed

+501
-294
lines changed

11 files changed

+501
-294
lines changed

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/MessageMappingMessageHandler.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public class MessageMappingMessageHandler extends AbstractMethodMessageHandler<C
8888
@Nullable
8989
private Validator validator;
9090

91+
@Nullable
9192
private RouteMatcher routeMatcher;
9293

9394
private ConversionService conversionService = new DefaultFormattingConversionService();
@@ -97,9 +98,6 @@ public class MessageMappingMessageHandler extends AbstractMethodMessageHandler<C
9798

9899

99100
public MessageMappingMessageHandler() {
100-
AntPathMatcher pathMatcher = new AntPathMatcher();
101-
pathMatcher.setPathSeparator(".");
102-
this.routeMatcher = new SimpleRouteMatcher(pathMatcher);
103101
setHandlerPredicate(type -> AnnotatedElementUtils.hasAnnotation(type, Controller.class));
104102
}
105103

@@ -150,7 +148,9 @@ public void setRouteMatcher(RouteMatcher routeMatcher) {
150148

151149
/**
152150
* Return the {@code RouteMatcher} used to map messages to handlers.
151+
* May be {@code null} before component is initialized.
153152
*/
153+
@Nullable
154154
public RouteMatcher getRouteMatcher() {
155155
return this.routeMatcher;
156156
}
@@ -203,6 +203,12 @@ protected List<? extends HandlerMethodArgumentResolver> initArgumentResolvers()
203203
resolvers.add(new PayloadMethodArgumentResolver(
204204
getDecoders(), this.validator, getReactiveAdapterRegistry(), true));
205205

206+
if (this.routeMatcher == null) {
207+
AntPathMatcher pathMatcher = new AntPathMatcher();
208+
pathMatcher.setPathSeparator(".");
209+
this.routeMatcher = new SimpleRouteMatcher(pathMatcher);
210+
}
211+
206212
return resolvers;
207213
}
208214

spring-messaging/src/main/java/org/springframework/messaging/rsocket/DefaultRSocketRequesterBuilder.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
import java.net.URI;
2020
import java.util.ArrayList;
21+
import java.util.Collections;
2122
import java.util.List;
2223
import java.util.function.Consumer;
23-
import java.util.stream.Stream;
2424

2525
import io.rsocket.RSocketFactory;
2626
import io.rsocket.frame.decoder.PayloadDecoder;
@@ -29,6 +29,9 @@
2929
import io.rsocket.transport.netty.client.WebsocketClientTransport;
3030
import reactor.core.publisher.Mono;
3131

32+
import org.springframework.core.codec.Decoder;
33+
import org.springframework.core.codec.StringDecoder;
34+
import org.springframework.core.io.buffer.NettyDataBufferFactory;
3235
import org.springframework.lang.Nullable;
3336
import org.springframework.util.Assert;
3437
import org.springframework.util.MimeType;
@@ -110,7 +113,10 @@ private Mono<RSocketRequester> doConnect(ClientTransport transport) {
110113
MimeType dataMimeType = getDataMimeType(rsocketStrategies);
111114
rsocketFactory.dataMimeType(dataMimeType.toString());
112115
rsocketFactory.metadataMimeType(this.metadataMimeType.toString());
113-
rsocketFactory.frameDecoder(PayloadDecoder.ZERO_COPY);
116+
117+
if (rsocketStrategies.dataBufferFactory() instanceof NettyDataBufferFactory) {
118+
rsocketFactory.frameDecoder(PayloadDecoder.ZERO_COPY);
119+
}
114120

115121
this.rsocketFactoryConfigurers.forEach(configurer -> {
116122
configurer.configureWithStrategies(rsocketStrategies);
@@ -139,16 +145,35 @@ private MimeType getDataMimeType(RSocketStrategies strategies) {
139145
if (this.dataMimeType != null) {
140146
return this.dataMimeType;
141147
}
142-
return Stream
143-
.concat(
144-
strategies.encoders().stream()
145-
.flatMap(encoder -> encoder.getEncodableMimeTypes().stream()),
146-
strategies.decoders().stream()
147-
.flatMap(encoder -> encoder.getDecodableMimeTypes().stream())
148-
)
149-
.filter(MimeType::isConcrete)
150-
.findFirst()
151-
.orElseThrow(() -> new IllegalArgumentException("Failed to select data MimeType to use."));
148+
// Look for non-basic Decoder (e.g. CBOR, Protobuf)
149+
MimeType selected = null;
150+
List<Decoder<?>> decoders = strategies.decoders();
151+
for (Decoder<?> candidate : decoders) {
152+
if (!isCoreCodec(candidate) && !candidate.getDecodableMimeTypes().isEmpty()) {
153+
Assert.state(selected == null,
154+
() -> "Cannot select default data MimeType based on configured decoders: " + decoders);
155+
selected = getMimeType(candidate);
156+
}
157+
}
158+
if (selected != null) {
159+
return selected;
160+
}
161+
// Fall back on 1st decoder (e.g. String)
162+
for (Decoder<?> decoder : decoders) {
163+
if (!decoder.getDecodableMimeTypes().isEmpty()) {
164+
return getMimeType(decoder);
165+
}
166+
}
167+
throw new IllegalArgumentException("Failed to select data MimeType to use.");
168+
}
169+
170+
private static boolean isCoreCodec(Object codec) {
171+
return codec.getClass().getPackage().equals(StringDecoder.class.getPackage());
172+
}
173+
174+
private static MimeType getMimeType(Decoder<?> decoder) {
175+
MimeType mimeType = decoder.getDecodableMimeTypes().get(0);
176+
return new MimeType(mimeType, Collections.emptyMap());
152177
}
153178

154179
}

spring-messaging/src/main/java/org/springframework/messaging/rsocket/DefaultRSocketStrategies.java

Lines changed: 97 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,27 @@
2222
import java.util.List;
2323
import java.util.function.Consumer;
2424

25+
import io.netty.buffer.PooledByteBufAllocator;
26+
2527
import org.springframework.core.ReactiveAdapterRegistry;
28+
import org.springframework.core.codec.ByteArrayDecoder;
29+
import org.springframework.core.codec.ByteArrayEncoder;
30+
import org.springframework.core.codec.ByteBufferDecoder;
31+
import org.springframework.core.codec.ByteBufferEncoder;
32+
import org.springframework.core.codec.CharSequenceEncoder;
33+
import org.springframework.core.codec.DataBufferDecoder;
34+
import org.springframework.core.codec.DataBufferEncoder;
2635
import org.springframework.core.codec.Decoder;
2736
import org.springframework.core.codec.Encoder;
37+
import org.springframework.core.codec.StringDecoder;
2838
import org.springframework.core.io.buffer.DataBufferFactory;
29-
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
39+
import org.springframework.core.io.buffer.NettyDataBufferFactory;
3040
import org.springframework.lang.Nullable;
41+
import org.springframework.util.AntPathMatcher;
3142
import org.springframework.util.Assert;
43+
import org.springframework.util.MimeTypeUtils;
44+
import org.springframework.util.RouteMatcher;
45+
import org.springframework.util.SimpleRouteMatcher;
3246

3347
/**
3448
* Default, package-private {@link RSocketStrategies} implementation.
@@ -42,18 +56,25 @@ final class DefaultRSocketStrategies implements RSocketStrategies {
4256

4357
private final List<Decoder<?>> decoders;
4458

45-
private final ReactiveAdapterRegistry adapterRegistry;
59+
private final RouteMatcher routeMatcher;
60+
61+
private final MetadataExtractor metadataExtractor;
4662

4763
private final DataBufferFactory bufferFactory;
4864

65+
private final ReactiveAdapterRegistry adapterRegistry;
66+
4967

5068
private DefaultRSocketStrategies(List<Encoder<?>> encoders, List<Decoder<?>> decoders,
51-
ReactiveAdapterRegistry adapterRegistry, DataBufferFactory bufferFactory) {
69+
RouteMatcher routeMatcher, MetadataExtractor metadataExtractor,
70+
DataBufferFactory bufferFactory, ReactiveAdapterRegistry adapterRegistry) {
5271

5372
this.encoders = Collections.unmodifiableList(encoders);
5473
this.decoders = Collections.unmodifiableList(decoders);
55-
this.adapterRegistry = adapterRegistry;
74+
this.routeMatcher = routeMatcher;
75+
this.metadataExtractor = metadataExtractor;
5676
this.bufferFactory = bufferFactory;
77+
this.adapterRegistry = adapterRegistry;
5778
}
5879

5980

@@ -68,15 +89,25 @@ public List<Decoder<?>> decoders() {
6889
}
6990

7091
@Override
71-
public ReactiveAdapterRegistry reactiveAdapterRegistry() {
72-
return this.adapterRegistry;
92+
public RouteMatcher routeMatcher() {
93+
return this.routeMatcher;
94+
}
95+
96+
@Override
97+
public MetadataExtractor metadataExtractor() {
98+
return this.metadataExtractor;
7399
}
74100

75101
@Override
76102
public DataBufferFactory dataBufferFactory() {
77103
return this.bufferFactory;
78104
}
79105

106+
@Override
107+
public ReactiveAdapterRegistry reactiveAdapterRegistry() {
108+
return this.adapterRegistry;
109+
}
110+
80111

81112
/**
82113
* Default RSocketStrategies.Builder implementation.
@@ -87,21 +118,42 @@ static class DefaultRSocketStrategiesBuilder implements RSocketStrategies.Builde
87118

88119
private final List<Decoder<?>> decoders = new ArrayList<>();
89120

121+
@Nullable
122+
private RouteMatcher routeMatcher;
123+
124+
@Nullable
125+
private MetadataExtractor metadataExtractor;
126+
90127
private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
91128

92129
@Nullable
93-
private DataBufferFactory dataBufferFactory;
130+
private DataBufferFactory bufferFactory;
131+
132+
133+
DefaultRSocketStrategiesBuilder() {
134+
135+
// Order of decoders may be significant for default data MimeType
136+
// selection in RSocketRequester.Builder
137+
138+
this.decoders.add(StringDecoder.allMimeTypes());
139+
this.decoders.add(new ByteBufferDecoder());
140+
this.decoders.add(new ByteArrayDecoder());
141+
this.decoders.add(new DataBufferDecoder());
94142

95-
public DefaultRSocketStrategiesBuilder() {
143+
this.encoders.add(CharSequenceEncoder.allMimeTypes());
144+
this.encoders.add(new ByteBufferEncoder());
145+
this.encoders.add(new ByteArrayEncoder());
146+
this.encoders.add(new DataBufferEncoder());
96147
}
97148

98-
public DefaultRSocketStrategiesBuilder(RSocketStrategies other) {
149+
DefaultRSocketStrategiesBuilder(RSocketStrategies other) {
99150
this.encoders.addAll(other.encoders());
100151
this.decoders.addAll(other.decoders());
101152
this.adapterRegistry = other.reactiveAdapterRegistry();
102-
this.dataBufferFactory = other.dataBufferFactory();
153+
this.bufferFactory = other.dataBufferFactory();
103154
}
104155

156+
105157
@Override
106158
public Builder encoder(Encoder<?>... encoders) {
107159
this.encoders.addAll(Arrays.asList(encoders));
@@ -126,6 +178,18 @@ public Builder decoders(Consumer<List<Decoder<?>>> consumer) {
126178
return this;
127179
}
128180

181+
@Override
182+
public Builder routeMatcher(RouteMatcher routeMatcher) {
183+
this.routeMatcher = routeMatcher;
184+
return this;
185+
}
186+
187+
@Override
188+
public Builder metadataExtractor(MetadataExtractor metadataExtractor) {
189+
this.metadataExtractor = metadataExtractor;
190+
return this;
191+
}
192+
129193
@Override
130194
public Builder reactiveAdapterStrategy(ReactiveAdapterRegistry registry) {
131195
Assert.notNull(registry, "ReactiveAdapterRegistry is required");
@@ -135,14 +199,34 @@ public Builder reactiveAdapterStrategy(ReactiveAdapterRegistry registry) {
135199

136200
@Override
137201
public Builder dataBufferFactory(DataBufferFactory bufferFactory) {
138-
this.dataBufferFactory = bufferFactory;
202+
this.bufferFactory = bufferFactory;
139203
return this;
140204
}
141205

142206
@Override
143207
public RSocketStrategies build() {
144-
return new DefaultRSocketStrategies(this.encoders, this.decoders, this.adapterRegistry,
145-
this.dataBufferFactory != null ? this.dataBufferFactory : new DefaultDataBufferFactory());
208+
return new DefaultRSocketStrategies(
209+
this.encoders, this.decoders,
210+
this.routeMatcher != null ? this.routeMatcher : initRouteMatcher(),
211+
this.metadataExtractor != null ? this.metadataExtractor : initMetadataExtractor(),
212+
this.bufferFactory != null ? this.bufferFactory : initBufferFactory(),
213+
this.adapterRegistry);
214+
}
215+
216+
private RouteMatcher initRouteMatcher() {
217+
AntPathMatcher pathMatcher = new AntPathMatcher();
218+
pathMatcher.setPathSeparator(".");
219+
return new SimpleRouteMatcher(pathMatcher);
220+
}
221+
222+
private MetadataExtractor initMetadataExtractor() {
223+
DefaultMetadataExtractor extractor = new DefaultMetadataExtractor();
224+
extractor.metadataToExtract(MimeTypeUtils.TEXT_PLAIN, String.class, MetadataExtractor.ROUTE_KEY);
225+
return extractor;
226+
}
227+
228+
private DataBufferFactory initBufferFactory() {
229+
return new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT);
146230
}
147231
}
148232

spring-messaging/src/main/java/org/springframework/messaging/rsocket/RSocketRequester.java

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828

2929
import org.springframework.core.ParameterizedTypeReference;
3030
import org.springframework.core.ReactiveAdapterRegistry;
31+
import org.springframework.core.codec.Decoder;
3132
import org.springframework.lang.Nullable;
32-
import org.springframework.messaging.rsocket.annotation.support.AnnotationClientResponderConfigurer;
33+
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
3334
import org.springframework.util.MimeType;
3435

3536
/**
@@ -125,32 +126,32 @@ static RSocketRequester wrap(
125126
interface Builder {
126127

127128
/**
128-
* Configure the MimeType for payload data which is then specified
129-
* on the {@code SETUP} frame and applies to the whole connection.
130-
* <p>By default this is set to the first concrete mime type supported
131-
* by the configured encoders and decoders.
132-
* @param mimeType the data MimeType to use
129+
* Configure the payload data MimeType to specify on the {@code SETUP}
130+
* frame that applies to the whole connection.
131+
* <p>If this is not set, the builder will try to select the mime type
132+
* based on the presence of a single
133+
* {@link RSocketStrategies.Builder#decoder(Decoder[]) non-default}
134+
* {@code Decoder}, or the first default decoder otherwise
135+
* (i.e. {@code String}) if no others are configured.
133136
*/
134137
RSocketRequester.Builder dataMimeType(@Nullable MimeType mimeType);
135138

136139
/**
137-
* Configure the MimeType for payload metadata which is then specified
138-
* on the {@code SETUP} frame and applies to the whole connection.
140+
* Configure the payload metadata MimeType to specify on the {@code SETUP}
141+
* frame and applies to the whole connection.
139142
* <p>By default this is set to
140143
* {@code "message/x.rsocket.composite-metadata.v0"} in which case the
141144
* route, if provided, is encoded as a
142-
* {@code "message/x.rsocket.routing.v0"} metadata entry, potentially
143-
* with other metadata entries added too. If this is set to any other
144-
* mime type, and a route is provided, it is assumed the mime type is
145-
* for the route.
146-
* @param mimeType the data MimeType to use
145+
* {@code "message/x.rsocket.routing.v0"} composite metadata entry.
146+
* For any other MimeType, it is assumed to be the MimeType for the
147+
* route, if provided.
147148
*/
148149
RSocketRequester.Builder metadataMimeType(MimeType mimeType);
149150

150151
/**
151-
* Set the {@link RSocketStrategies} to use for access to encoders,
152-
* decoders, and a factory for {@code DataBuffer's}.
153-
* @param strategies the codecs strategies to use
152+
* Set the {@link RSocketStrategies} to use.
153+
* <p>By default this is set to {@code RSocketStrategies.builder().build()}
154+
* but may be further customized via {@link #rsocketStrategies(Consumer)}.
154155
*/
155156
RSocketRequester.Builder rsocketStrategies(@Nullable RSocketStrategies strategies);
156157

@@ -159,21 +160,20 @@ interface Builder {
159160
* <p>By default this starts out with an empty builder, i.e.
160161
* {@link RSocketStrategies#builder()}, but the strategies can also be
161162
* set via {@link #rsocketStrategies(RSocketStrategies)}.
162-
* @param configurer the configurer to apply
163163
*/
164164
RSocketRequester.Builder rsocketStrategies(Consumer<RSocketStrategies.Builder> configurer);
165165

166166
/**
167167
* Callback to configure the {@code ClientRSocketFactory} directly.
168-
* <p>See {@link AnnotationClientResponderConfigurer} for configuring a
169-
* client side responder.
168+
* <p>See static factory method
169+
* {@link RSocketMessageHandler#clientResponder(Object...)} for
170+
* configuring a client side responder with annotated methods.
170171
* <p><strong>Note:</strong> Do not set {@link #dataMimeType(MimeType)}
171172
* and {@link #metadataMimeType(MimeType)} directly on the
172173
* {@code ClientRSocketFactory}. Use the shortcuts on this builder
173174
* instead since the created {@code RSocketRequester} needs to be aware
174175
* of those settings.
175-
* @param configurer consumer to customize the factory
176-
* @see AnnotationClientResponderConfigurer
176+
* @see RSocketMessageHandler#clientResponder(Object...)
177177
*/
178178
RSocketRequester.Builder rsocketFactory(ClientRSocketFactoryConfigurer configurer);
179179

0 commit comments

Comments
 (0)