Création de microservices avec Eclipse MicroProfile

1. Vue d'ensemble

Dans cet article, nous allons nous concentrer sur la création d'un microservice basé sur Eclipse MicroProfile.

Nous verrons comment écrire une application Web RESTful à l'aide des API JAX-RS, CDI et JSON-P.

2. Une architecture de microservices

En termes simples, les microservices sont un style d'architecture logicielle qui forme un système complet sous la forme d'un ensemble de plusieurs services indépendants.

Chacun se concentre sur un périmètre fonctionnel et communique avec les autres avec un protocole indépendant du langage, tel que REST.

3. Eclipse MicroProfile

Eclipse MicroProfile est une initiative qui vise à optimiser Enterprise Java pour l'architecture des microservices. Il est basé sur un sous-ensemble d'API Jakarta EE WebProfile, ce qui nous permet de créer des applications MicroProfile comme celles de Jakarta EE.

L'objectif de MicroProfile est de définir des API standard pour créer des microservices et fournir des applications portables sur plusieurs environnements d'exécution MicroProfile.

4. Dépendances de Maven

Toutes les dépendances requises pour créer une application Eclipse MicroProfile sont fournies par cette dépendance de nomenclature (nomenclature):

 org.eclipse.microprofile microprofile 1.2 pom provided  

La portée est définie comme fournie car le runtime MicroProfile inclut déjà l'API et l'implémentation.

5. Modèle de représentation

Commençons par créer une classe de ressources rapide:

