On a previous blog post, Triber Ivan Junckes Filho published an overview of the MicroProfile Rest Client (Rest Client) standard. In this tutorial, we are going to build on Ivan’s overview to show you, step by step, how you can develop and deploy this type-safe approach to consume an existing JAX-RS microservices with Apache TomEE 8 M1. If you have not read Ivan’s overview, it is suggested you do that first and then come back to this tutorial.
The toolset we are going to use for this tutorial includes Java 8, Apache TomEE 8 M1 and Maven 3.6. The technologies applied during the tutorial includes JAX-RS API and Arquillian for smooth and easy in-container testing.
Build and run the project
If you want to go straight to the action, you can execute the following commands to run the project, keep in mind that the rest client is created and executed only during the maven Test phase.
$ git clone https://github.com/tomitribe/tomee-mp-rest-client-starter-project.git
$ cd tomee-mp-rest-client-starter-project
$ mvn clean install tomee:run
If you want to run just the maven test phase:
$ mvn clean test
Feeling empowered? Awesome, but before going for more coffee, let’s review how this project is structured, the overall functionality of the microservice we are going to consume and finally the creation and usage of the MicroProfile Rest Client.
Project Structure
├── pom.xml
└── src
├── main
│ └── java
│ └── org
│ └── superbiz
│ └── rest
│ ├── ApplicationConfig.java
│ ├── Movie.java
│ ├── MovieBean.java
│ └── MovieResource.java
└── test
├── java
│ └── org
│ └── superbiz
│ └── rest
│ ├── MovieResourceClient.java
│ └── MovieResourceTest.java
└── resources
├── META-INF
│ └── microprofile-config.properties
└── arquillian.xml
The project structure follows the Maven standard conventions. For simplicity purposes we can divide the project structure, and the rest of this tutorial, into three sections:
pom.xml
file defining the project name, package, and version along with the necessary java dependencies need it by the project.src/main/../rest
package containing all the classes need it by the project to expose a JAX-RS based Movie microservice with basic CRUD operations on a movie catalog.src/test/
package containing an arquillian-base test to simplify how to configure and use the MicroProfile Rest Client.
About dependencies
We need to make sure our pom.xml
file has both JavaEE 8 and MicroProfile dependencies. Notice that both dependencies have the scope provided, meaning that the artifact is automatically provided by TomEE and do not need to be packaged with your microservices.
<dependency>
<groupId>org.apache.tomee</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>${version.microprofile}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
About the Movie microservice
The movie microservice is a JAX-RS endpoint with basic CRUD operations on a movie catalog. The following is a list of the available endpoints provided by the MovieResource.java
DELETE /api/catalog/movies/{id} -> void deleteMovie(int)
GET /api/catalog/ -> String status()
GET /api/catalog/movies -> List getListOfMovies()
GET /api/catalog/movies/{id} -> Movie getMovie(int)
POST /api/catalog/movies -> void addMovie(Movie)
PUT /api/catalog/movies -> void updateMovie(Movie)
How to build the Movie microservice is out of the scope of this tutorial but you can study the sample code on GitHub to learn how it works. If you want to know more about JAX-RS and arquillian you can check the blog post, Apache TomEE JAX-RS and Arquillian Starter Project.
Getting down to business with MicroProfile Rest Client
For developing the type-safe rest client, we need the following:
1) A client interface that you can use to make calls to the JAX-RS microservice.
The MovieResourceClient.java
interface located in the src/test/../rest
package is similar to the JAX-RS resource (MovieResource.java
) but take notice of the usage of @RegisterRestClient
and @Path("/test/api/catalog")
annotations. The @RegisterRestClient
declares the interface as a MicroProfile Rest Client Interface and the @Path
defines the base URL for the microservice our client is going to consume.
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;
@RegisterRestClient
@Path("/test/api/catalog")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public interface MovieResourceClient {
@GET
String status();
@POST
@Path("/movies")
void addMovie(Movie newMovie);
@DELETE
@Path("/movies/{id}")
void deleteMovie(@PathParam("id") int id);
@PUT
@Path("/movies")
void updateMovie(Movie updatedMovie);
@GET
@Path("/movies/{id}")
Movie getMovie(@PathParam("id") int id);
@GET
@Path("/movies")
List getListOfMovies();
}
2) A property file that declares the URL protocol and port of the movies microservice.
The microprofile-config.properties
file located in src/test/resources/META-INF
folder provides the URL protocol and port of the movies microservice. This property is going to be used along with the MovieResourceClient.java
interface to build the absolute URL to consume the movie microservice.
org.superbiz.rest.MovieResourceClient/mp-rest/url=http://localhost:8080
This is where the fun of using the MicroProfile Rest Client begins. In the class MovieResourceTest.java
the simplicity involved in the creation and usage of the client can really be appreciated. It’s clear and concise without an excessive amount of code or configuration.
At a high level, here’s how to build and use the client in the MovieResourceTest.java:
Inject the client by merely using the @Inject
and @RestClient
annotations in conjunction with the MovieResourceClient
interface:
...
//Injection point MP Rest Client
@Inject
@RestClient
private MovieResourceClient mpClient;
...
From there you can start consuming the movie microservice with basic Java methods calls.
...
//POST
mpClient.addMovie(new Movie(1, "TomEE MicroProfile Adventures"));
mpClient.addMovie(new Movie(2, "Top 10 Tomee Configuraiton Tips"));
//GET
assertTrue(mpClient.getListOfMovies().size() == 2);
assertTrue(mpClient.getMovie(1).getName().equalsIgnoreCase("TomEE MicroProfile Adventures"));
//DELETE
mpClient.deleteMovie(1);
assertTrue(mpClient.getListOfMovies().size() == 1);
assertTrue(mpClient.getMovie(2).getName().equalsIgnoreCase("Top 10 Tomee Configuraiton Tips"));
//UPDATE
mpClient.updateMovie(new Movie(2, "Top 3 Tomee Configuraiton Tips"));
assertTrue(mpClient.getListOfMovies().size() == 1);
assertTrue(mpClient.getMovie(2).getName().equalsIgnoreCase("Top 3 Tomee Configuraiton Tips"));
...
This is the complete code of MovieResourceTest.java
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.inject.Inject;
import static org.junit.Assert.assertTrue;
@RunWith(Arquillian.class)
public class MovieResourceTest {
//Arquillian deployment information
@Deployment()
public static WebArchive createDeployment() {
final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war")
.addPackage(ApplicationConfig.class.getPackage())
.addAsWebInfResource(new StringAsset(""), "beans.xml")
.addAsResource("META-INF/microprofile-config.properties");
return webArchive;
}
//Injection point MP Rest Client
@Inject
@RestClient
private MovieResourceClient mpClient;
@Test()
public void testServerStatus() {
assertTrue(mpClient.status().equalsIgnoreCase("ok"));
}
@Test
public void tesMovieResource() {
//POST
mpClient.addMovie(new Movie(1, "TomEE MicroProfile Adventures"));
mpClient.addMovie(new Movie(2, "Top 10 Tomee Configuraiton Tips"));
//GET
assertTrue(mpClient.getListOfMovies().size() == 2);
assertTrue(mpClient.getMovie(1).getName().equalsIgnoreCase("TomEE MicroProfile Adventures"));
//DELETE
mpClient.deleteMovie(1);
assertTrue(mpClient.getListOfMovies().size() == 1);
assertTrue(mpClient.getMovie(2).getName().equalsIgnoreCase("Top 10 Tomee Configuraiton Tips"));
//UPDATE
mpClient.updateMovie(new Movie(2, "Top 3 Tomee Configuraiton Tips"));
assertTrue(mpClient.getListOfMovies().size() == 1);
assertTrue(mpClient.getMovie(2).getName().equalsIgnoreCase("Top 3 Tomee Configuraiton Tips"));
}
}
Thank you for reading this tutorial, as you can see the MicroProfile Rest Client proposes a standardized approach to consume REST service via a Type-Safe approach in Java. You can learn more about it in https://microprofile.io/project/eclipse/microprofile-rest-client.
Let’s go now for more rewarding coffee and enjoy coding with Apache TomEE.