Exposing JEE JAX-RS resources under Thorntail with Swagger and Swagger-UI embedded

Part of five-cents demo

This topic is part of the ‘new-client’ system of the Five-cents demo :

Five cents demo general schema - backend - new client

GitHubThe source code is available on GitHub, under /backends/client-brandnew and /framework/utils folder.

 

Project initialization

Our concern here is to make a very simple unique microservice to expose a Client resource, with Thorntail (new name of WildFly-Swarm since the 2.0.0 update in June, 2018).

We are using a “code-first” mechanism, through Java EE JAX-RS annotations, and we will be producing the Swagger contract from the code.

To initiate our project, many possibilities exist, from simple Maven archetypes, to advanced JBoss Forge generators. For our example, we are going to use the WildFfly Swarm online project generator.

In the required fields, let’s put these elements :

  • Group ID : com.fivecents.backends
  • Artifact ID : client-brand-new
  • Dependencies :
    • JAX-RS (REST JEE exposure)
    • JAX-RS with JAXB (for XML and JSON automatic marshalling)
    • Undertow (the Servlet Engine)
    • CDI (JBoss Weld implementation)
    • Swagger (to generate a swagger.json document for our REST Api)
    • Swagger Webapp (to bring an instance of Swagger UI inside our WAR)

WildFly Swarm web generator

If we miss dependencies here, it will be very easy to modify the generated pom.xml to get the desired fractions from the WildFly Swarm bom.

The “Generate Project” button generates our project skeleton, we can now add our REST resource.

Add of a simple REST resource with CRUD operations

Ok, we can now make the interesting part, and write the JAX-RS resource for our service.

Remember Dan North good principle that a microservice should be “software that fits in your head”. Here of course, with a single resource in our microservice system, we won’t have trouble to keep in one mind the functional operations of our microservice.

Here is the skeleton of com.fivecents.backends.clientbrandnew.rest.ClientResource, the full code is available at GitHub source code :

@Path("/client")
public class ClientResource {
   @GET
   @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
   @Path("/")
   public Paginated<ClientListing> searchClients(
         @DefaultValue("id") @QueryParam("order_by") String orderBy,
         @DefaultValue("1") @QueryParam(PaginationConstants.PAGE_QUERY_PARAM) int page,
         @DefaultValue("10") @QueryParam(PaginationConstants.PER_PAGE_QUERY_PARAM) int perPage) {
      (...)
   }

   @GET
   @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
   @Path("{clientId}")
   public Response searchClient(@PathParam("clientId") int clientId) {
      (...)
   }

   @POST
   @Path("/")
   public Response createClient(Client client) {
      (...)
   }

   @DELETE
   @Path("{clientId}")
   public Response removeClient(@PathParam("clientId") int clientId) {
      (...)
   }

   @PUT
   @Path("{clientId}")
   public Response updateClient(@PathParam("clientId") int clientId, Client client) {
      (...)
   }
}

Of course, feel free to ask details about the implementation in the comments part of this article.

A list of clients is initiated by the load of an XML file located in the resources files of the project.

We can run our service either through Maven classloader and Thorntail plugin :

mvn thorntail:run

Or directly through the fat Jar :

mvn clean install
java -jar target/client-brand-new-thorntail.jar

Thorntail startup should display :

INFO [org.jboss.as.server] (main) WFLYSRV0010: Deployed "client-brand-new.war" (runtime-name : "client-brand-new.war")
INFO [org.wildfly.swarm] (main) WFSWARM99999: Thorntail is Ready

At that point, we can make some simple requests to the client resource, for example a POST request to create a new client :

curl -X POST http://localhost:8080/api/v1/client -i -H "Content-Type: text/json" --data "{\"firstname\": \"Isabelle\", \"lastname\": \"PIVOT\", \"legacy-id\": 1009}"

Java EE 7 makes automatic XML and JSON bindings, which is nice, no need to add boilerplate code for this concern. You can notice this line in the REST resource, for the searchClients method :

@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

It indicates that if we don’t give any Accept HTTP header, or an Accept header with ‘application/json’ value, we will get a JSON answer :

