0
# Asynchronous Processing and I/O
1
2
The Java Servlet API provides comprehensive support for asynchronous request processing and non-blocking I/O operations. This enables scalable web applications that can handle many concurrent connections efficiently without blocking threads.
3
4
## AsyncContext Interface
5
6
```java { .api }
7
/**
8
* Interface representing the execution context for an asynchronous operation
9
* that was initiated on a ServletRequest.
10
*/
11
public interface AsyncContext {
12
13
// Request attribute constants for async context information
14
public static final String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
15
public static final String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
16
public static final String ASYNC_MAPPING = "javax.servlet.async.mapping";
17
public static final String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
18
public static final String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
19
public static final String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";
20
21
/**
22
* Get the original ServletRequest that was used to initialize this AsyncContext.
23
*/
24
ServletRequest getRequest();
25
26
/**
27
* Get the original ServletResponse that was used to initialize this AsyncContext.
28
*/
29
ServletResponse getResponse();
30
31
/**
32
* Check if this AsyncContext was initialized with the original or wrapped
33
* ServletRequest and ServletResponse objects.
34
*/
35
boolean hasOriginalRequestAndResponse();
36
37
/**
38
* Dispatch the request and response to the container for processing.
39
* Uses the original request URI.
40
*/
41
void dispatch();
42
43
/**
44
* Dispatch the request and response to the specified path.
45
*/
46
void dispatch(String path);
47
48
/**
49
* Dispatch the request and response to the specified context and path.
50
*/
51
void dispatch(ServletContext context, String path);
52
53
/**
54
* Complete the asynchronous operation and close the response.
55
*/
56
void complete();
57
58
/**
59
* Start a new thread to process the asynchronous operation.
60
*/
61
void start(Runnable run);
62
63
/**
64
* Add an AsyncListener to receive notifications about async events.
65
*/
66
void addListener(AsyncListener listener);
67
68
/**
69
* Add an AsyncListener with associated request and response objects.
70
*/
71
void addListener(AsyncListener listener, ServletRequest servletRequest,
72
ServletResponse servletResponse);
73
74
/**
75
* Create and return an AsyncListener instance.
76
*/
77
<T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException;
78
79
/**
80
* Set the timeout for this asynchronous operation in milliseconds.
81
*/
82
void setTimeout(long timeout);
83
84
/**
85
* Get the timeout for this asynchronous operation in milliseconds.
86
*/
87
long getTimeout();
88
}
89
```
90
91
## AsyncListener Interface
92
93
```java { .api }
94
/**
95
* Listener interface for receiving notifications about asynchronous operations.
96
*/
97
public interface AsyncListener extends EventListener {
98
99
/**
100
* Called when an asynchronous operation has completed.
101
*/
102
void onComplete(AsyncEvent event) throws IOException;
103
104
/**
105
* Called when an asynchronous operation has timed out.
106
*/
107
void onTimeout(AsyncEvent event) throws IOException;
108
109
/**
110
* Called when an asynchronous operation has failed or encountered an error.
111
*/
112
void onError(AsyncEvent event) throws IOException;
113
114
/**
115
* Called when a new asynchronous operation is started.
116
*/
117
void onStartAsync(AsyncEvent event) throws IOException;
118
}
119
```
120
121
## AsyncEvent Class
122
123
```java { .api }
124
/**
125
* Event class representing an asynchronous operation event.
126
*/
127
public class AsyncEvent {
128
129
private AsyncContext asyncContext;
130
private ServletRequest suppliedRequest;
131
private ServletResponse suppliedResponse;
132
private Throwable throwable;
133
134
/**
135
* Create an AsyncEvent with the specified AsyncContext.
136
*/
137
public AsyncEvent(AsyncContext context) {
138
this(context, null, null, null);
139
}
140
141
/**
142
* Create an AsyncEvent with AsyncContext and associated request/response.
143
*/
144
public AsyncEvent(AsyncContext context, ServletRequest suppliedRequest,
145
ServletResponse suppliedResponse) {
146
this(context, suppliedRequest, suppliedResponse, null);
147
}
148
149
/**
150
* Create an AsyncEvent with AsyncContext and throwable.
151
*/
152
public AsyncEvent(AsyncContext context, Throwable throwable) {
153
this(context, null, null, throwable);
154
}
155
156
/**
157
* Create an AsyncEvent with all parameters.
158
*/
159
public AsyncEvent(AsyncContext context, ServletRequest suppliedRequest,
160
ServletResponse suppliedResponse, Throwable throwable) {
161
this.asyncContext = context;
162
this.suppliedRequest = suppliedRequest;
163
this.suppliedResponse = suppliedResponse;
164
this.throwable = throwable;
165
}
166
167
/**
168
* Get the AsyncContext associated with this event.
169
*/
170
public AsyncContext getAsyncContext() {
171
return asyncContext;
172
}
173
174
/**
175
* Get the ServletRequest supplied when the AsyncContext was created.
176
*/
177
public ServletRequest getSuppliedRequest() {
178
return suppliedRequest;
179
}
180
181
/**
182
* Get the ServletResponse supplied when the AsyncContext was created.
183
*/
184
public ServletResponse getSuppliedResponse() {
185
return suppliedResponse;
186
}
187
188
/**
189
* Get the throwable that caused the async operation to fail.
190
*/
191
public Throwable getThrowable() {
192
return throwable;
193
}
194
}
195
```
196
197
## Non-Blocking I/O Interfaces
198
199
### ReadListener Interface
200
201
```java { .api }
202
/**
203
* Listener interface for non-blocking read operations on ServletInputStream.
204
*/
205
public interface ReadListener extends EventListener {
206
207
/**
208
* Called when data is available to be read from the input stream.
209
* This method should read all available data.
210
*/
211
void onDataAvailable() throws IOException;
212
213
/**
214
* Called when all data has been read from the input stream.
215
*/
216
void onAllDataRead() throws IOException;
217
218
/**
219
* Called when an error occurs during a non-blocking read operation.
220
*/
221
void onError(Throwable t);
222
}
223
```
224
225
### WriteListener Interface
226
227
```java { .api }
228
/**
229
* Listener interface for non-blocking write operations on ServletOutputStream.
230
*/
231
public interface WriteListener extends EventListener {
232
233
/**
234
* Called when it's possible to write data to the output stream.
235
* This method should write all pending data.
236
*/
237
void onWritePossible() throws IOException;
238
239
/**
240
* Called when an error occurs during a non-blocking write operation.
241
*/
242
void onError(Throwable t);
243
}
244
```
245
246
## Enhanced ServletInputStream
247
248
```java { .api }
249
/**
250
* Enhanced ServletInputStream with non-blocking I/O support.
251
*/
252
public abstract class ServletInputStream extends InputStream {
253
254
/**
255
* Read a line from the input stream into a byte array.
256
* Legacy method from earlier servlet versions.
257
*/
258
public int readLine(byte[] b, int off, int len) throws IOException {
259
if (len <= 0) {
260
return 0;
261
}
262
int count = 0, c;
263
264
while ((c = read()) != -1) {
265
b[off++] = (byte) c;
266
count++;
267
if (c == '\n' || count == len) {
268
break;
269
}
270
}
271
return count > 0 ? count : -1;
272
}
273
274
/**
275
* Check if all data has been read from the input stream.
276
*/
277
public abstract boolean isFinished();
278
279
/**
280
* Check if data can be read from the input stream without blocking.
281
*/
282
public abstract boolean isReady();
283
284
/**
285
* Set a ReadListener for non-blocking I/O operations.
286
* The container will invoke the listener when data becomes available.
287
*/
288
public abstract void setReadListener(ReadListener readListener);
289
}
290
```
291
292
## Enhanced ServletOutputStream
293
294
```java { .api }
295
/**
296
* Enhanced ServletOutputStream with non-blocking I/O support.
297
*/
298
public abstract class ServletOutputStream extends OutputStream {
299
300
/**
301
* Write a string to the output stream.
302
*/
303
public void print(String s) throws IOException {
304
if (s == null) s = "null";
305
int len = s.length();
306
byte[] out = new byte[len];
307
for (int i = 0; i < len; i++) {
308
out[i] = (byte) s.charAt(i);
309
}
310
write(out, 0, len);
311
}
312
313
/**
314
* Write a boolean value to the output stream.
315
*/
316
public void print(boolean b) throws IOException {
317
print(b ? "true" : "false");
318
}
319
320
/**
321
* Write a character to the output stream.
322
*/
323
public void print(char c) throws IOException {
324
print(String.valueOf(c));
325
}
326
327
/**
328
* Write an integer to the output stream.
329
*/
330
public void print(int i) throws IOException {
331
print(String.valueOf(i));
332
}
333
334
/**
335
* Write a long value to the output stream.
336
*/
337
public void print(long l) throws IOException {
338
print(String.valueOf(l));
339
}
340
341
/**
342
* Write a float value to the output stream.
343
*/
344
public void print(float f) throws IOException {
345
print(String.valueOf(f));
346
}
347
348
/**
349
* Write a double value to the output stream.
350
*/
351
public void print(double d) throws IOException {
352
print(String.valueOf(d));
353
}
354
355
/**
356
* Write a line separator to the output stream.
357
*/
358
public void println() throws IOException {
359
print("\r\n");
360
}
361
362
/**
363
* Write a string followed by a line separator.
364
*/
365
public void println(String s) throws IOException {
366
print(s);
367
println();
368
}
369
370
/**
371
* Write a boolean followed by a line separator.
372
*/
373
public void println(boolean b) throws IOException {
374
print(b);
375
println();
376
}
377
378
/**
379
* Write a character followed by a line separator.
380
*/
381
public void println(char c) throws IOException {
382
print(c);
383
println();
384
}
385
386
/**
387
* Write an integer followed by a line separator.
388
*/
389
public void println(int i) throws IOException {
390
print(i);
391
println();
392
}
393
394
/**
395
* Write a long followed by a line separator.
396
*/
397
public void println(long l) throws IOException {
398
print(l);
399
println();
400
}
401
402
/**
403
* Write a float followed by a line separator.
404
*/
405
public void println(float f) throws IOException {
406
print(f);
407
println();
408
}
409
410
/**
411
* Write a double followed by a line separator.
412
*/
413
public void println(double d) throws IOException {
414
print(d);
415
println();
416
}
417
418
/**
419
* Check if data can be written to the output stream without blocking.
420
*/
421
public abstract boolean isReady();
422
423
/**
424
* Set a WriteListener for non-blocking I/O operations.
425
* The container will invoke the listener when writing becomes possible.
426
*/
427
public abstract void setWriteListener(WriteListener writeListener);
428
}
429
```
430
431
## Asynchronous Processing Examples
432
433
### Basic Async Servlet
434
435
```java { .api }
436
/**
437
* Basic asynchronous servlet example
438
*/
439
@WebServlet(value = "/async-basic", asyncSupported = true)
440
public class BasicAsyncServlet extends HttpServlet {
441
442
private ExecutorService executor = Executors.newCachedThreadPool();
443
444
@Override
445
protected void doGet(HttpServletRequest request, HttpServletResponse response)
446
throws ServletException, IOException {
447
448
// Start asynchronous processing
449
AsyncContext asyncContext = request.startAsync();
450
451
// Set timeout (30 seconds)
452
asyncContext.setTimeout(30000);
453
454
// Add async listener for event handling
455
asyncContext.addListener(new AsyncListener() {
456
@Override
457
public void onComplete(AsyncEvent event) throws IOException {
458
System.out.println("Async operation completed");
459
}
460
461
@Override
462
public void onTimeout(AsyncEvent event) throws IOException {
463
System.out.println("Async operation timed out");
464
AsyncContext ctx = event.getAsyncContext();
465
try {
466
ServletResponse resp = ctx.getResponse();
467
resp.setContentType("text/plain");
468
resp.getWriter().write("Request timed out");
469
} finally {
470
ctx.complete();
471
}
472
}
473
474
@Override
475
public void onError(AsyncEvent event) throws IOException {
476
System.out.println("Async operation failed: " + event.getThrowable());
477
event.getAsyncContext().complete();
478
}
479
480
@Override
481
public void onStartAsync(AsyncEvent event) throws IOException {
482
System.out.println("Async operation started");
483
}
484
});
485
486
// Start async work using AsyncContext.start()
487
asyncContext.start(new Runnable() {
488
@Override
489
public void run() {
490
try {
491
// Simulate long-running operation
492
Thread.sleep(5000);
493
494
// Generate response
495
ServletResponse resp = asyncContext.getResponse();
496
resp.setContentType("text/plain;charset=UTF-8");
497
PrintWriter writer = resp.getWriter();
498
writer.write("Async operation completed at " + new Date());
499
writer.flush();
500
501
} catch (Exception e) {
502
throw new RuntimeException(e);
503
} finally {
504
asyncContext.complete();
505
}
506
}
507
});
508
}
509
510
@Override
511
public void destroy() {
512
executor.shutdown();
513
try {
514
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
515
executor.shutdownNow();
516
}
517
} catch (InterruptedException e) {
518
executor.shutdownNow();
519
}
520
}
521
}
522
```
523
524
### Advanced Async Processing with Thread Pool
525
526
```java { .api }
527
/**
528
* Advanced asynchronous servlet with custom thread pool and error handling
529
*/
530
@WebServlet(value = "/async-advanced", asyncSupported = true)
531
public class AdvancedAsyncServlet extends HttpServlet {
532
533
private ExecutorService executor;
534
private ScheduledExecutorService scheduler;
535
536
@Override
537
public void init() throws ServletException {
538
// Create custom thread pools
539
executor = Executors.newFixedThreadPool(10, r -> {
540
Thread t = new Thread(r, "AsyncProcessor-" + System.currentTimeMillis());
541
t.setDaemon(true);
542
return t;
543
});
544
545
scheduler = Executors.newScheduledThreadPool(2, r -> {
546
Thread t = new Thread(r, "AsyncScheduler-" + System.currentTimeMillis());
547
t.setDaemon(true);
548
return t;
549
});
550
}
551
552
@Override
553
protected void doPost(HttpServletRequest request, HttpServletResponse response)
554
throws ServletException, IOException {
555
556
String operation = request.getParameter("operation");
557
String delayParam = request.getParameter("delay");
558
559
int delay = 1000; // Default 1 second
560
if (delayParam != null) {
561
try {
562
delay = Integer.parseInt(delayParam);
563
} catch (NumberFormatException e) {
564
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid delay parameter");
565
return;
566
}
567
}
568
569
AsyncContext asyncContext = request.startAsync(request, response);
570
asyncContext.setTimeout(60000); // 60 second timeout
571
572
// Add comprehensive async listener
573
asyncContext.addListener(new ComprehensiveAsyncListener());
574
575
// Submit async task based on operation type
576
switch (operation != null ? operation : "default") {
577
case "immediate":
578
handleImmediateResponse(asyncContext);
579
break;
580
case "delayed":
581
handleDelayedResponse(asyncContext, delay);
582
break;
583
case "stream":
584
handleStreamingResponse(asyncContext);
585
break;
586
case "error":
587
handleErrorResponse(asyncContext);
588
break;
589
default:
590
handleDefaultResponse(asyncContext, delay);
591
}
592
}
593
594
private void handleImmediateResponse(AsyncContext asyncContext) {
595
executor.submit(() -> {
596
try {
597
ServletResponse response = asyncContext.getResponse();
598
response.setContentType("application/json;charset=UTF-8");
599
600
PrintWriter writer = response.getWriter();
601
writer.write("{");
602
writer.write("\"status\":\"success\",");
603
writer.write("\"message\":\"Immediate response\",");
604
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
605
writer.write("}");
606
writer.flush();
607
608
} catch (IOException e) {
609
throw new RuntimeException(e);
610
} finally {
611
asyncContext.complete();
612
}
613
});
614
}
615
616
private void handleDelayedResponse(AsyncContext asyncContext, int delay) {
617
scheduler.schedule(() -> {
618
try {
619
ServletResponse response = asyncContext.getResponse();
620
response.setContentType("application/json;charset=UTF-8");
621
622
PrintWriter writer = response.getWriter();
623
writer.write("{");
624
writer.write("\"status\":\"success\",");
625
writer.write("\"message\":\"Delayed response after " + delay + "ms\",");
626
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
627
writer.write("}");
628
writer.flush();
629
630
} catch (IOException e) {
631
throw new RuntimeException(e);
632
} finally {
633
asyncContext.complete();
634
}
635
}, delay, TimeUnit.MILLISECONDS);
636
}
637
638
private void handleStreamingResponse(AsyncContext asyncContext) {
639
executor.submit(() -> {
640
try {
641
ServletResponse response = asyncContext.getResponse();
642
response.setContentType("text/plain;charset=UTF-8");
643
644
PrintWriter writer = response.getWriter();
645
646
// Send chunked response
647
for (int i = 1; i <= 10; i++) {
648
writer.println("Chunk " + i + " sent at " + Instant.now());
649
writer.flush();
650
651
Thread.sleep(500); // Delay between chunks
652
}
653
654
writer.println("Streaming completed");
655
writer.flush();
656
657
} catch (Exception e) {
658
throw new RuntimeException(e);
659
} finally {
660
asyncContext.complete();
661
}
662
});
663
}
664
665
private void handleErrorResponse(AsyncContext asyncContext) {
666
executor.submit(() -> {
667
try {
668
// Simulate processing delay
669
Thread.sleep(1000);
670
671
// Intentionally throw an error
672
throw new RuntimeException("Simulated async error");
673
674
} catch (Exception e) {
675
try {
676
ServletResponse response = asyncContext.getResponse();
677
response.setContentType("application/json;charset=UTF-8");
678
679
if (!response.isCommitted()) {
680
PrintWriter writer = response.getWriter();
681
writer.write("{");
682
writer.write("\"status\":\"error\",");
683
writer.write("\"message\":\"" + e.getMessage() + "\",");
684
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
685
writer.write("}");
686
writer.flush();
687
}
688
} catch (IOException ioException) {
689
ioException.printStackTrace();
690
} finally {
691
asyncContext.complete();
692
}
693
}
694
});
695
}
696
697
private void handleDefaultResponse(AsyncContext asyncContext, int delay) {
698
CompletableFuture
699
.supplyAsync(() -> {
700
try {
701
Thread.sleep(delay);
702
return "Processing completed after " + delay + "ms";
703
} catch (InterruptedException e) {
704
throw new RuntimeException(e);
705
}
706
}, executor)
707
.whenComplete((result, throwable) -> {
708
try {
709
ServletResponse response = asyncContext.getResponse();
710
response.setContentType("application/json;charset=UTF-8");
711
712
PrintWriter writer = response.getWriter();
713
if (throwable == null) {
714
writer.write("{");
715
writer.write("\"status\":\"success\",");
716
writer.write("\"message\":\"" + result + "\",");
717
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
718
writer.write("}");
719
} else {
720
writer.write("{");
721
writer.write("\"status\":\"error\",");
722
writer.write("\"message\":\"" + throwable.getMessage() + "\",");
723
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
724
writer.write("}");
725
}
726
writer.flush();
727
728
} catch (IOException e) {
729
e.printStackTrace();
730
} finally {
731
asyncContext.complete();
732
}
733
});
734
}
735
736
private class ComprehensiveAsyncListener implements AsyncListener {
737
@Override
738
public void onComplete(AsyncEvent event) throws IOException {
739
System.out.println("Async request completed successfully");
740
}
741
742
@Override
743
public void onTimeout(AsyncEvent event) throws IOException {
744
System.out.println("Async request timed out");
745
746
AsyncContext ctx = event.getAsyncContext();
747
try {
748
ServletResponse response = ctx.getResponse();
749
if (!response.isCommitted()) {
750
response.setContentType("application/json;charset=UTF-8");
751
PrintWriter writer = response.getWriter();
752
writer.write("{");
753
writer.write("\"status\":\"timeout\",");
754
writer.write("\"message\":\"Request processing timed out\",");
755
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
756
writer.write("}");
757
writer.flush();
758
}
759
} finally {
760
ctx.complete();
761
}
762
}
763
764
@Override
765
public void onError(AsyncEvent event) throws IOException {
766
Throwable throwable = event.getThrowable();
767
System.out.println("Async request failed: " + throwable.getMessage());
768
769
AsyncContext ctx = event.getAsyncContext();
770
try {
771
ServletResponse response = ctx.getResponse();
772
if (!response.isCommitted()) {
773
response.setContentType("application/json;charset=UTF-8");
774
PrintWriter writer = response.getWriter();
775
writer.write("{");
776
writer.write("\"status\":\"error\",");
777
writer.write("\"message\":\"" + throwable.getMessage() + "\",");
778
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
779
writer.write("}");
780
writer.flush();
781
}
782
} finally {
783
ctx.complete();
784
}
785
}
786
787
@Override
788
public void onStartAsync(AsyncEvent event) throws IOException {
789
System.out.println("Async request started");
790
}
791
}
792
793
@Override
794
public void destroy() {
795
executor.shutdown();
796
scheduler.shutdown();
797
try {
798
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
799
executor.shutdownNow();
800
}
801
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
802
scheduler.shutdownNow();
803
}
804
} catch (InterruptedException e) {
805
executor.shutdownNow();
806
scheduler.shutdownNow();
807
}
808
}
809
}
810
```
811
812
## Non-Blocking I/O Examples
813
814
### Non-Blocking Read Example
815
816
```java { .api }
817
/**
818
* Servlet demonstrating non-blocking input stream reading
819
*/
820
@WebServlet(value = "/non-blocking-read", asyncSupported = true)
821
public class NonBlockingReadServlet extends HttpServlet {
822
823
@Override
824
protected void doPost(HttpServletRequest request, HttpServletResponse response)
825
throws ServletException, IOException {
826
827
AsyncContext asyncContext = request.startAsync();
828
asyncContext.setTimeout(30000);
829
830
ServletInputStream inputStream = request.getInputStream();
831
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
832
833
inputStream.setReadListener(new ReadListener() {
834
@Override
835
public void onDataAvailable() throws IOException {
836
// Read all available data
837
byte[] data = new byte[1024];
838
int bytesRead;
839
840
while (inputStream.isReady() && (bytesRead = inputStream.read(data)) != -1) {
841
buffer.write(data, 0, bytesRead);
842
}
843
}
844
845
@Override
846
public void onAllDataRead() throws IOException {
847
try {
848
// Process the complete request data
849
String requestData = buffer.toString("UTF-8");
850
String processedData = processData(requestData);
851
852
// Send response
853
ServletResponse resp = asyncContext.getResponse();
854
resp.setContentType("application/json;charset=UTF-8");
855
856
PrintWriter writer = resp.getWriter();
857
writer.write("{");
858
writer.write("\"status\":\"success\",");
859
writer.write("\"originalSize\":" + requestData.length() + ",");
860
writer.write("\"processedData\":\"" + processedData + "\",");
861
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
862
writer.write("}");
863
writer.flush();
864
865
} finally {
866
asyncContext.complete();
867
}
868
}
869
870
@Override
871
public void onError(Throwable t) {
872
System.err.println("Error reading request data: " + t.getMessage());
873
try {
874
ServletResponse resp = asyncContext.getResponse();
875
if (!resp.isCommitted()) {
876
resp.setContentType("application/json;charset=UTF-8");
877
PrintWriter writer = resp.getWriter();
878
writer.write("{");
879
writer.write("\"status\":\"error\",");
880
writer.write("\"message\":\"" + t.getMessage() + "\"");
881
writer.write("}");
882
writer.flush();
883
}
884
} catch (IOException e) {
885
e.printStackTrace();
886
} finally {
887
asyncContext.complete();
888
}
889
}
890
});
891
}
892
893
private String processData(String data) {
894
// Simulate data processing
895
return data.toUpperCase().replaceAll("\\s+", "_");
896
}
897
}
898
```
899
900
### Non-Blocking Write Example
901
902
```java { .api }
903
/**
904
* Servlet demonstrating non-blocking output stream writing
905
*/
906
@WebServlet(value = "/non-blocking-write", asyncSupported = true)
907
public class NonBlockingWriteServlet extends HttpServlet {
908
909
@Override
910
protected void doGet(HttpServletRequest request, HttpServletResponse response)
911
throws ServletException, IOException {
912
913
AsyncContext asyncContext = request.startAsync();
914
asyncContext.setTimeout(60000);
915
916
response.setContentType("text/plain;charset=UTF-8");
917
ServletOutputStream outputStream = response.getOutputStream();
918
919
// Generate large amount of data to write
920
Queue<String> dataQueue = generateLargeDataSet();
921
922
outputStream.setWriteListener(new WriteListener() {
923
@Override
924
public void onWritePossible() throws IOException {
925
// Write data while the output stream is ready
926
while (outputStream.isReady() && !dataQueue.isEmpty()) {
927
String data = dataQueue.poll();
928
outputStream.print(data);
929
outputStream.println(); // Add line separator
930
}
931
932
// Check if all data has been written
933
if (dataQueue.isEmpty()) {
934
outputStream.println("=== End of Data ===");
935
asyncContext.complete();
936
}
937
}
938
939
@Override
940
public void onError(Throwable t) {
941
System.err.println("Error writing response data: " + t.getMessage());
942
asyncContext.complete();
943
}
944
});
945
}
946
947
private Queue<String> generateLargeDataSet() {
948
Queue<String> data = new LinkedList<>();
949
950
// Generate 1000 lines of sample data
951
for (int i = 1; i <= 1000; i++) {
952
data.offer("Line " + i + ": This is sample data generated at " +
953
Instant.now() + " with some additional content to make it longer.");
954
}
955
956
return data;
957
}
958
}
959
```
960
961
### File Upload with Non-Blocking I/O
962
963
```java { .api }
964
/**
965
* Servlet for handling file uploads with non-blocking I/O
966
*/
967
@WebServlet(value = "/upload-async", asyncSupported = true)
968
@MultipartConfig(
969
location = "/tmp",
970
fileSizeThreshold = 1024 * 1024, // 1 MB
971
maxFileSize = 1024 * 1024 * 50, // 50 MB
972
maxRequestSize = 1024 * 1024 * 100 // 100 MB
973
)
974
public class AsyncFileUploadServlet extends HttpServlet {
975
976
@Override
977
protected void doPost(HttpServletRequest request, HttpServletResponse response)
978
throws ServletException, IOException {
979
980
AsyncContext asyncContext = request.startAsync();
981
asyncContext.setTimeout(300000); // 5 minutes for large uploads
982
983
// Process multipart request asynchronously
984
CompletableFuture.runAsync(() -> {
985
try {
986
Collection<Part> parts = request.getParts();
987
List<String> uploadedFiles = new ArrayList<>();
988
989
for (Part part : parts) {
990
if (part.getName().equals("file") && part.getSize() > 0) {
991
String fileName = getSubmittedFileName(part);
992
String uploadPath = saveFileAsync(part, fileName);
993
uploadedFiles.add(fileName + " (" + part.getSize() + " bytes)");
994
}
995
}
996
997
// Send success response
998
ServletResponse resp = asyncContext.getResponse();
999
resp.setContentType("application/json;charset=UTF-8");
1000
1001
PrintWriter writer = resp.getWriter();
1002
writer.write("{");
1003
writer.write("\"status\":\"success\",");
1004
writer.write("\"filesUploaded\":" + uploadedFiles.size() + ",");
1005
writer.write("\"files\":[");
1006
for (int i = 0; i < uploadedFiles.size(); i++) {
1007
if (i > 0) writer.write(",");
1008
writer.write("\"" + uploadedFiles.get(i) + "\"");
1009
}
1010
writer.write("],");
1011
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
1012
writer.write("}");
1013
writer.flush();
1014
1015
} catch (Exception e) {
1016
try {
1017
ServletResponse resp = asyncContext.getResponse();
1018
if (!resp.isCommitted()) {
1019
resp.setContentType("application/json;charset=UTF-8");
1020
PrintWriter writer = resp.getWriter();
1021
writer.write("{");
1022
writer.write("\"status\":\"error\",");
1023
writer.write("\"message\":\"" + e.getMessage() + "\"");
1024
writer.write("}");
1025
writer.flush();
1026
}
1027
} catch (IOException ioException) {
1028
ioException.printStackTrace();
1029
}
1030
} finally {
1031
asyncContext.complete();
1032
}
1033
});
1034
}
1035
1036
private String saveFileAsync(Part part, String fileName) throws IOException {
1037
String uploadDir = getServletContext().getRealPath("/uploads");
1038
Files.createDirectories(Paths.get(uploadDir));
1039
1040
String filePath = uploadDir + File.separator +
1041
System.currentTimeMillis() + "_" + fileName;
1042
1043
// Save file using NIO for better performance
1044
try (InputStream input = part.getInputStream()) {
1045
Files.copy(input, Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING);
1046
}
1047
1048
return filePath;
1049
}
1050
1051
private String getSubmittedFileName(Part part) {
1052
String contentDisposition = part.getHeader("Content-Disposition");
1053
if (contentDisposition != null) {
1054
String[] elements = contentDisposition.split(";");
1055
for (String element : elements) {
1056
if (element.trim().startsWith("filename")) {
1057
return element.substring(element.indexOf('=') + 1)
1058
.trim().replace("\"", "");
1059
}
1060
}
1061
}
1062
return "unknown_" + System.currentTimeMillis();
1063
}
1064
}
1065
```
1066
1067
This comprehensive coverage of asynchronous processing and non-blocking I/O provides all the tools needed for building scalable, high-performance servlet applications that can handle many concurrent connections efficiently.