Long polling is a method that server applications use to hold a client connection until information becomes available. This is often used when a server must call a downstream service to get information and await a result.

zx8n0upc.bmp

Untitled

We can use *DeferredResult in Spring MVC as a way to handle inbound HTTP requests asynchronously.* It allows the HTTP worker thread to be freed up to handle other incoming requests and offloads the work to another worker thread. As such, it helps with service availability for requests that require long computations or arbitrary wait times.

DeferredResult

First, we’ll covers the capabilities and use cases of DeferredResult.

Let's start with developing a standard blocking REST service:

@GetMapping("/process-blocking")
public ResponseEntity<?> handleReqSync(Model model) {
    // ...
    return ResponseEntity.ok("ok");
}

The problem here is that the request processing thread is blocked until the complete request is processed and the result is returned. In case of long-running computations, this is a sub-optimal solution.

To address this, we can make better use of container threads to handle client requests as we'll see in the next section.

To avoid blocking, we'll use callbacks-based programming model where instead of the actual result, we'll return a DeferredResult to the servlet container.

@GetMapping("/async-deferredresult")
public DeferredResult<ResponseEntity<?>> handleReqDefResult(Model model) {
    LOG.info("Received async-deferredresult request");
    DeferredResult<ResponseEntity<?>> output = new DeferredResult<>();

    ForkJoinPool.commonPool().submit(() -> {
        LOG.info("Processing in separate thread");
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
        }
        output.setResult(ResponseEntity.ok("ok"));
    });

    LOG.info("servlet thread freed");
    return output;
}

Request processing is done in a separate thread and once completed we invoke the setResult operation on the DeferredResult object.

Let's look at the log output to check that our threads behave as expected:

[nio-8080-exec-6] com.baeldung.controller.AsyncDeferredResultController:
Received async-deferredresult request
[nio-8080-exec-6] com.baeldung.controller.AsyncDeferredResultController:
Servlet thread freed
[nio-8080-exec-6] java.lang.Thread : Processing in separate thread

Internally, the container thread is notified and the HTTP response is delivered to the client. The connection will remain open by the container(servlet 3.0 or later) until the response arrives or times out.

DeferredResult Callbacks

We can register 3 types of callbacks with a DeferredResult: completion, timeout and error callbacks.

Let's use the onCompletion() method to define a block of code that's executed when an async request completes:

deferredResult.onCompletion(() -> LOG.info("Processing complete"));