Make streaming response body while using CompletableFuture

Viewed 215

I have setup a following mechanism which uses an async setup with the help of CompletableFuture. doGET is blocked while waiting for doPOST operation to complete.

enter image description here

Class level hashmap

private final Map<String, HttpRequestResponse> requests = new HashMap<>();

HttpRequestResponse Wrapper class

public class HttpRequestResponse {
private final HttpServletRequest request;
private final CompletableFuture<HttpServletResponse> responseSupplier;

public HttpRequestResponse(HttpServletRequest request, CompletableFuture<HttpServletResponse> responseSupplier) {
    this.request = request;
    this.responseSupplier = responseSupplier;
}

public void supplyResponse(HttpServletResponse response) {
    this.responseSupplier.complete(response); //<-- this will release the .get()
}

//getters
public CompletableFuture<HttpServletResponse> getSupplier() {
    return responseSupplier;
}

}

doGET

public void doGET(Request jettyReq, HttpServletRequest request, HttpServletResponse response) {

    // 1. Read request ID for the request
    String xRequestId = request.getHeader("X-Request-ID");

    // 2. Upon receiving the request, create the instance of wrapper
    //    and put it into the map, then wait for the result
    HttpRequestResponse responseSupplier = new HttpRequestResponse(request, new CompletableFuture<>());
    requests.put(xRequestId, responseSupplier); //add supplier to the map (so that doPOST can retrieve it later)

    // perform other processing & perform request to 3rd party server

    try {
        response = responseSupplier.getSupplier().get(); //<- wait until someone completes the future
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

So here .get() method is a blocking call which is waiting till 3rd party API call made to doPOST is processed completely. As soon as doPOST is done writing a response to the request of doGET, doGET can simply return it.

doPOST

public void  doPOST(Request jettyReq, HttpServletRequest request, HttpServletResponse response)  {
    // upon receiving the response from the 3rd party API on writeGetObjectResponse,
    // I will need to get the future by its id, remove it from the map and complete it:
    String xRequestId = request.getHeader("X-Request-ID");
    HttpRequestResponse responseSupplier = requests.remove(xRequestId); // removes the supplier from the map and returns it to you

    // Build a complete response
    HttpServletResponse getObjectResponse = response;

    // set response headers
    getObjectResponse.setContentType("application/json");
    getObjectResponse.setCharacterEncoding("UTF-8");

    // write payload into response body
    String payloadRequest = null;
    try {
        payloadRequest = getBody(request);
    } catch (IOException exception) {
        exception.printStackTrace();
    }
    PrintWriter out = null;
    try {
        out = getObjectResponse.getWriter();
    } catch (IOException exception) {
        exception.printStackTrace();
    }
    out.print(payloadRequest);
    out.flush();

    // complete the future so that getObject() can access it.
    responseSupplier.getSupplier().complete(getObjectResponse);

    response.setStatus(HttpServletResponse.SC_OK);
}

The response object I am trying to build can have a large amount of data to return. I am in search of a mechanism where I can perform a streaming return of data or response body. How do I solve this issue?

0 Answers
Related