Now that we have create a restful interface in part 1 we can start adding some logic to the API. First, we need some data, we need to know at what time our Flight is leaving. From there we can start calculating the actual time we will need to leave the house.
Schiphol provides a Public flight API that returns the flight information. Registering is easy, simply go to the developer portal and create an account. Calling a rest service from a Java application is simple. However, a rest call is blocking the process will wait for the API to respond as it is a synchronous action. This won’t be a problem when the API is responding in a timely fashion. However, when the API is not responding in a timely fashion the call will block the thread and the Vert.x event loop. Luckily Vert.x provides library with which you can call a rest service in a non-blocking fashion.
<dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> </dependency>
The library contains a class called Web Client. With this client you can call a Rest interface. You first must “create” the web client. This client has been implemented with the event loop of vertx so you actually need to run it with an vertx instance. With this the client is non blocking and it allows you to call Rest APIs without blocking the event loop.
private void callPublicFlight(String flightName, Handler<AsyncResult<HttpResponse>> handler){ WebClient client = WebClient.create(vertx); logger.info(String.format("Calling public flight endpoint at: https://api.schiphol.nl/public-flights/flights with flightname %s", flightName)); HttpRequest request = client .getAbs("https://api.schiphol.nl/public-flights/flights") .addQueryParam("app_id", APP_ID) .addQueryParam("app_key", APP_KEY) .addQueryParam("flightname", flightName) .addQueryParam("flightdirection", "D") .putHeader("Accept", "application/json") .putHeader("ResourceVersion", "v3") .as(BodyCodec.json(FlightList.class)); request.send(handler); }
As you can see calling a Rest API is easy. Now we invoke the method from the handler that we created in the first blog post. To keep the code clean I created a method that instantiates the handler. This handler handles the incoming rest call. It contains the logic that eventually results in the response message. Now in this case we have to retrieve the Flight Data of the incoming flightName. This can be done by using the above defined method.
As you can see the BodyCodec allows you to unmarshal the response to a Java Object. In this case I used to swagger documentation provided by Schiphol to generate the FlightList class.
Because Vert.x is non-blocking, the result is handled in asynchronously in the Handler object that is being returned. You don’t know if the call was successful, so you must verify it before you do anything else. If the result was successful you can start processing it.
private Handler callPublicFlightAPI() { return routingContext -> { callPublicFlight(routingContext.request().getParam("id"), handler -> { if (handler.succeeded()){ routingContext.response().end(handler.result().body().toString()); } else { routingContext.response().end(handler.cause().toString()); } }); }; }
With the public flight you know at what time your plain departs. So now we need to know at what time you need to be at Schiphol. If you google a bit you will find out that Schiphol suggest that you arrive at the airport 3 hours in advanced for international flights and for EU flights they suggest 2 hours. Determining if the flight is EU is a bit tricky, so I will leave it out for now.
Now that we know at what time you need to be at Schiphol we can determine the time we need to leave by car or train using the Google API. As the API requires a credit card when you want the train information I will leave it out for now. The nice thing about this API is that it also takes into account the expected traffic delays. So, if you specify the location and the destination with a given time you want to arrive the API will tell you how long it is going to take (Source of my wisdom).
The API call to google is like the public flight so I will not show it here. The logic that makes the actual call is to be executed after the successful call to the public flight API. So, I added the logic to that part of the code.
callPublicFlight(flightId, handler -> { if (handler.succeeded()){ ResponseModel response = createResponseModel(handler); getTravelTimeByCar(origin, response.getTimeToBeAtSchiphol().toEpochSecond(ZoneOffset.UTC), httpResponseAsyncResult -> { if (httpResponseAsyncResult.succeeded()){ try { addTimeToLeave(response, httpResponseAsyncResult); routingContext.response() .putHeader("Content-Type", "application/json") .end(mapper.writeValueAsString(response)); } catch (JsonProcessingException e) { e.printStackTrace(); } } else { routingContext.response().end(httpResponseAsyncResult.cause().toString()); } }); } else { routingContext.response().end(handler.cause().toString()); } });
Now as you can see we have to do the second call in the call-back handler of the first call. That is because that is the code that is responsible for handling the response. Imagine you must do not two but four or more sequential calls. You will get a call-back in a call-back in a call-back in a call-back, in other words you have arrived in something called the “call-back hell”.
Now if this was the extend of what you could with do Vert.x I would not have liked it so much. However luckily Vertx supports the RxJava library. RxJava can be used to replace the call-backs with Observables. Now you might say we are just replacing the call-backs with something new. But in the next blog post I will show you why I think it is great and how easy it is to implement using Vert.x.