Part of five-cents demo
This topic is part of the ‘new-client’ system of the Five-cents demo :
The 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)
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 :
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 :


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