Java Servlet API specification defining core interfaces and classes for web application development
—
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.
/**
* Interface representing the execution context for an asynchronous operation
* that was initiated on a ServletRequest.
*/
public interface AsyncContext {
// Request attribute constants for async context information
public static final String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
public static final String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
public static final String ASYNC_MAPPING = "javax.servlet.async.mapping";
public static final String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
public static final String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
public static final String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";
/**
* Get the original ServletRequest that was used to initialize this AsyncContext.
*/
ServletRequest getRequest();
/**
* Get the original ServletResponse that was used to initialize this AsyncContext.
*/
ServletResponse getResponse();
/**
* Check if this AsyncContext was initialized with the original or wrapped
* ServletRequest and ServletResponse objects.
*/
boolean hasOriginalRequestAndResponse();
/**
* Dispatch the request and response to the container for processing.
* Uses the original request URI.
*/
void dispatch();
/**
* Dispatch the request and response to the specified path.
*/
void dispatch(String path);
/**
* Dispatch the request and response to the specified context and path.
*/
void dispatch(ServletContext context, String path);
/**
* Complete the asynchronous operation and close the response.
*/
void complete();
/**
* Start a new thread to process the asynchronous operation.
*/
void start(Runnable run);
/**
* Add an AsyncListener to receive notifications about async events.
*/
void addListener(AsyncListener listener);
/**
* Add an AsyncListener with associated request and response objects.
*/
void addListener(AsyncListener listener, ServletRequest servletRequest,
ServletResponse servletResponse);
/**
* Create and return an AsyncListener instance.
*/
<T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException;
/**
* Set the timeout for this asynchronous operation in milliseconds.
*/
void setTimeout(long timeout);
/**
* Get the timeout for this asynchronous operation in milliseconds.
*/
long getTimeout();
}/**
* Listener interface for receiving notifications about asynchronous operations.
*/
public interface AsyncListener extends EventListener {
/**
* Called when an asynchronous operation has completed.
*/
void onComplete(AsyncEvent event) throws IOException;
/**
* Called when an asynchronous operation has timed out.
*/
void onTimeout(AsyncEvent event) throws IOException;
/**
* Called when an asynchronous operation has failed or encountered an error.
*/
void onError(AsyncEvent event) throws IOException;
/**
* Called when a new asynchronous operation is started.
*/
void onStartAsync(AsyncEvent event) throws IOException;
}/**
* Event class representing an asynchronous operation event.
*/
public class AsyncEvent {
private AsyncContext asyncContext;
private ServletRequest suppliedRequest;
private ServletResponse suppliedResponse;
private Throwable throwable;
/**
* Create an AsyncEvent with the specified AsyncContext.
*/
public AsyncEvent(AsyncContext context) {
this(context, null, null, null);
}
/**
* Create an AsyncEvent with AsyncContext and associated request/response.
*/
public AsyncEvent(AsyncContext context, ServletRequest suppliedRequest,
ServletResponse suppliedResponse) {
this(context, suppliedRequest, suppliedResponse, null);
}
/**
* Create an AsyncEvent with AsyncContext and throwable.
*/
public AsyncEvent(AsyncContext context, Throwable throwable) {
this(context, null, null, throwable);
}
/**
* Create an AsyncEvent with all parameters.
*/
public AsyncEvent(AsyncContext context, ServletRequest suppliedRequest,
ServletResponse suppliedResponse, Throwable throwable) {
this.asyncContext = context;
this.suppliedRequest = suppliedRequest;
this.suppliedResponse = suppliedResponse;
this.throwable = throwable;
}
/**
* Get the AsyncContext associated with this event.
*/
public AsyncContext getAsyncContext() {
return asyncContext;
}
/**
* Get the ServletRequest supplied when the AsyncContext was created.
*/
public ServletRequest getSuppliedRequest() {
return suppliedRequest;
}
/**
* Get the ServletResponse supplied when the AsyncContext was created.
*/
public ServletResponse getSuppliedResponse() {
return suppliedResponse;
}
/**
* Get the throwable that caused the async operation to fail.
*/
public Throwable getThrowable() {
return throwable;
}
}/**
* Listener interface for non-blocking read operations on ServletInputStream.
*/
public interface ReadListener extends EventListener {
/**
* Called when data is available to be read from the input stream.
* This method should read all available data.
*/
void onDataAvailable() throws IOException;
/**
* Called when all data has been read from the input stream.
*/
void onAllDataRead() throws IOException;
/**
* Called when an error occurs during a non-blocking read operation.
*/
void onError(Throwable t);
}/**
* Listener interface for non-blocking write operations on ServletOutputStream.
*/
public interface WriteListener extends EventListener {
/**
* Called when it's possible to write data to the output stream.
* This method should write all pending data.
*/
void onWritePossible() throws IOException;
/**
* Called when an error occurs during a non-blocking write operation.
*/
void onError(Throwable t);
}/**
* Enhanced ServletInputStream with non-blocking I/O support.
*/
public abstract class ServletInputStream extends InputStream {
/**
* Read a line from the input stream into a byte array.
* Legacy method from earlier servlet versions.
*/
public int readLine(byte[] b, int off, int len) throws IOException {
if (len <= 0) {
return 0;
}
int count = 0, c;
while ((c = read()) != -1) {
b[off++] = (byte) c;
count++;
if (c == '\n' || count == len) {
break;
}
}
return count > 0 ? count : -1;
}
/**
* Check if all data has been read from the input stream.
*/
public abstract boolean isFinished();
/**
* Check if data can be read from the input stream without blocking.
*/
public abstract boolean isReady();
/**
* Set a ReadListener for non-blocking I/O operations.
* The container will invoke the listener when data becomes available.
*/
public abstract void setReadListener(ReadListener readListener);
}/**
* Enhanced ServletOutputStream with non-blocking I/O support.
*/
public abstract class ServletOutputStream extends OutputStream {
/**
* Write a string to the output stream.
*/
public void print(String s) throws IOException {
if (s == null) s = "null";
int len = s.length();
byte[] out = new byte[len];
for (int i = 0; i < len; i++) {
out[i] = (byte) s.charAt(i);
}
write(out, 0, len);
}
/**
* Write a boolean value to the output stream.
*/
public void print(boolean b) throws IOException {
print(b ? "true" : "false");
}
/**
* Write a character to the output stream.
*/
public void print(char c) throws IOException {
print(String.valueOf(c));
}
/**
* Write an integer to the output stream.
*/
public void print(int i) throws IOException {
print(String.valueOf(i));
}
/**
* Write a long value to the output stream.
*/
public void print(long l) throws IOException {
print(String.valueOf(l));
}
/**
* Write a float value to the output stream.
*/
public void print(float f) throws IOException {
print(String.valueOf(f));
}
/**
* Write a double value to the output stream.
*/
public void print(double d) throws IOException {
print(String.valueOf(d));
}
/**
* Write a line separator to the output stream.
*/
public void println() throws IOException {
print("\r\n");
}
/**
* Write a string followed by a line separator.
*/
public void println(String s) throws IOException {
print(s);
println();
}
/**
* Write a boolean followed by a line separator.
*/
public void println(boolean b) throws IOException {
print(b);
println();
}
/**
* Write a character followed by a line separator.
*/
public void println(char c) throws IOException {
print(c);
println();
}
/**
* Write an integer followed by a line separator.
*/
public void println(int i) throws IOException {
print(i);
println();
}
/**
* Write a long followed by a line separator.
*/
public void println(long l) throws IOException {
print(l);
println();
}
/**
* Write a float followed by a line separator.
*/
public void println(float f) throws IOException {
print(f);
println();
}
/**
* Write a double followed by a line separator.
*/
public void println(double d) throws IOException {
print(d);
println();
}
/**
* Check if data can be written to the output stream without blocking.
*/
public abstract boolean isReady();
/**
* Set a WriteListener for non-blocking I/O operations.
* The container will invoke the listener when writing becomes possible.
*/
public abstract void setWriteListener(WriteListener writeListener);
}/**
* Basic asynchronous servlet example
*/
@WebServlet(value = "/async-basic", asyncSupported = true)
public class BasicAsyncServlet extends HttpServlet {
private ExecutorService executor = Executors.newCachedThreadPool();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Start asynchronous processing
AsyncContext asyncContext = request.startAsync();
// Set timeout (30 seconds)
asyncContext.setTimeout(30000);
// Add async listener for event handling
asyncContext.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("Async operation completed");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("Async operation timed out");
AsyncContext ctx = event.getAsyncContext();
try {
ServletResponse resp = ctx.getResponse();
resp.setContentType("text/plain");
resp.getWriter().write("Request timed out");
} finally {
ctx.complete();
}
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println("Async operation failed: " + event.getThrowable());
event.getAsyncContext().complete();
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("Async operation started");
}
});
// Start async work using AsyncContext.start()
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
// Simulate long-running operation
Thread.sleep(5000);
// Generate response
ServletResponse resp = asyncContext.getResponse();
resp.setContentType("text/plain;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("Async operation completed at " + new Date());
writer.flush();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
asyncContext.complete();
}
}
});
}
@Override
public void destroy() {
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}/**
* Advanced asynchronous servlet with custom thread pool and error handling
*/
@WebServlet(value = "/async-advanced", asyncSupported = true)
public class AdvancedAsyncServlet extends HttpServlet {
private ExecutorService executor;
private ScheduledExecutorService scheduler;
@Override
public void init() throws ServletException {
// Create custom thread pools
executor = Executors.newFixedThreadPool(10, r -> {
Thread t = new Thread(r, "AsyncProcessor-" + System.currentTimeMillis());
t.setDaemon(true);
return t;
});
scheduler = Executors.newScheduledThreadPool(2, r -> {
Thread t = new Thread(r, "AsyncScheduler-" + System.currentTimeMillis());
t.setDaemon(true);
return t;
});
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String operation = request.getParameter("operation");
String delayParam = request.getParameter("delay");
int delay = 1000; // Default 1 second
if (delayParam != null) {
try {
delay = Integer.parseInt(delayParam);
} catch (NumberFormatException e) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid delay parameter");
return;
}
}
AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(60000); // 60 second timeout
// Add comprehensive async listener
asyncContext.addListener(new ComprehensiveAsyncListener());
// Submit async task based on operation type
switch (operation != null ? operation : "default") {
case "immediate":
handleImmediateResponse(asyncContext);
break;
case "delayed":
handleDelayedResponse(asyncContext, delay);
break;
case "stream":
handleStreamingResponse(asyncContext);
break;
case "error":
handleErrorResponse(asyncContext);
break;
default:
handleDefaultResponse(asyncContext, delay);
}
}
private void handleImmediateResponse(AsyncContext asyncContext) {
executor.submit(() -> {
try {
ServletResponse response = asyncContext.getResponse();
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("{");
writer.write("\"status\":\"success\",");
writer.write("\"message\":\"Immediate response\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
asyncContext.complete();
}
});
}
private void handleDelayedResponse(AsyncContext asyncContext, int delay) {
scheduler.schedule(() -> {
try {
ServletResponse response = asyncContext.getResponse();
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("{");
writer.write("\"status\":\"success\",");
writer.write("\"message\":\"Delayed response after " + delay + "ms\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
asyncContext.complete();
}
}, delay, TimeUnit.MILLISECONDS);
}
private void handleStreamingResponse(AsyncContext asyncContext) {
executor.submit(() -> {
try {
ServletResponse response = asyncContext.getResponse();
response.setContentType("text/plain;charset=UTF-8");
PrintWriter writer = response.getWriter();
// Send chunked response
for (int i = 1; i <= 10; i++) {
writer.println("Chunk " + i + " sent at " + Instant.now());
writer.flush();
Thread.sleep(500); // Delay between chunks
}
writer.println("Streaming completed");
writer.flush();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
asyncContext.complete();
}
});
}
private void handleErrorResponse(AsyncContext asyncContext) {
executor.submit(() -> {
try {
// Simulate processing delay
Thread.sleep(1000);
// Intentionally throw an error
throw new RuntimeException("Simulated async error");
} catch (Exception e) {
try {
ServletResponse response = asyncContext.getResponse();
response.setContentType("application/json;charset=UTF-8");
if (!response.isCommitted()) {
PrintWriter writer = response.getWriter();
writer.write("{");
writer.write("\"status\":\"error\",");
writer.write("\"message\":\"" + e.getMessage() + "\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
}
} catch (IOException ioException) {
ioException.printStackTrace();
} finally {
asyncContext.complete();
}
}
});
}
private void handleDefaultResponse(AsyncContext asyncContext, int delay) {
CompletableFuture
.supplyAsync(() -> {
try {
Thread.sleep(delay);
return "Processing completed after " + delay + "ms";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, executor)
.whenComplete((result, throwable) -> {
try {
ServletResponse response = asyncContext.getResponse();
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
if (throwable == null) {
writer.write("{");
writer.write("\"status\":\"success\",");
writer.write("\"message\":\"" + result + "\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
} else {
writer.write("{");
writer.write("\"status\":\"error\",");
writer.write("\"message\":\"" + throwable.getMessage() + "\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
asyncContext.complete();
}
});
}
private class ComprehensiveAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("Async request completed successfully");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("Async request timed out");
AsyncContext ctx = event.getAsyncContext();
try {
ServletResponse response = ctx.getResponse();
if (!response.isCommitted()) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("{");
writer.write("\"status\":\"timeout\",");
writer.write("\"message\":\"Request processing timed out\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
}
} finally {
ctx.complete();
}
}
@Override
public void onError(AsyncEvent event) throws IOException {
Throwable throwable = event.getThrowable();
System.out.println("Async request failed: " + throwable.getMessage());
AsyncContext ctx = event.getAsyncContext();
try {
ServletResponse response = ctx.getResponse();
if (!response.isCommitted()) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("{");
writer.write("\"status\":\"error\",");
writer.write("\"message\":\"" + throwable.getMessage() + "\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
}
} finally {
ctx.complete();
}
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("Async request started");
}
}
@Override
public void destroy() {
executor.shutdown();
scheduler.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
scheduler.shutdownNow();
}
}
}/**
* Servlet demonstrating non-blocking input stream reading
*/
@WebServlet(value = "/non-blocking-read", asyncSupported = true)
public class NonBlockingReadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(30000);
ServletInputStream inputStream = request.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
inputStream.setReadListener(new ReadListener() {
@Override
public void onDataAvailable() throws IOException {
// Read all available data
byte[] data = new byte[1024];
int bytesRead;
while (inputStream.isReady() && (bytesRead = inputStream.read(data)) != -1) {
buffer.write(data, 0, bytesRead);
}
}
@Override
public void onAllDataRead() throws IOException {
try {
// Process the complete request data
String requestData = buffer.toString("UTF-8");
String processedData = processData(requestData);
// Send response
ServletResponse resp = asyncContext.getResponse();
resp.setContentType("application/json;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("{");
writer.write("\"status\":\"success\",");
writer.write("\"originalSize\":" + requestData.length() + ",");
writer.write("\"processedData\":\"" + processedData + "\",");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
} finally {
asyncContext.complete();
}
}
@Override
public void onError(Throwable t) {
System.err.println("Error reading request data: " + t.getMessage());
try {
ServletResponse resp = asyncContext.getResponse();
if (!resp.isCommitted()) {
resp.setContentType("application/json;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("{");
writer.write("\"status\":\"error\",");
writer.write("\"message\":\"" + t.getMessage() + "\"");
writer.write("}");
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
asyncContext.complete();
}
}
});
}
private String processData(String data) {
// Simulate data processing
return data.toUpperCase().replaceAll("\\s+", "_");
}
}/**
* Servlet demonstrating non-blocking output stream writing
*/
@WebServlet(value = "/non-blocking-write", asyncSupported = true)
public class NonBlockingWriteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(60000);
response.setContentType("text/plain;charset=UTF-8");
ServletOutputStream outputStream = response.getOutputStream();
// Generate large amount of data to write
Queue<String> dataQueue = generateLargeDataSet();
outputStream.setWriteListener(new WriteListener() {
@Override
public void onWritePossible() throws IOException {
// Write data while the output stream is ready
while (outputStream.isReady() && !dataQueue.isEmpty()) {
String data = dataQueue.poll();
outputStream.print(data);
outputStream.println(); // Add line separator
}
// Check if all data has been written
if (dataQueue.isEmpty()) {
outputStream.println("=== End of Data ===");
asyncContext.complete();
}
}
@Override
public void onError(Throwable t) {
System.err.println("Error writing response data: " + t.getMessage());
asyncContext.complete();
}
});
}
private Queue<String> generateLargeDataSet() {
Queue<String> data = new LinkedList<>();
// Generate 1000 lines of sample data
for (int i = 1; i <= 1000; i++) {
data.offer("Line " + i + ": This is sample data generated at " +
Instant.now() + " with some additional content to make it longer.");
}
return data;
}
}/**
* Servlet for handling file uploads with non-blocking I/O
*/
@WebServlet(value = "/upload-async", asyncSupported = true)
@MultipartConfig(
location = "/tmp",
fileSizeThreshold = 1024 * 1024, // 1 MB
maxFileSize = 1024 * 1024 * 50, // 50 MB
maxRequestSize = 1024 * 1024 * 100 // 100 MB
)
public class AsyncFileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(300000); // 5 minutes for large uploads
// Process multipart request asynchronously
CompletableFuture.runAsync(() -> {
try {
Collection<Part> parts = request.getParts();
List<String> uploadedFiles = new ArrayList<>();
for (Part part : parts) {
if (part.getName().equals("file") && part.getSize() > 0) {
String fileName = getSubmittedFileName(part);
String uploadPath = saveFileAsync(part, fileName);
uploadedFiles.add(fileName + " (" + part.getSize() + " bytes)");
}
}
// Send success response
ServletResponse resp = asyncContext.getResponse();
resp.setContentType("application/json;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("{");
writer.write("\"status\":\"success\",");
writer.write("\"filesUploaded\":" + uploadedFiles.size() + ",");
writer.write("\"files\":[");
for (int i = 0; i < uploadedFiles.size(); i++) {
if (i > 0) writer.write(",");
writer.write("\"" + uploadedFiles.get(i) + "\"");
}
writer.write("],");
writer.write("\"timestamp\":\"" + Instant.now() + "\"");
writer.write("}");
writer.flush();
} catch (Exception e) {
try {
ServletResponse resp = asyncContext.getResponse();
if (!resp.isCommitted()) {
resp.setContentType("application/json;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("{");
writer.write("\"status\":\"error\",");
writer.write("\"message\":\"" + e.getMessage() + "\"");
writer.write("}");
writer.flush();
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
} finally {
asyncContext.complete();
}
});
}
private String saveFileAsync(Part part, String fileName) throws IOException {
String uploadDir = getServletContext().getRealPath("/uploads");
Files.createDirectories(Paths.get(uploadDir));
String filePath = uploadDir + File.separator +
System.currentTimeMillis() + "_" + fileName;
// Save file using NIO for better performance
try (InputStream input = part.getInputStream()) {
Files.copy(input, Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING);
}
return filePath;
}
private String getSubmittedFileName(Part part) {
String contentDisposition = part.getHeader("Content-Disposition");
if (contentDisposition != null) {
String[] elements = contentDisposition.split(";");
for (String element : elements) {
if (element.trim().startsWith("filename")) {
return element.substring(element.indexOf('=') + 1)
.trim().replace("\"", "");
}
}
}
return "unknown_" + System.currentTimeMillis();
}
}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.
Install with Tessl CLI
npx tessl i tessl/maven-javax-servlet--javax-servlet-api