16
16
package org .springframework .web .servlet .mvc .method .annotation ;
17
17
18
18
import java .io .IOException ;
19
+ import java .util .ArrayList ;
19
20
import java .util .Arrays ;
20
21
import java .util .Collections ;
22
+ import java .util .List ;
21
23
import java .util .Set ;
22
24
import java .util .concurrent .atomic .AtomicReference ;
25
+ import java .util .stream .Collectors ;
23
26
24
27
import org .junit .Before ;
25
28
import org .junit .Test ;
52
55
import static junit .framework .TestCase .assertNull ;
53
56
import static org .junit .Assert .assertEquals ;
54
57
import static org .junit .Assert .assertFalse ;
55
- import static org .junit .Assert .assertNotNull ;
56
58
import static org .junit .Assert .assertTrue ;
59
+ import static org .springframework .core .ResolvableType .forClass ;
57
60
import static org .springframework .web .method .ResolvableMethod .on ;
58
61
59
62
/**
@@ -108,27 +111,27 @@ public void deferredResultSubscriberWithOneValue() throws Exception {
108
111
109
112
// Mono
110
113
MonoProcessor <String > mono = MonoProcessor .create ();
111
- testDeferredResultSubscriber (mono , Mono .class , () -> mono .onNext ("foo" ), "foo" );
114
+ testDeferredResultSubscriber (mono , Mono .class , forClass ( String . class ), () -> mono .onNext ("foo" ), "foo" );
112
115
113
116
// Mono empty
114
117
MonoProcessor <String > monoEmpty = MonoProcessor .create ();
115
- testDeferredResultSubscriber (monoEmpty , Mono .class , monoEmpty ::onComplete , null );
118
+ testDeferredResultSubscriber (monoEmpty , Mono .class , forClass ( String . class ), monoEmpty ::onComplete , null );
116
119
117
120
// RxJava 1 Single
118
121
AtomicReference <SingleEmitter <String >> ref = new AtomicReference <>();
119
122
Single <String > single = Single .fromEmitter (ref ::set );
120
- testDeferredResultSubscriber (single , Single .class , () -> ref .get ().onSuccess ("foo" ), "foo" );
123
+ testDeferredResultSubscriber (single , Single .class , forClass ( String . class ), () -> ref .get ().onSuccess ("foo" ), "foo" );
121
124
122
125
// RxJava 2 Single
123
126
AtomicReference <io .reactivex .SingleEmitter <String >> ref2 = new AtomicReference <>();
124
127
io .reactivex .Single <String > single2 = io .reactivex .Single .create (ref2 ::set );
125
- testDeferredResultSubscriber (single2 , io .reactivex .Single .class , () -> ref2 .get ().onSuccess ("foo" ), "foo" );
128
+ testDeferredResultSubscriber (single2 , io .reactivex .Single .class , forClass ( String . class ), () -> ref2 .get ().onSuccess ("foo" ), "foo" );
126
129
}
127
130
128
131
@ Test
129
132
public void deferredResultSubscriberWithNoValues () throws Exception {
130
133
MonoProcessor <String > monoEmpty = MonoProcessor .create ();
131
- testDeferredResultSubscriber (monoEmpty , Mono .class , monoEmpty ::onComplete , null );
134
+ testDeferredResultSubscriber (monoEmpty , Mono .class , forClass ( String . class ), monoEmpty ::onComplete , null );
132
135
}
133
136
134
137
@ Test
@@ -137,13 +140,15 @@ public void deferredResultSubscriberWithMultipleValues() throws Exception {
137
140
// JSON must be preferred for Flux<String> -> List<String> or else we stream
138
141
this .servletRequest .addHeader ("Accept" , "application/json" );
139
142
140
- EmitterProcessor <String > emitter = EmitterProcessor .create ();
141
- testDeferredResultSubscriber (emitter , Flux .class , () -> {
142
- emitter .onNext ("foo" );
143
- emitter .onNext ("bar" );
144
- emitter .onNext ("baz" );
143
+ Bar bar1 = new Bar ("foo" );
144
+ Bar bar2 = new Bar ("bar" );
145
+
146
+ EmitterProcessor <Bar > emitter = EmitterProcessor .create ();
147
+ testDeferredResultSubscriber (emitter , Flux .class , forClass (Bar .class ), () -> {
148
+ emitter .onNext (bar1 );
149
+ emitter .onNext (bar2 );
145
150
emitter .onComplete ();
146
- }, Arrays .asList ("foo" , "bar" , "baz" ));
151
+ }, Arrays .asList (bar1 , bar2 ));
147
152
}
148
153
149
154
@ Test
@@ -153,48 +158,17 @@ public void deferredResultSubscriberWithError() throws Exception {
153
158
154
159
// Mono
155
160
MonoProcessor <String > mono = MonoProcessor .create ();
156
- testDeferredResultSubscriber (mono , Mono .class , () -> mono .onError (ex ), ex );
161
+ testDeferredResultSubscriber (mono , Mono .class , forClass ( String . class ), () -> mono .onError (ex ), ex );
157
162
158
163
// RxJava 1 Single
159
164
AtomicReference <SingleEmitter <String >> ref = new AtomicReference <>();
160
165
Single <String > single = Single .fromEmitter (ref ::set );
161
- testDeferredResultSubscriber (single , Single .class , () -> ref .get ().onError (ex ), ex );
166
+ testDeferredResultSubscriber (single , Single .class , forClass ( String . class ), () -> ref .get ().onError (ex ), ex );
162
167
163
168
// RxJava 2 Single
164
169
AtomicReference <io .reactivex .SingleEmitter <String >> ref2 = new AtomicReference <>();
165
170
io .reactivex .Single <String > single2 = io .reactivex .Single .create (ref2 ::set );
166
- testDeferredResultSubscriber (single2 , io .reactivex .Single .class , () -> ref2 .get ().onError (ex ), ex );
167
- }
168
-
169
- @ Test
170
- public void jsonArrayOfStrings () throws Exception {
171
-
172
- // Empty -> null
173
- testJsonNotPreferred ("text/plain" );
174
- testJsonNotPreferred ("text/plain, application/json" );
175
- testJsonNotPreferred ("text/markdown" );
176
- testJsonNotPreferred ("foo/bar" );
177
-
178
- // Empty -> List[0] when JSON is preferred
179
- testJsonPreferred ("application/json" );
180
- testJsonPreferred ("application/foo+json" );
181
- testJsonPreferred ("application/json, text/plain" );
182
- testJsonPreferred ("*/*, application/json, text/plain" );
183
- }
184
-
185
- private void testJsonNotPreferred (String acceptHeaderValue ) throws Exception {
186
- resetRequest ();
187
- this .servletRequest .addHeader ("Accept" , acceptHeaderValue );
188
- EmitterProcessor <String > processor = EmitterProcessor .create ();
189
- ResponseBodyEmitter emitter = handleValue (processor , Flux .class );
190
- assertNotNull (emitter );
191
- }
192
-
193
- private void testJsonPreferred (String acceptHeaderValue ) throws Exception {
194
- resetRequest ();
195
- this .servletRequest .addHeader ("Accept" , acceptHeaderValue );
196
- EmitterProcessor <String > processor = EmitterProcessor .create ();
197
- testDeferredResultSubscriber (processor , Flux .class , processor ::onComplete , Collections .emptyList ());
171
+ testDeferredResultSubscriber (single2 , io .reactivex .Single .class , forClass (String .class ), () -> ref2 .get ().onError (ex ), ex );
198
172
}
199
173
200
174
@ Test
@@ -211,14 +185,10 @@ public void mediaTypes() throws Exception {
211
185
212
186
// No media type preferences
213
187
testSseResponse (false );
214
-
215
- // Requested media types are sorted
216
- testJsonPreferred ("text/plain;q=0.8, application/json;q=1.0" );
217
- testJsonNotPreferred ("text/plain, application/json" );
218
188
}
219
189
220
190
private void testSseResponse (boolean expectSseEimtter ) throws Exception {
221
- ResponseBodyEmitter emitter = handleValue (Flux .empty (), Flux .class );
191
+ ResponseBodyEmitter emitter = handleValue (Flux .empty (), Flux .class , forClass ( String . class ) );
222
192
assertEquals (expectSseEimtter , emitter instanceof SseEmitter );
223
193
resetRequest ();
224
194
}
@@ -228,7 +198,7 @@ public void writeServerSentEvents() throws Exception {
228
198
229
199
this .servletRequest .addHeader ("Accept" , "text/event-stream" );
230
200
EmitterProcessor <String > processor = EmitterProcessor .create ();
231
- SseEmitter sseEmitter = (SseEmitter ) handleValue (processor , Flux .class );
201
+ SseEmitter sseEmitter = (SseEmitter ) handleValue (processor , Flux .class , forClass ( String . class ) );
232
202
233
203
EmitterHandler emitterHandler = new EmitterHandler ();
234
204
sseEmitter .initialize (emitterHandler );
@@ -238,11 +208,11 @@ public void writeServerSentEvents() throws Exception {
238
208
processor .onNext ("baz" );
239
209
processor .onComplete ();
240
210
241
- assertEquals ("data:foo\n \n data:bar\n \n data:baz\n \n " , emitterHandler .getOutput ());
211
+ assertEquals ("data:foo\n \n data:bar\n \n data:baz\n \n " , emitterHandler .getValuesAsText ());
242
212
}
243
213
244
214
@ Test
245
- public void writeSentEventsWithBuilder () throws Exception {
215
+ public void writeServerSentEventsWithBuilder () throws Exception {
246
216
247
217
ResolvableType type = ResolvableType .forClassWithGenerics (ServerSentEvent .class , String .class );
248
218
@@ -258,36 +228,39 @@ public void writeSentEventsWithBuilder() throws Exception {
258
228
processor .onComplete ();
259
229
260
230
assertEquals ("id:1\n data:foo\n \n id:2\n data:bar\n \n id:3\n data:baz\n \n " ,
261
- emitterHandler .getOutput ());
231
+ emitterHandler .getValuesAsText ());
262
232
}
263
233
264
234
@ Test
265
235
public void writeStreamJson () throws Exception {
266
236
267
237
this .servletRequest .addHeader ("Accept" , "application/stream+json" );
268
238
269
- EmitterProcessor <String > processor = EmitterProcessor .create ();
270
- ResponseBodyEmitter emitter = handleValue (processor , Flux .class );
239
+ EmitterProcessor <Bar > processor = EmitterProcessor .create ();
240
+ ResponseBodyEmitter emitter = handleValue (processor , Flux .class , forClass ( Bar . class ) );
271
241
272
242
EmitterHandler emitterHandler = new EmitterHandler ();
273
243
emitter .initialize (emitterHandler );
274
244
275
245
ServletServerHttpResponse message = new ServletServerHttpResponse (this .servletResponse );
276
246
emitter .extendResponse (message );
277
247
278
- processor .onNext ("[\" foo\" ,\" bar\" ]" );
279
- processor .onNext ("[\" bar\" ,\" baz\" ]" );
248
+ Bar bar1 = new Bar ("foo" );
249
+ Bar bar2 = new Bar ("bar" );
250
+
251
+ processor .onNext (bar1 );
252
+ processor .onNext (bar2 );
280
253
processor .onComplete ();
281
254
282
255
assertEquals ("application/stream+json" , message .getHeaders ().getContentType ().toString ());
283
- assertEquals ("[ \" foo \" , \" bar \" ] \n [ \" bar \" , \" baz \" ] \n " , emitterHandler .getOutput ());
256
+ assertEquals (Arrays . asList ( bar1 , " \n " , bar2 , " \n " ) , emitterHandler .getValues ());
284
257
}
285
258
286
259
@ Test
287
260
public void writeText () throws Exception {
288
261
289
262
EmitterProcessor <String > processor = EmitterProcessor .create ();
290
- ResponseBodyEmitter emitter = handleValue (processor , Flux .class );
263
+ ResponseBodyEmitter emitter = handleValue (processor , Flux .class , forClass ( String . class ) );
291
264
292
265
EmitterHandler emitterHandler = new EmitterHandler ();
293
266
emitter .initialize (emitterHandler );
@@ -297,41 +270,45 @@ public void writeText() throws Exception {
297
270
processor .onNext ("the lazy dog" );
298
271
processor .onComplete ();
299
272
300
- assertEquals ("The quick brown fox jumps over the lazy dog" , emitterHandler .getOutput ());
273
+ assertEquals ("The quick brown fox jumps over the lazy dog" , emitterHandler .getValuesAsText ());
301
274
}
302
275
303
276
@ Test
304
- public void writeTextContentType () throws Exception {
277
+ public void writeFluxOfString () throws Exception {
278
+
279
+ // Default to "text/plain"
280
+ testEmitterContentType ("text/plain" );
305
281
306
- // Any requested, concrete, "text" media type
282
+ // Same if no concrete media type
283
+ this .servletRequest .addHeader ("Accept" , "text/*" );
284
+ testEmitterContentType ("text/plain" );
285
+
286
+ // Otherwise pick concrete media type
307
287
this .servletRequest .addHeader ("Accept" , "*/*, text/*, text/markdown" );
308
288
testEmitterContentType ("text/markdown" );
309
289
310
- // Or any requested concrete media type
290
+ // Any concrete media type
311
291
this .servletRequest .addHeader ("Accept" , "*/*, text/*, foo/bar" );
312
292
testEmitterContentType ("foo/bar" );
313
293
314
- // Or default to...
315
- testEmitterContentType ("text/plain" );
316
-
317
- // Or default to if not concrete..
318
- this .servletRequest .addHeader ("Accept" , "text/*" );
319
- testEmitterContentType ("text/plain" );
294
+ // Including json
295
+ this .servletRequest .addHeader ("Accept" , "*/*, text/*, application/json" );
296
+ testEmitterContentType ("application/json" );
320
297
}
321
298
322
299
private void testEmitterContentType (String expected ) throws Exception {
323
300
ServletServerHttpResponse message = new ServletServerHttpResponse (this .servletResponse );
324
- ResponseBodyEmitter emitter = handleValue (Flux .empty (), Flux .class );
301
+ ResponseBodyEmitter emitter = handleValue (Flux .empty (), Flux .class , forClass ( String . class ) );
325
302
emitter .extendResponse (message );
326
303
assertEquals (expected , message .getHeaders ().getContentType ().toString ());
327
304
resetRequest ();
328
305
}
329
306
330
307
331
308
private void testDeferredResultSubscriber (Object returnValue , Class <?> asyncType ,
332
- Runnable produceTask , Object expected ) throws Exception {
309
+ ResolvableType elementType , Runnable produceTask , Object expected ) throws Exception {
333
310
334
- ResponseBodyEmitter emitter = handleValue (returnValue , asyncType );
311
+ ResponseBodyEmitter emitter = handleValue (returnValue , asyncType , elementType );
335
312
assertNull (emitter );
336
313
337
314
assertTrue (this .servletRequest .isAsyncStarted ());
@@ -345,10 +322,6 @@ private void testDeferredResultSubscriber(Object returnValue, Class<?> asyncType
345
322
resetRequest ();
346
323
}
347
324
348
- private ResponseBodyEmitter handleValue (Object returnValue , Class <?> asyncType ) throws Exception {
349
- return handleValue (returnValue , asyncType , ResolvableType .forClass (String .class ));
350
- }
351
-
352
325
private ResponseBodyEmitter handleValue (Object returnValue , Class <?> asyncType ,
353
326
ResolvableType genericType ) throws Exception {
354
327
@@ -369,24 +342,30 @@ static class TestController {
369
342
370
343
io .reactivex .Single <String > handleSingleRxJava2 () { return null ; }
371
344
372
- Flux <String > handleFlux () { return null ; }
345
+ Flux <Bar > handleFlux () { return null ; }
346
+
347
+ Flux <String > handleFluxString () { return null ; }
373
348
374
349
Flux <ServerSentEvent <String >> handleFluxSseEventBuilder () { return null ; }
375
350
}
376
351
377
352
378
353
private static class EmitterHandler implements ResponseBodyEmitter .Handler {
379
354
380
- private final StringBuilder stringBuilder = new StringBuilder ();
355
+ private final List < Object > values = new ArrayList <> ();
381
356
382
357
383
- public String getOutput () {
384
- return this .stringBuilder .toString ();
358
+ public List <?> getValues () {
359
+ return this .values ;
360
+ }
361
+
362
+ public String getValuesAsText () {
363
+ return this .values .stream ().map (Object ::toString ).collect (Collectors .joining ());
385
364
}
386
365
387
366
@ Override
388
367
public void send (Object data , MediaType mediaType ) throws IOException {
389
- this .stringBuilder . append (data );
368
+ this .values . add (data );
390
369
}
391
370
392
371
@ Override
@@ -406,4 +385,16 @@ public void onCompletion(Runnable callback) {
406
385
}
407
386
}
408
387
388
+ private static class Bar {
389
+
390
+ private final String value ;
391
+
392
+ public Bar (String value ) {
393
+ this .value = value ;
394
+ }
395
+
396
+ public String getValue () {
397
+ return this .value ;
398
+ }
399
+ }
409
400
}
0 commit comments