public class Book { private String id; private String name; private String author; private Integer pages; // ... }

Comme nous pouvons le voir, il n'y a pas d'annotation sur cette classe Book .

6. Utilisation de CDI

En termes simples, CDI est une API qui fournit une injection de dépendances et une gestion du cycle de vie. Il simplifie l'utilisation des beans entreprise dans les applications Web.

Créons maintenant un bean géré par CDI comme magasin pour la représentation du livre:

@ApplicationScoped public class BookManager { private ConcurrentMap inMemoryStore = new ConcurrentHashMap(); public String add(Book book) { // ... } public Book get(String id) { // ... } public List getAll() { // ... } } 

Nous annotons cette classe avec @ApplicationScoped car nous n'avons besoin que d'une seule instance dont l'état est partagé par tous les clients. Pour cela, nous avons utilisé un ConcurrentMap comme magasin de données en mémoire de type sécurisé. Ensuite, nous avons ajouté des méthodes pour les opérations CRUD .

Maintenant, notre bean est un CDI prêt et peut être injecté dans le bean BookEndpoint en utilisant l' annotation @Inject .

7. API JAX-RS

Pour créer une application REST avec JAX-RS, nous devons créer une classe Application annotée avec @ApplicationPath et une ressource annotée avec @Path.

7.1. Application JAX RS

L'application JAX-RS identifie l'URI de base sous lequel nous exposons la ressource dans une application Web.

Créons l'application JAX-RS suivante:

@ApplicationPath("/library") public class LibraryApplication extends Application { }

Dans cet exemple, toutes les classes de ressources JAX-RS de l'application Web sont associées à LibraryApplication en les créant sous le même chemin de bibliothèque , c'est la valeur de l' annotation ApplicationPath.

Cette classe annotée indique à l'environnement d'exécution JAX RS qu'il doit trouver automatiquement les ressources et les expose.

7.2. Point de terminaison JAX RS

Une classe Endpoint , également appelée classe Resource , doit définir une ressource bien que plusieurs des mêmes types soient techniquement possibles.

Chaque classe Java annotée avec @Path , ou ayant au moins une méthode annotée avec @Path ou @HttpMethod est un point de terminaison.

Maintenant, nous allons créer un point de terminaison JAX-RS qui expose cette représentation:

@Path("books") @RequestScoped public class BookEndpoint { @Inject private BookManager bookManager; @GET @Path("{id}") @Produces(MediaType.APPLICATION_JSON) public Response getBook(@PathParam("id") String id) { return Response.ok(bookManager.get(id)).build(); } @GET @Produces(MediaType.APPLICATION_JSON) public Response getAllBooks() { return Response.ok(bookManager.getAll()).build(); } @POST @Consumes(MediaType.APPLICATION_JSON) public Response add(Book book) { String bookId = bookManager.add(book); return Response.created( UriBuilder.fromResource(this.getClass()) .path(bookId).build()) .build(); } } 

À ce stade, nous pouvons accéder à la ressource BookEndpoint sous le chemin / library / books dans l'application Web.

7.3. Type de support JAX RS JSON

JAX RS prend en charge de nombreux types de supports pour communiquer avec les clients REST, mais Eclipse MicroProfile limite l'utilisation de JSON car il spécifie l'utilisation de l'API JSOP-P. En tant que tel, nous devons annoter nos méthodes avec @Consumes (MediaType.APPLICATION_JSON) et @ Produces (MediaType.APPLICATION_JSON).

L' annotation @Consumes limite les formats acceptés - dans cet exemple, seul le format de données JSON est accepté. L'en-tête de la requête HTTP Content-Type doit être application / json .

La même idée se cache derrière l' annotation @Produces . Le JAX RS Runtime doit rassembler la réponse au format JSON. L'en-tête HTTP de la requête Accept doit être application / json.

8. JSON-P

JAX RS Runtime prend en charge JSON-P prêt à l' emploi afin que nous puissions utiliser JsonObject comme paramètre d'entrée de méthode ou type de retour.

Mais dans le monde réel, nous travaillons souvent avec des classes POJO. Nous avons donc besoin d'un moyen de faire le mappage entre JsonObject et POJO. Voici où le fournisseur d'entités JAX RS va jouer.

Pour marshaler le flux d'entrée JSON vers le POJO Book , qui appelle une méthode de ressource avec un paramètre de type Book, nous devons créer une classe BookMessageBodyReader:

@Provider @Consumes(MediaType.APPLICATION_JSON) public class BookMessageBodyReader implements MessageBodyReader { @Override public boolean isReadable( Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return type.equals(Book.class); } @Override public Book readFrom( Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { return BookMapper.map(entityStream); } } 

Nous procédons de la même manière pour démarseler un flux de sortie Book to JSON, qui appelle une méthode de ressource dont le type de retour est Book, en créant un BookMessageBodyWriter:

@Provider @Produces(MediaType.APPLICATION_JSON) public class BookMessageBodyWriter implements MessageBodyWriter { @Override public boolean isWriteable( Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { return type.equals(Book.class); } // ... @Override public void writeTo( Book book, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { JsonWriter jsonWriter = Json.createWriter(entityStream); JsonObject jsonObject = BookMapper.map(book); jsonWriter.writeObject(jsonObject); jsonWriter.close(); } } 

Comme BookMessageBodyReader et BookMessageBodyWriter sont annotés avec @Provider , ils sont automatiquement enregistrés par le runtime JAX RS.

9. Création et exécution de l'application

Une application MicroProfile est portable et doit s'exécuter dans n'importe quel environnement d'exécution MicroProfile conforme. Nous expliquerons comment créer et exécuter notre application dans Open Liberty, mais nous pouvons utiliser n'importe quel MicroProfile Eclipse conforme.

Nous configurons le runtime Open Liberty via un fichier de configuration server.xml :

  jaxrs-2.0 cdi-1.2 jsonp-1.0     

Ajoutons le plugin liberty-maven-plugin à notre pom.xml:

  net.wasdev.wlp.maven.plugins liberty-maven-plugin 2.1.2   io.openliberty openliberty-runtime 17.0.0.4 zip  ${basedir}/src/main/liberty/config/server.xml ${package.file} ${packaging.type} false project  / ${project.artifactId}-${project.version}.war 9080 9443     install-server prepare-package  install-server create-server install-feature    package-server-with-apps package  install-apps package-server    

Ce plugin est configurable avec un ensemble de propriétés:

  library ${project.build.directory}/${app.name}-service.jar runnable 

The exec goal above produces an executable jar file so that our application will be an independent microservice which can be deployed and run in isolation. We can also deploy it as Docker image.

To create an executable jar, run the following command:

mvn package 

And to run our microservice, we use this command:

java -jar target/library-service.jar

This will start the Open Liberty runtime and deploy our service. We can access to our Endpoint and getting all books at this URL:

curl //localhost:9080/library/books

The result is a JSON:

[ { "id": "0001-201802", "isbn": "1", "name": "Building Microservice With Eclipse MicroProfile", "author": "baeldung", "pages": 420 } ] 

To get a single book, we request this URL:

curl //localhost:9080/library/books/0001-201802

And the result is JSON:

{ "id": "0001-201802", "isbn": "1", "name": "Building Microservice With Eclipse MicroProfile", "author": "baeldung", "pages": 420 }

Now we'll add a new Book by interacting with the API:

curl -H "Content-Type: application/json" -X POST -d '{"isbn": "22", "name": "Gradle in Action","author": "baeldung","pages": 420}' //localhost:9080/library/books 

As we can see, the status of the response is 201, indicating that the book was successfully created, and the Location is the URI by which we can access it:

< HTTP/1.1 201 Created < Location: //localhost:9080/library/books/0009-201802

10. Conclusion

Cet article a montré comment créer un microservice simple basé sur Eclipse MicroProfile, en discutant de JAX RS, JSON-P et CDI.

Le code est disponible sur Github; il s'agit d'un projet basé sur Maven, il devrait donc être simple à importer et à exécuter tel quel.