javaasynchronousfuturevert.xvertx-httpclient

Vert.x's Future gets null value because handler is fired before the future's complete


I am trying to understand Vert.x framework and I have a task to create HTTP server which will perform simple mathematical computations and a client which will send multiple requests to that server. I need to count the time needed to send all requests and get responses. I managed to create the client, the server and to send requests and retrieve responses, but I have a problem with measuring the time needed to do these operations.

My client verticle has the following start() method:

@Override
    public void start() throws Exception {

        WebClient client = WebClient.create(vertx);

        IntStream.range(0, MathClientApp.REQUEST_NUMBER)
            .forEach(i -> Arrays.stream(Operations.values()).forEach(operation -> {

                client
                    .get(8080, "localhost", operation.getPath())
                    .addQueryParam("numbers", StringUtils.join(numbers, ","))
                    .send(result -> {
                        if (result.succeeded()) {
                            Double mathResult = Double.parseDouble(result.result().bodyAsString());
                            if (mathResult.equals(operation.result(numbers))) {
                                System.out.println("Result: " + result.result().bodyAsString() + " OK!");
                            } else {
                                System.out.println("Result: " + result.result().bodyAsString() + " WRONG!");
                            }
                        } else {
                            System.out.println("Communication failed.");
                        }
                    });
            }));
    }

Operations is an enum with all of the mathematical operations the server can do.

Now, I figured out that I need to set starting time before sending a request to the client and then to set ending time on send() callback. Because operations are asynchronous, the ending time may not be set in the moment of getting it to count difference, so I thought this ending time needs to be Future object. So I added some code:

@Override
    public void start() throws Exception {

        WebClient client = WebClient.create(vertx);

        IntStream.range(0, MathClientApp.REQUEST_NUMBER)
            .forEach(i -> Arrays.stream(Operations.values()).forEach(operation -> {

                Long startTime = System.currentTimeMillis();
                Future<Long> endTime = Future.future(future -> {
                    times.add(future.result() - startTime);
                });

                client
                    .get(8080, "localhost", operation.getPath())
                    .addQueryParam("numbers", StringUtils.join(numbers, ","))
                    .send(result -> {
                        if (result.succeeded()) {
                            endTime.complete(System.currentTimeMillis());
                            Double mathResult = Double.parseDouble(result.result().bodyAsString());
                            if (mathResult.equals(operation.result(numbers))) {
                                System.out.println("Result: " + result.result().bodyAsString() + " OK!");
                            } else {
                                System.out.println("Result: " + result.result().bodyAsString() + " WRONG!");
                            }
                        } else {
                            System.out.println("Communication failed.");
                        }
                    });
            }));
    }

My understanding is the following: before each request I take the current time in miliseconds and create Future for ending time with a handler that will subtract these times when ending time will appear. Then the request is sent and when a response is received ending time is set, so handler method of the Future is called, times are subtracted and saved to a list of all times from all requests.

But I get NullPointerException in the Future's handler method. It's executing before the call to the server so the value is not present yet. I can't understand why and the official documentation for Vert.x doesn't say exactly how to use this Future feature.


Solution

  • You should check if the future was completed inside the handler method. So it would look something like this:

    Future<Long> endTime = Future.future(future -> {
        if(future.succeeded()) {
            times.add(future.result() - startTime);
        }
    });