aCarella
aCarella

Reputation: 2578

How to use futures in Vertx routers with WebClient in Java

I have a Vertx application with a router endpoint:

router.route(HttpMethod.GET, Constants.ENDPOINT).blockingHandler(this::getItems);

This router calls a method, that is supposed to return a JSON object in the browser, or whatever client is calling this endpoint. The JSON object actually comes from a completely different service. I am using Vert.x's WebClient library to call this service.

    private void getItems(RoutingContext routingContext) {
    HttpServerResponse response = routingContext.response();
    response.setChunked(true);
    response.putHeader("content-type", "text/plain");
    response.putHeader("Access-Control-Allow-Origin", "*");
    JsonObject data = new JsonObject();
    WebClient webClient = WebClient.create(vertx);
    webClient.post(80, "my-site.com", "/api/items")
        .as(BodyCodec.jsonArray())
        .putHeader("Accept", "application/json")
        .putHeader("Content-Type", "application/json")
        .sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
            if (ar.succeeded()) {
                HttpResponse<JsonArray> result = ar.result();
                JsonArray body = result.body();
                System.out.println(body);
                data.put("data", body.getJsonObject(0));
            } else {
                data.put("data", ar.cause().getMessage());
            }
        }); 
    response.write(data.encode());
    routingContext.response().end();
}

The data I get from my-site.com is fine and displays in the console with my System.out command. The problem is that I cannot get it into response.write.

Reading up, I see that this is related to futures. I don't really understand the concept, so I've been doing a lot of reading but cannot find any examples that fit my particular code.

How would I go about implementing futures so that the data I receive from my-site.com gets put into my Json object (data), and then can be used in response.write?

Upvotes: 1

Views: 2015

Answers (2)

Mohamed Ibrahim Elsayed
Mohamed Ibrahim Elsayed

Reputation: 2964

The Vert.x documentation about async coordination is very good and uses futures in the examples. Here is the way I would implement it using Vert.x futures:

private void getItems(RoutingContext routingContext) {
        HttpServerResponse response = routingContext.response();
        response.setChunked(true);
        response.putHeader("content-type", "text/plain");
        response.putHeader("Access-Control-Allow-Origin", "*");
        // init a future that should hold a JsonObject result
        Future<JsonObject> future = Future.future();
        JsonObject data = new JsonObject();
        WebClient webClient = WebClient.create(vertx);
        webClient.post(80, "my-site.com", "/api/items")
                .as(BodyCodec.jsonArray())
                .putHeader("Accept", "application/json")
                .putHeader("Content-Type", "application/json")
                .sendJsonObject(new JsonObject().put("mutator", "*"), ar -> {
                    if (ar.succeeded()) {
                        HttpResponse<JsonArray> result = ar.result();
                        JsonArray body = result.body();
                        System.out.println(body);
                        data.put("data", body.getJsonObject(0));
                        // set future to be completed, with data object as its JsonObject result
                        future.complete(data);
                    } else {
                         data.put("data", ar.cause().getMessage());
                         future.complete(data);
                         // we can also set the future as failed and give it a Throwable
                        // future.fail(ar.cause());
                    }
                });
        // handle when the future is completed
        future.setHandler(jsonObjectAsyncResult -> {
            if(jsonObjectAsyncResult.succeeded()) {
                response.write(data.encode());
                routingContext.response().end();
            }
        });
    }

Upvotes: 1

Anders Mikkelsen
Anders Mikkelsen

Reputation: 79

In your impl data will be an empty JSON object because Webclient is async. You are writing the response to the client before the response from Webclient is ready.

Move the write into the webclient response and end the context there. E.g:

...

if (ar.succeeded()) { 
    HttpResponse<JsonArray> result = ar.result();
    JsonArray body = result.body();
    System.out.println(body); 
    data.put("data", body.getJsonObject(0));
} else { 
    data.put("data", ar.cause().getMessage());
}

response.write(data.encode());
routingContext.response().end();

...

Upvotes: 2

Related Questions