The great thing about the MicroProfile REST Client is that it makes it really easy to invoke remote APIs of other services. As developer you don't have to worry about serialization/deserialization/etc. All you need to do is to define interfaces and some configuration.
In order to map HTTP response codes to Java exceptions, a ResponseExceptionMapper is used. Let's take a look.
Next an interface of the service that is supposed to be invoked is defined. The implementation of this interface is provided magically by MicroProfile.
Create the class ArticlesService.java. To keep this as simple as possible, there is only one method to read a list of articles.
Note that the annotations @Get and @Produces can be confusing. These are the JAX-RS annotations you used in the previous exercise. This time however they are not used to expose REST APIs, but to define how to invoke remote APIs.
Also note that the service does not return a Response object directly. Instead it returns a CompletionStage object with a Response object as described earlier. With the MicroProfile Rest Client you can invoke services both synchronously as well as asynchronously.
packageorg.acme.rest.json;importorg.eclipse.microprofile.rest.client.annotation.RegisterProvider;importjavax.ws.rs.*;importjavax.ws.rs.core.MediaType;importjava.util.List;importjava.util.concurrent.CompletionStage;@RegisterProvider(ExceptionMapperArticles.class)publicinterfaceArticlesService { @GET @Produces(MediaType.APPLICATION_JSON)CompletionStage<List<Article>> getArticlesFromService(@QueryParam("amount") int amount);}
Exit the Editor via 'Ctrl-X', 'y' and 'Enter'.
Step 4: Create the Code to invoke Services
Now let's write the code to invoke the 'Articles' service. Basically all you need to do is to define the URL of the endpoint and invoke a Java method. Check out the code below, especially the invocation of the service via 'articlesService.getArticlesFromService(amount)'.
packageorg.acme.rest.json;importorg.eclipse.microprofile.config.inject.ConfigProperty;importorg.eclipse.microprofile.rest.client.RestClientBuilder;importjavax.annotation.PostConstruct;importjavax.enterprise.context.ApplicationScoped;importjavax.ws.rs.core.UriBuilder;importjava.net.URI;importjava.util.List;importjava.util.concurrent.CompletionStage;importjava.util.concurrent.TimeUnit;@ApplicationScopedpublicclassArticlesDataAccess {privatestaticfinalint MAXIMAL_DURATION =5000;// this configuration needs to be used when running this web-api service locally// run the following command to get this URL: os4scripts/show-urls.shprivatestaticString urlArticlesServiceOpenShift ="http://articles-reactive-cloud-native-starter.niklas-heidloff-os-fra-162e406f043e20da9b0ef0731954a894-0000.eu-de.containers.appdomain.cloud/v2/articles?amount=10";privateArticlesService articlesService; @PostConstructvoidinitialize() {URI apiUrl =UriBuilder.fromUri(urlArticlesServiceOpenShift).build(); articlesService =RestClientBuilder.newBuilder().baseUri(apiUrl).register(ExceptionMapperArticles.class).build(ArticlesService.class); }publicCompletionStage<List<Article>> getArticlesReactive(int amount) {returnarticlesService.getArticlesFromService(amount); }}
Open a second terminal session and run the following command to get the URL of your 'Articles' service.
Copy and paste the URL in the class ArticlesDataAccess you have open in your editor. Replace the value for the variable 'urlArticlesServiceOpenShift' with your copied value.
In the last step you need to modify ArticleResource.java from the previous exercise to invoke the actual service rather than returning a sample article.
Open a second terminal in the Cloud Shell and invoke the following command.
curlhttp://localhost:8080/articles
You should see the following response.
Step 7: Understand Timeouts
When writing asynchronous code it's important to consider timeouts, especially when you invoke third party services like databases or other microservices.
Fortunately starting with Java 9 this is easy to handle. When invoking the 'Articles' service via MicroProfile, you can use the method 'orTimeout'. If it comes to a timeout, an exception is thrown which you can handled via 'exceptionally' as explained in the last exercise.
The method 'orTimeout' doesn't exist in the CompletionStage interface. You need to run 'toCompletableFuture' first to get an instance of CompletableFuture.
Unfortunately this capability is only available in Java 9+. Since the current version of the Cloud Shell supports only Java 8, we cannot run it here. But you can obviously run it locally or in a container on OpenShift.