17
17
package org .springframework .web .context .request .async ;
18
18
19
19
import java .io .IOException ;
20
+ import java .io .PrintWriter ;
20
21
import java .util .ArrayList ;
21
22
import java .util .List ;
23
+ import java .util .Locale ;
24
+ import java .util .concurrent .locks .Lock ;
22
25
import java .util .concurrent .locks .ReentrantLock ;
23
26
import java .util .function .Consumer ;
24
27
@@ -226,6 +229,9 @@ private static final class LifecycleHttpServletResponse extends HttpServletRespo
226
229
@ Nullable
227
230
private ServletOutputStream outputStream ;
228
231
232
+ @ Nullable
233
+ private PrintWriter writer ;
234
+
229
235
public LifecycleHttpServletResponse (HttpServletResponse response ) {
230
236
super (response );
231
237
}
@@ -243,6 +249,15 @@ public ServletOutputStream getOutputStream() {
243
249
}
244
250
return this .outputStream ;
245
251
}
252
+
253
+ @ Override
254
+ public PrintWriter getWriter () throws IOException {
255
+ if (this .writer == null ) {
256
+ Assert .notNull (this .asyncWebRequest , "Not initialized" );
257
+ this .writer = new LifecyclePrintWriter (getResponse ().getWriter (), this .asyncWebRequest );
258
+ }
259
+ return this .writer ;
260
+ }
246
261
}
247
262
248
263
@@ -340,6 +355,11 @@ private void obtainLockAndCheckState() throws AsyncRequestNotUsableException {
340
355
}
341
356
}
342
357
358
+ private void handleIOException (IOException ex , String msg ) throws AsyncRequestNotUsableException {
359
+ this .asyncWebRequest .transitionToErrorState ();
360
+ throw new AsyncRequestNotUsableException (msg , ex );
361
+ }
362
+
343
363
private void releaseLock () {
344
364
if (state () != State .NEW ) {
345
365
stateLock ().unlock ();
@@ -350,15 +370,262 @@ private State state() {
350
370
return this .asyncWebRequest .state ;
351
371
}
352
372
353
- private ReentrantLock stateLock () {
373
+ private Lock stateLock () {
354
374
return this .asyncWebRequest .stateLock ;
355
375
}
356
376
357
- private void handleIOException (IOException ex , String msg ) throws AsyncRequestNotUsableException {
358
- this .asyncWebRequest .transitionToErrorState ();
359
- throw new AsyncRequestNotUsableException (msg , ex );
377
+ }
378
+
379
+
380
+ /**
381
+ * Wraps a PrintWriter to prevent use after Servlet container onError
382
+ * notifications, and after async request completion.
383
+ */
384
+ private static final class LifecyclePrintWriter extends PrintWriter {
385
+
386
+ private final PrintWriter delegate ;
387
+
388
+ private final StandardServletAsyncWebRequest asyncWebRequest ;
389
+
390
+ private LifecyclePrintWriter (PrintWriter delegate , StandardServletAsyncWebRequest asyncWebRequest ) {
391
+ super (delegate );
392
+ this .delegate = delegate ;
393
+ this .asyncWebRequest = asyncWebRequest ;
394
+ }
395
+
396
+ @ Override
397
+ public void flush () {
398
+ if (tryObtainLockAndCheckState ()) {
399
+ try {
400
+ this .delegate .flush ();
401
+ }
402
+ finally {
403
+ releaseLock ();
404
+ }
405
+ }
406
+ }
407
+
408
+ @ Override
409
+ public void close () {
410
+ if (tryObtainLockAndCheckState ()) {
411
+ try {
412
+ this .delegate .close ();
413
+ }
414
+ finally {
415
+ releaseLock ();
416
+ }
417
+ }
418
+ }
419
+
420
+ @ Override
421
+ public boolean checkError () {
422
+ return this .delegate .checkError ();
423
+ }
424
+
425
+ @ Override
426
+ public void write (int c ) {
427
+ if (tryObtainLockAndCheckState ()) {
428
+ try {
429
+ this .delegate .write (c );
430
+ }
431
+ finally {
432
+ releaseLock ();
433
+ }
434
+ }
435
+ }
436
+
437
+ @ Override
438
+ public void write (char [] buf , int off , int len ) {
439
+ if (tryObtainLockAndCheckState ()) {
440
+ try {
441
+ this .delegate .write (buf , off , len );
442
+ }
443
+ finally {
444
+ releaseLock ();
445
+ }
446
+ }
447
+ }
448
+
449
+ @ Override
450
+ public void write (char [] buf ) {
451
+ this .delegate .write (buf );
452
+ }
453
+
454
+ @ Override
455
+ public void write (String s , int off , int len ) {
456
+ if (tryObtainLockAndCheckState ()) {
457
+ try {
458
+ this .delegate .write (s , off , len );
459
+ }
460
+ finally {
461
+ releaseLock ();
462
+ }
463
+ }
464
+ }
465
+
466
+ @ Override
467
+ public void write (String s ) {
468
+ this .delegate .write (s );
469
+ }
470
+
471
+ private boolean tryObtainLockAndCheckState () {
472
+ if (state () == State .NEW ) {
473
+ return true ;
474
+ }
475
+ if (stateLock ().tryLock ()) {
476
+ if (state () == State .ASYNC ) {
477
+ return true ;
478
+ }
479
+ stateLock ().unlock ();
480
+ }
481
+ return false ;
482
+ }
483
+
484
+ private void releaseLock () {
485
+ if (state () != State .NEW ) {
486
+ stateLock ().unlock ();
487
+ }
488
+ }
489
+
490
+ private State state () {
491
+ return this .asyncWebRequest .state ;
492
+ }
493
+
494
+ private Lock stateLock () {
495
+ return this .asyncWebRequest .stateLock ;
496
+ }
497
+
498
+ // Plain delegates
499
+
500
+ @ Override
501
+ public void print (boolean b ) {
502
+ this .delegate .print (b );
503
+ }
504
+
505
+ @ Override
506
+ public void print (char c ) {
507
+ this .delegate .print (c );
508
+ }
509
+
510
+ @ Override
511
+ public void print (int i ) {
512
+ this .delegate .print (i );
513
+ }
514
+
515
+ @ Override
516
+ public void print (long l ) {
517
+ this .delegate .print (l );
518
+ }
519
+
520
+ @ Override
521
+ public void print (float f ) {
522
+ this .delegate .print (f );
523
+ }
524
+
525
+ @ Override
526
+ public void print (double d ) {
527
+ this .delegate .print (d );
528
+ }
529
+
530
+ @ Override
531
+ public void print (char [] s ) {
532
+ this .delegate .print (s );
533
+ }
534
+
535
+ @ Override
536
+ public void print (String s ) {
537
+ this .delegate .print (s );
538
+ }
539
+
540
+ @ Override
541
+ public void print (Object obj ) {
542
+ this .delegate .print (obj );
543
+ }
544
+
545
+ @ Override
546
+ public void println () {
547
+ this .delegate .println ();
548
+ }
549
+
550
+ @ Override
551
+ public void println (boolean x ) {
552
+ this .delegate .println (x );
553
+ }
554
+
555
+ @ Override
556
+ public void println (char x ) {
557
+ this .delegate .println (x );
360
558
}
361
559
560
+ @ Override
561
+ public void println (int x ) {
562
+ this .delegate .println (x );
563
+ }
564
+
565
+ @ Override
566
+ public void println (long x ) {
567
+ this .delegate .println (x );
568
+ }
569
+
570
+ @ Override
571
+ public void println (float x ) {
572
+ this .delegate .println (x );
573
+ }
574
+
575
+ @ Override
576
+ public void println (double x ) {
577
+ this .delegate .println (x );
578
+ }
579
+
580
+ @ Override
581
+ public void println (char [] x ) {
582
+ this .delegate .println (x );
583
+ }
584
+
585
+ @ Override
586
+ public void println (String x ) {
587
+ this .delegate .println (x );
588
+ }
589
+
590
+ @ Override
591
+ public void println (Object x ) {
592
+ this .delegate .println (x );
593
+ }
594
+
595
+ @ Override
596
+ public PrintWriter printf (String format , Object ... args ) {
597
+ return this .delegate .printf (format , args );
598
+ }
599
+
600
+ @ Override
601
+ public PrintWriter printf (Locale l , String format , Object ... args ) {
602
+ return this .delegate .printf (l , format , args );
603
+ }
604
+
605
+ @ Override
606
+ public PrintWriter format (String format , Object ... args ) {
607
+ return this .delegate .format (format , args );
608
+ }
609
+
610
+ @ Override
611
+ public PrintWriter format (Locale l , String format , Object ... args ) {
612
+ return this .delegate .format (l , format , args );
613
+ }
614
+
615
+ @ Override
616
+ public PrintWriter append (CharSequence csq ) {
617
+ return this .delegate .append (csq );
618
+ }
619
+
620
+ @ Override
621
+ public PrintWriter append (CharSequence csq , int start , int end ) {
622
+ return this .delegate .append (csq , start , end );
623
+ }
624
+
625
+ @ Override
626
+ public PrintWriter append (char c ) {
627
+ return this .delegate .append (c );
628
+ }
362
629
}
363
630
364
631
0 commit comments