Objectif

Réaliser à l’aide l’architecture JEE, une application web avec des services web Rest

Les prérequis…

 

Un service web REST avec JERSEY…

Un service web Rest est un service accessible via http. Il utilise à la fois

  • l’URI (chemin vers la ressource demandée) et
  • une méthode http aux choix (GET, POST, PUT, DELETE).

Chaque service doit être conçu via les différentes URIs et méthodes.

Le framework JERSEY est une implémentation candidate pour la gestion des services rest via JAX-RS (API JAVA). Plusieurs autres existent sur le marché : CXF (Apache), RestEasy (JBoss), RESTlet, etc. Il faut donc l’ajouter à la liste de vos bibliothèques utilisées via MAVEN.

On ajoute dans le pom.xml du projet :

   <dependencies>     
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-common</artifactId>
            <version>2.9</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.13</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>2.13</version>
        </dependency>
   </dependencies>

JERSEY permet d’appeler le bon service lors de chaque requête http et ce via une servlet référencée dans le web.xml. En effet cette servlet utilise les différents paramètres (init-param) pour repérer la classe du contexte applicatif (Application) ou les packages encapsulant des services.

On complète le web.xml de l’application web comme suit :

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <servlet>
        <servlet-name>rest</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.hautil.exemple</param-value>
        </init-param>

    </servlet>
    
    <servlet-mapping>
        <servlet-name>rest</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

</web-app>

De plus, chaque projet Rest prévoit une classe Application pour rappeler le context et y ajouter le cas échéant des informations importantes relatives aux différents services.   Cette classe, même vide, permet d’initialiser l’application et d’éviter des problèmes de redirection vers les différents services surtout quand il y a transfert de données (JSON ou XML).

Voici un exemple de la classe application

package com.hautil.exemple;


import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class MyApplication extends Application {

}

L’application HelloWorld

On initialisera notre premier projet Rest avec les indications précisées ci-dessus.  On ajoutera notre première classe HelloService qui encapsulera deux services différents :

  • une URI (/hello) avec la méthode GET et
  • une URI (/hello/{name}) avec la méthode GET

Voici la Classe HelloService :

package com.hautil.exemple;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloWorld {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getMessage() {
        return "Hello world!";
    }

    @GET
    @Path("/{nom}")
    @Produces(MediaType.TEXT_PLAIN)
    public String getMessagePerso(@PathParam("nom") String nom) {
        return "Hello "+nom;
    }
}

Un client pour tester (SoapUI)…

Pour tester les requêtes GET, il suffit de saisir l’URL sur un navigateur.  En effet, pour tester chacun de nos nouveaux services, on saisira les URLs suivantes :

  • http://<serveur>:<port>/<nomApp>/rest/hello  pour le premier service
  • http://<serveur>:<port>/<nomApp>/rest/hello/<nom> pour le deuxième service

Ceci étant dit, pour tester les différents nouveaux services Rest, on peut installer l’utilitaire SoapUI (version 5.3.0) et ainsi simuler plus facilement les autres méthodes HTTP.

 

Un service web REST qui produit du JSON avec JACKSON…

Dans le cadre de cette partie, nous allons créer une autre classe de services.  Elle pourra à terme envoyer et recevoir des données JSON.

En effet, pour le transport de données, plusieurs formats existent : XML, HTML, JSON, PLAIN TEXT, etc. Dans le cadre de ce tutoriel, on utilisera JSON (structuré et léger). Le framework Jackson est utilisé pour transformer des objets en JSON et inversement. On demandera à MAVEN de nous l’importer dans le projet :

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.6.3</version>
		</dependency>

Voici maintenant une nouvelle classe UserService qui contiendra les différents nouveaux services. Elle utilise un tableau de users pour simuler une source de données (cette classe n’est pas encore fonctionnelle !!!) :

package com.hautil.exemple;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hautil.bean.User;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.ArrayList;

@Path("/users")
public class UserService {
    ArrayList<User> users = new ArrayList<User>();

    public UserService(){
        users.add(new User("ZA", 30));
        users.add(new User("FLM", 35));
        users.add(new User("LF", 20));
    }

...
}

Voici la classe User utilisée:

package com.hautil.bean;


public class User {
    String nom;
    int age;

    public User() {
    }

