17
17
package org .springframework .boot .actuate .autoconfigure ;
18
18
19
19
import java .io .IOException ;
20
+ import java .util .concurrent .CountDownLatch ;
20
21
21
22
import javax .servlet .Filter ;
22
23
import javax .servlet .FilterChain ;
34
35
import org .springframework .context .annotation .Configuration ;
35
36
import org .springframework .core .annotation .Order ;
36
37
import org .springframework .http .HttpStatus ;
38
+ import org .springframework .http .ResponseEntity ;
37
39
import org .springframework .mock .web .MockHttpServletRequest ;
38
40
import org .springframework .mock .web .MockHttpServletResponse ;
39
41
import org .springframework .stereotype .Component ;
40
42
import org .springframework .test .web .servlet .MockMvc ;
43
+ import org .springframework .test .web .servlet .MvcResult ;
41
44
import org .springframework .test .web .servlet .setup .MockMvcBuilders ;
42
45
import org .springframework .web .bind .annotation .PathVariable ;
43
46
import org .springframework .web .bind .annotation .RequestMapping ;
44
47
import org .springframework .web .bind .annotation .ResponseBody ;
45
48
import org .springframework .web .bind .annotation .ResponseStatus ;
46
49
import org .springframework .web .bind .annotation .RestController ;
50
+ import org .springframework .web .context .request .async .DeferredResult ;
47
51
import org .springframework .web .filter .OncePerRequestFilter ;
48
52
import org .springframework .web .util .NestedServletException ;
49
53
50
54
import static org .hamcrest .Matchers .equalTo ;
55
+ import static org .hamcrest .Matchers .is ;
56
+ import static org .hamcrest .Matchers .notNullValue ;
57
+ import static org .hamcrest .Matchers .nullValue ;
51
58
import static org .junit .Assert .assertThat ;
59
+ import static org .junit .Assert .fail ;
52
60
import static org .mockito .BDDMockito .willAnswer ;
53
61
import static org .mockito .BDDMockito .willThrow ;
54
62
import static org .mockito .Matchers .anyDouble ;
57
65
import static org .mockito .Mockito .mock ;
58
66
import static org .mockito .Mockito .times ;
59
67
import static org .mockito .Mockito .verify ;
68
+ import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .asyncDispatch ;
60
69
import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .get ;
70
+ import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .post ;
71
+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .request ;
61
72
import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
62
73
63
74
/**
@@ -202,6 +213,56 @@ public void gaugeServiceThatThrows() throws Exception {
202
213
context .close ();
203
214
}
204
215
216
+ @ Test
217
+ public void correctlyRecordsMetricsForDeferredResultResponse () throws Exception {
218
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (
219
+ Config .class , MetricFilterAutoConfiguration .class );
220
+ MetricsFilter filter = context .getBean (MetricsFilter .class );
221
+ CountDownLatch latch = new CountDownLatch (1 );
222
+ MockMvc mvc = MockMvcBuilders
223
+ .standaloneSetup (new MetricFilterTestController (latch )).addFilter (filter )
224
+ .build ();
225
+ String attributeName = MetricsFilter .class .getName () + ".StopWatch" ;
226
+ MvcResult result = mvc .perform (post ("/create" )).andExpect (status ().isOk ())
227
+ .andExpect (request ().asyncStarted ())
228
+ .andExpect (request ().attribute (attributeName , is (notNullValue ())))
229
+ .andReturn ();
230
+ latch .countDown ();
231
+ mvc .perform (asyncDispatch (result )).andExpect (status ().isCreated ())
232
+ .andExpect (request ().attribute (attributeName , is (nullValue ())));
233
+ verify (context .getBean (CounterService .class )).increment ("status.201.create" );
234
+ context .close ();
235
+ }
236
+
237
+ @ Test
238
+ public void correctlyRecordsMetricsForFailedDeferredResultResponse () throws Exception {
239
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (
240
+ Config .class , MetricFilterAutoConfiguration .class );
241
+ MetricsFilter filter = context .getBean (MetricsFilter .class );
242
+ CountDownLatch latch = new CountDownLatch (1 );
243
+ MockMvc mvc = MockMvcBuilders
244
+ .standaloneSetup (new MetricFilterTestController (latch )).addFilter (filter )
245
+ .build ();
246
+ String attributeName = MetricsFilter .class .getName () + ".StopWatch" ;
247
+ MvcResult result = mvc .perform (post ("/createFailure" )).andExpect (status ().isOk ())
248
+ .andExpect (request ().asyncStarted ())
249
+ .andExpect (request ().attribute (attributeName , is (notNullValue ())))
250
+ .andReturn ();
251
+ latch .countDown ();
252
+ try {
253
+ mvc .perform (asyncDispatch (result ));
254
+ fail ();
255
+ }
256
+ catch (Exception ex ) {
257
+ assertThat (result .getRequest ().getAttribute (attributeName ), is (nullValue ()));
258
+ verify (context .getBean (CounterService .class )).increment (
259
+ "status.500.createFailure" );
260
+ }
261
+ finally {
262
+ context .close ();
263
+ }
264
+ }
265
+
205
266
@ Configuration
206
267
public static class Config {
207
268
@@ -220,6 +281,16 @@ public GaugeService gaugeService() {
220
281
@ RestController
221
282
class MetricFilterTestController {
222
283
284
+ private final CountDownLatch latch ;
285
+
286
+ MetricFilterTestController () {
287
+ this (null );
288
+ }
289
+
290
+ MetricFilterTestController (CountDownLatch latch ) {
291
+ this .latch = latch ;
292
+ }
293
+
223
294
@ RequestMapping ("templateVarTest/{someVariable}" )
224
295
public String testTemplateVariableResolution (@ PathVariable String someVariable ) {
225
296
return someVariable ;
@@ -237,6 +308,43 @@ public String testKnownPathWith404Response(@PathVariable String someVariable) {
237
308
public String testException () {
238
309
throw new RuntimeException ();
239
310
}
311
+
312
+ @ RequestMapping ("create" )
313
+ public DeferredResult <ResponseEntity <String >> create () {
314
+ final DeferredResult <ResponseEntity <String >> result = new DeferredResult <ResponseEntity <String >>();
315
+ new Thread (new Runnable () {
316
+ @ Override
317
+ public void run () {
318
+ try {
319
+ MetricFilterTestController .this .latch .await ();
320
+ result .setResult (new ResponseEntity <String >("Done" ,
321
+ HttpStatus .CREATED ));
322
+ }
323
+ catch (InterruptedException ex ) {
324
+ }
325
+ }
326
+ }).start ();
327
+ return result ;
328
+ }
329
+
330
+ @ RequestMapping ("createFailure" )
331
+ public DeferredResult <ResponseEntity <String >> createFailure () {
332
+ final DeferredResult <ResponseEntity <String >> result = new DeferredResult <ResponseEntity <String >>();
333
+ new Thread (new Runnable () {
334
+ @ Override
335
+ public void run () {
336
+ try {
337
+ MetricFilterTestController .this .latch .await ();
338
+ result .setErrorResult (new Exception ("It failed" ));
339
+ }
340
+ catch (InterruptedException ex ) {
341
+
342
+ }
343
+ }
344
+ }).start ();
345
+ return result ;
346
+ }
347
+
240
348
}
241
349
242
350
@ Component
0 commit comments