15
15
*/
16
16
package org .springframework .messaging .rsocket ;
17
17
18
+ import java .nio .charset .StandardCharsets ;
19
+ import java .util .ArrayList ;
18
20
import java .util .Collections ;
19
21
import java .util .HashMap ;
22
+ import java .util .List ;
20
23
import java .util .Map ;
21
24
import java .util .function .BiConsumer ;
22
25
23
26
import io .netty .buffer .ByteBuf ;
27
+ import io .netty .buffer .PooledByteBufAllocator ;
24
28
import io .rsocket .Payload ;
25
29
import io .rsocket .metadata .CompositeMetadata ;
26
30
27
31
import org .springframework .core .ParameterizedTypeReference ;
28
32
import org .springframework .core .ResolvableType ;
29
33
import org .springframework .core .codec .Decoder ;
30
- import org .springframework .core .io .buffer .DataBuffer ;
31
- import org .springframework .core .io .buffer .DataBufferFactory ;
34
+ import org .springframework .core .io .buffer .NettyDataBuffer ;
32
35
import org .springframework .core .io .buffer .NettyDataBufferFactory ;
33
36
import org .springframework .lang .Nullable ;
37
+ import org .springframework .util .Assert ;
34
38
import org .springframework .util .MimeType ;
35
39
36
40
/**
47
51
*/
48
52
public class DefaultMetadataExtractor implements MetadataExtractor {
49
53
50
- private final Map <String , EntryProcessor <?>> entryProcessors = new HashMap <>();
54
+ private final List <Decoder <?>> decoders = new ArrayList <>();
55
+
56
+ private final Map <String , MetadataProcessor <?>> processors = new HashMap <>();
51
57
52
58
53
59
/**
54
- * Default constructor with {@link RSocketStrategies}.
60
+ * Configure the decoders to use for de-serializing metadata entries.
61
+ * <p>By default this is not set.
62
+ * <p>When this extractor is passed into {@link RSocketStrategies.Builder} or
63
+ * {@link org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler
64
+ * RSocketMessageHandler}, the decoders may be left not set, and they will
65
+ * be initialized from the decoders already configured there.
55
66
*/
56
- public DefaultMetadataExtractor () {
57
- // TODO: remove when rsocket-core API available
58
- metadataToExtract (MetadataExtractor .ROUTING , String .class , ROUTE_KEY );
67
+ public void setDecoders (List <? extends Decoder <?>> decoders ) {
68
+ this .decoders .clear ();
69
+ if (!decoders .isEmpty ()) {
70
+ this .decoders .addAll (decoders );
71
+ updateProcessors ();
72
+ }
73
+ }
74
+
75
+ @ SuppressWarnings ("unchecked" )
76
+ private <T > void updateProcessors () {
77
+ for (MetadataProcessor <?> info : this .processors .values ()) {
78
+ Decoder <T > decoder = decoderFor (info .mimeType (), info .targetType ());
79
+ Assert .isTrue (decoder != null , "No decoder for " + info );
80
+ info = ((MetadataProcessor <T >) info ).setDecoder (decoder );
81
+ this .processors .put (info .mimeType ().toString (), info );
82
+ }
83
+ }
84
+
85
+ @ Nullable
86
+ @ SuppressWarnings ("unchecked" )
87
+ private <T > Decoder <T > decoderFor (MimeType mimeType , ResolvableType type ) {
88
+ for (Decoder <?> decoder : this .decoders ) {
89
+ if (decoder .canDecode (type , mimeType )) {
90
+ return (Decoder <T >) decoder ;
91
+ }
92
+ }
93
+ return null ;
94
+ }
95
+
96
+ /**
97
+ * Return the {@link #setDecoders(List) configured} decoders.
98
+ */
99
+ public List <? extends Decoder <?>> getDecoders () {
100
+ return this .decoders ;
59
101
}
60
102
61
103
@@ -97,11 +139,9 @@ public void metadataToExtract(
97
139
* @param <T> the target value type
98
140
*/
99
141
public <T > void metadataToExtract (
100
- MimeType mimeType , Class <T > targetType ,
101
- BiConsumer <T , Map <String , Object >> mapper ) {
142
+ MimeType mimeType , Class <T > targetType , BiConsumer <T , Map <String , Object >> mapper ) {
102
143
103
- EntryProcessor <T > spec = new EntryProcessor <>(mimeType , targetType , mapper );
104
- this .entryProcessors .put (mimeType .toString (), spec );
144
+ metadataToExtract (mimeType , mapper , ResolvableType .forClass (targetType ));
105
145
}
106
146
107
147
/**
@@ -117,87 +157,107 @@ public <T> void metadataToExtract(
117
157
MimeType mimeType , ParameterizedTypeReference <T > targetType ,
118
158
BiConsumer <T , Map <String , Object >> mapper ) {
119
159
120
- EntryProcessor <T > spec = new EntryProcessor <>(mimeType , targetType , mapper );
121
- this .entryProcessors .put (mimeType .toString (), spec );
160
+ metadataToExtract (mimeType , mapper , ResolvableType .forType (targetType ));
161
+ }
162
+
163
+ private <T > void metadataToExtract (
164
+ MimeType mimeType , BiConsumer <T , Map <String , Object >> mapper , ResolvableType elementType ) {
165
+
166
+ Decoder <T > decoder = decoderFor (mimeType , elementType );
167
+ Assert .isTrue (this .decoders .isEmpty () || decoder != null , () -> "No decoder for " + mimeType );
168
+ MetadataProcessor <T > info = new MetadataProcessor <>(mimeType , elementType , mapper , decoder );
169
+ this .processors .put (mimeType .toString (), info );
122
170
}
123
171
124
172
125
173
@ Override
126
- public Map <String , Object > extract (Payload payload , MimeType metadataMimeType , RSocketStrategies strategies ) {
174
+ public Map <String , Object > extract (Payload payload , MimeType metadataMimeType ) {
127
175
Map <String , Object > result = new HashMap <>();
128
176
if (metadataMimeType .equals (COMPOSITE_METADATA )) {
129
177
for (CompositeMetadata .Entry entry : new CompositeMetadata (payload .metadata (), false )) {
130
- processEntry (entry .getContent (), entry .getMimeType (), result , strategies );
178
+ processEntry (entry .getContent (), entry .getMimeType (), result );
131
179
}
132
180
}
133
181
else {
134
- processEntry (payload .metadata (), metadataMimeType .toString (), result , strategies );
182
+ processEntry (payload .metadata (), metadataMimeType .toString (), result );
135
183
}
136
184
return result ;
137
185
}
138
186
139
- private void processEntry (ByteBuf content ,
140
- @ Nullable String mimeType , Map <String , Object > result , RSocketStrategies strategies ) {
141
-
142
- EntryProcessor <?> entryProcessor = this .entryProcessors .get (mimeType );
143
- if (entryProcessor != null ) {
144
- content .retain ();
145
- entryProcessor .process (content , result , strategies );
187
+ @ SuppressWarnings ("unchecked" )
188
+ private <T > void processEntry (ByteBuf content , @ Nullable String mimeType , Map <String , Object > result ) {
189
+ MetadataProcessor <T > info = (MetadataProcessor <T >) this .processors .get (mimeType );
190
+ if (info != null ) {
191
+ info .process (content , result );
146
192
return ;
147
193
}
148
194
if (MetadataExtractor .ROUTING .toString ().equals (mimeType )) {
149
195
// TODO: use rsocket-core API when available
196
+ result .put (MetadataExtractor .ROUTE_KEY , content .toString (StandardCharsets .UTF_8 ));
150
197
}
151
198
}
152
199
153
200
154
- /**
155
- * Helps to decode a metadata entry and add the resulting value to the
156
- * output map.
157
- */
158
- private class EntryProcessor < T > {
201
+ private static class MetadataProcessor < T > {
202
+
203
+ private final static NettyDataBufferFactory bufferFactory =
204
+ new NettyDataBufferFactory ( PooledByteBufAllocator . DEFAULT );
205
+
159
206
160
207
private final MimeType mimeType ;
161
208
162
209
private final ResolvableType targetType ;
163
210
164
211
private final BiConsumer <T , Map <String , Object >> accumulator ;
165
212
213
+ @ Nullable
214
+ private final Decoder <T > decoder ;
166
215
167
- public EntryProcessor (
168
- MimeType mimeType , Class <T > targetType ,
169
- BiConsumer <T , Map <String , Object >> accumulator ) {
170
216
171
- this ( mimeType , ResolvableType . forClass ( targetType ), accumulator );
172
- }
217
+ MetadataProcessor ( MimeType mimeType , ResolvableType targetType ,
218
+ BiConsumer < T , Map < String , Object >> accumulator , @ Nullable Decoder < T > decoder ) {
173
219
174
- public EntryProcessor (
175
- MimeType mimeType , ParameterizedTypeReference <T > targetType ,
176
- BiConsumer <T , Map <String , Object >> accumulator ) {
220
+ this .mimeType = mimeType ;
221
+ this .targetType = targetType ;
222
+ this .accumulator = accumulator ;
223
+ this .decoder = decoder ;
224
+ }
177
225
178
- this (mimeType , ResolvableType .forType (targetType ), accumulator );
226
+ MetadataProcessor (MetadataProcessor <T > other , Decoder <T > decoder ) {
227
+ this .mimeType = other .mimeType ;
228
+ this .targetType = other .targetType ;
229
+ this .accumulator = other .accumulator ;
230
+ this .decoder = decoder ;
179
231
}
180
232
181
- private EntryProcessor (
182
- MimeType mimeType , ResolvableType targetType ,
183
- BiConsumer <T , Map <String , Object >> accumulator ) {
184
233
185
- this .mimeType = mimeType ;
186
- this .targetType = targetType ;
187
- this .accumulator = accumulator ;
234
+ public MimeType mimeType () {
235
+ return this .mimeType ;
188
236
}
189
237
238
+ public ResolvableType targetType () {
239
+ return this .targetType ;
240
+ }
241
+
242
+ public MetadataProcessor <T > setDecoder (Decoder <T > decoder ) {
243
+ return this .decoder != decoder ? new MetadataProcessor <>(this , decoder ) : this ;
244
+ }
190
245
191
- public void process (ByteBuf byteBuf , Map <String , Object > result , RSocketStrategies strategies ) {
192
- DataBufferFactory factory = strategies .dataBufferFactory ();
193
- DataBuffer buffer = factory instanceof NettyDataBufferFactory ?
194
- ((NettyDataBufferFactory ) factory ).wrap (byteBuf ) :
195
- factory .wrap (byteBuf .nioBuffer ());
196
246
197
- Decoder <T > decoder = strategies .decoder (this .targetType , this .mimeType );
198
- T value = decoder .decode (buffer , this .targetType , this .mimeType , Collections .emptyMap ());
247
+ public void process (ByteBuf content , Map <String , Object > result ) {
248
+ if (this .decoder == null ) {
249
+ throw new IllegalStateException ("No decoder for " + this );
250
+ }
251
+ NettyDataBuffer dataBuffer = bufferFactory .wrap (content .retain ());
252
+ T value = this .decoder .decode (dataBuffer , this .targetType , this .mimeType , Collections .emptyMap ());
199
253
this .accumulator .accept (value , result );
200
254
}
255
+
256
+
257
+ @ Override
258
+ public String toString () {
259
+ return "MetadataProcessor mimeType=" + this .mimeType + ", targetType=" + this .targetType ;
260
+ }
201
261
}
202
262
203
263
}
0 commit comments