    public User(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }

    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Dans la classe UserService, nous pourrons ajouter plusieurs types de services.  Ce tutoriel en proposera plusieurs qui peuvent ne pas cohabiter entre elles. Quelques requêtes étant similaires, elle peuvent provoquer des ambiguïtés pour JERSEY.

Voici, donc, quelques exemples de services :

1- URI : /users et la méthode : GET (cette ressource produit une liste de users avec JSON)

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String getUsers() {
        //Serialiser d'objets en JSON
        ObjectMapper mapper = new ObjectMapper();
        String jsonInString = null;
        try {
            jsonInString = mapper.writeValueAsString(users);
        } catch (Exception e) {
            return e.getMessage();
        }
        return jsonInString;
    }

2- URI : /users/{nom} et la méthode : GET (cette ressource produit un objet user selon le nom avec JSON)

    @GET
    @Path("/{nom}")
    @Produces(MediaType.APPLICATION_JSON)
    public String getUser(@PathParam("nom") String nom) {
        User res=null;
        ObjectMapper mapper = new ObjectMapper();

        for(User u : users)
        {
            if(u.getNom().equalsIgnoreCase(nom)) res=u;
        }
        String jsonInString = null;
        try {
            jsonInString = mapper.writeValueAsString(res);
        } catch (Exception e) {
            return e.getMessage();
        }
        return jsonInString;
    }

 

Attention la requête suivante ne peut cohabiter avec la première requête décrite (elles se ressemblent trop entre elles !!!)

3- URI : /users ?nom=xxx avec un paramètre de requête (nom) et la méthode : GET (cette ressource produit un objet user selon le nom avec JSON)

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String getUser(@QueryParam("nom") String name) {
        User res=null;
        ObjectMapper mapper = new ObjectMapper();

        for(User u : users)
        {
            if(u.getNom().equalsIgnoreCase(name)) res=u;
        }
        //Object to JSON in String
        String jsonInString = null;
        try {
            jsonInString = mapper.writeValueAsString(res);
        } catch (Exception e) {
            return e.getMessage();
        }
        return jsonInString;
    }

 

4- URI : /users via un formulaire à deux entrées (nom et age) et la méthode : POST (cette ressource ajoute un user à la liste et renvoie le tout avec JSON)

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String addUser(@FormParam("nom") String nom, @FormParam("age") int age) {
        User res=null;
        ObjectMapper mapper = new ObjectMapper();

        User u = new User(nom, age);
        users.add(u);
        String jsonInString = null;
        try {
            jsonInString = mapper.writeValueAsString(users);
        } catch (Exception e) {
            return e.getMessage();
        }
        return jsonInString;
    }

 

5- URI : /users/{nom} et la méthode : DELETE (cette ressource supprime un user de la liste et renvoie la nouvelle liste avec JSON)

    @DELETE
    @Path("/{nom}")
    @Produces(MediaType.APPLICATION_JSON)
    public String suppUser(@PathParam("nom") String nom) {
        User res=null;
        ObjectMapper mapper = new ObjectMapper();
        for(User u : users)
        {
            if(u.getNom().equalsIgnoreCase(nom)) res=u;
        }

        users.remove(res);
        String jsonInString = null;
        try {
            jsonInString = mapper.writeValueAsString(users);
        } catch (Exception e) {
            return e.getMessage();
        }
        return jsonInString;
    }


Un service web REST qui consomme du JSON avec JACKSON et JERSEY…

Pour consommer du JSON, il faut ajouter quelques dépendances supplémentaires pour permettre au framework JERSEY et JACKSON de transformer le texte JSON en Objet Java.

        <dependency>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
            <version>2.5.1</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
            <version>2.9</version>
        </dependency>

Voici un dernier exemple de service qui utilise du JSON en entrée :

URI : /users avec un objet user en JSON et la méthode : PUT (cette ressource ajoute un user à la liste et renvoie la nouvelle liste avec JSON)

    @PUT
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public String addUser(User user) {
        ObjectMapper mapper = new ObjectMapper();
        String jsonInString = null;
        users.add(user);
        try {
            jsonInString = mapper.writeValueAsString(users);
        } catch (Exception e) {
            return e.getMessage();
        }
        return jsonInString;
    }

Vous pouvez maintenant tester tous vos services ou presque et compléter cet exemple en intégrant une connexion à la base de données pour plus d’interactivité !!