curl http://localhost:8080/api/v1/client -H "Accept:application/json"

{"clients":[
   {"id":0,"legacy-id":1001,"lastname":"MARTIN","firstname":"Adele","birthdate":1423954800000},
   {"id":1,"legacy-id":1002,"lastname":"SMITH","firstname":"Lucie","birthdate":1323126000000},
   {"id":2,"legacy-id":1003,"lastname":"CAILLETEAU","firstname":"Laurent","birthdate":231890400000},
   {"id":3,"legacy-id":1004,"lastname":"JORDAN","firstname":"Emmanuel","birthdate":181004400000},
   {"id":4,"legacy-id":1005,"lastname":"PIVOT","firstname":"Lucas","birthdate":589500000000},
   {"id":5,"legacy-id":1006,"lastname":"TORDU","firstname":"Salome","birthdate":257986800000},
   {"id":6,"legacy-id":1007,"lastname":"MOUTON","firstname":"Gerard","birthdate":-688611600000},
   {"id":7,"legacy-id":1008,"lastname":"MARCHAND","firstname":"Bernard","birthdate":-716691600000}
]}

If we ask for an XML format, well no problem, it will be accepted :

curl http://localhost:8080/api/v1/client -H "Accept:application/xml"

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<clients>
   <clients>
      <birthdate>2015-02-15T00:00:00+01:00</birthdate>
      <firstname>Adele</firstname>
      <id>0</id>
      <lastname>MARTIN</lastname>
      <legacy-id>1001</legacy-id>
   </clients>
   <clients>
      <birthdate>2011-12-06T00:00:00+01:00</birthdate>
      <firstname>Lucie</firstname>
      <id>1</id>
      <lastname>SMITH</lastname>
      <legacy-id>1002</legacy-id></clients>
   </clients>
   (...)
</clients>

 

Swagger documentation and UI

We can go a little further in the building of our API, and deliver a Swagger contract for our resource.

The swagger fraction dependency in the pom.xml of our service allows us to use the annotations from the io.swagger.annotations package :

<dependency>
   <groupId>io.thorntail</groupId>
   <artifactId>swagger</artifactId>
</dependency>

Let’s annotate our resource with Swagger information :

@Path("/client")
@Api(value = "/client", description = "Client brand new REST service")
@SwaggerDefinition (
info = @Info (
        title = "Example Service",
        description = "A simple example here",
        version = "1.0.0",
        contact = @Contact (
            name = "Laurent CAILLETEAU",
            email = "lll@gmail.com",
            url = "https://lcailleteau.wordpress.com"
        )
    ),
    host = "localhost",
    basePath = "/api/v1",
    schemes = {SwaggerDefinition.Scheme.HTTP}
)
public class ClientResource {

And our operations, for example with io.swagger.annotations.ApiResponses and io.swagger.annotations.ApiOperation :

@ApiResponses(value = {
   @ApiResponse(code = 204, message = "Client successfully deleted"),
   @ApiResponse(code = 400, message = "Invalid ID supplied"),
   @ApiResponse(code = 404, message = "Client not found") })
public Response removeClient(@PathParam("clientId") int clientId) {

The JSON Swagger document generated can be accessed at http://localhost:8080/api/v1/swagger.json :

Swagger json output

If you remember well, we also import another fraction in the pom.xml :

<dependency>
   <groupId>io.thorntail</groupId>
   <artifactId>swagger-webapp</artifactId>
</dependency>

This automatically adds a swagger-webapp.war inside our microservice, giving access to the nice GUI of Swagger-UI at http://localhost:8080/swagger-ui/. We can feed the input field with the previously generated JSON file (http://localhost:8080/api/v1/swagger.json) to discover the Api, and make some calls :

Swagger-UI of Client Api
Of course, it is still possible to use a classic postman collection, by importing the Swagger file (located at http://localhost:8080/api/v1/swagger.json). The resulting test collection is available inside the /src/test/resources folder of the project :
Postman new client

Now what ?

We have a simple REST resource exposed through Thorntail microservice server. We can improve it a little bit, let’s move on to the next article : Producing some REST HATEOAS navigation elements with JAX-RS 2.0

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: