Aller au contenu

Exercices - Création d'un client Rest⚓︎

Toujours dans notre application de locations, on peut imaginer rajouter une page Web permettant l'ajout d'un nouveau logement. Pour cela, il faudrait pouvoir saisir son adresse. Lorsque vous remplissez un tel formulaire sur une page Web, il y a souvent des propositions d'adresses qui apparaîssent lorsqu'on commence la saisie. Nous allons mettre en place cela (en partie).

Nous allons donc utiliser un Web Service (externe à notre application) comme source de données. Nous allons donc rajouter une couche DAO, qui ne se connecte pas à une base de données cette fois, mais à ce Web Service.

Exercice 1 - Prise en main de l'API⚓︎

Prise en main de l'API

Pour cela, nous allons utiliser l'API nationale permettant de chercher une adresse en France.

Vous pouvez effectuer quelques tests, avec Postman par exemple, pour la prendre en main.

Elle est disponible ici : https://adresse.data.gouv.fr/api-doc/adresse

Nous allons utiliser l'URI /search avec le verbe HTTP GET et deux paramètres de requête (c'est-à-dire des "query parameters") :

  • q : Permet d'indiquer l'adresse recherchée.
  • limit : Le nombre (maximum) de résultats renvoyés.

Il y a d'autres paramètres possibles, mais ils ne nous sont pas nécessaires ici.

Par exemple, vous pouvez effectuer un test avec https://api-adresse.data.gouv.fr/search/?q=64%20avenue%20jean%20portalis&limit=15, qui renvoie, à l'heure où ceci est écrit, 4 résultats.

Remarque : Comme nous n'utilisons que le verbe HTTP GET, que nous ne modifions ni l'en-tête, ni le corps de la requête, et que le résultat est par défaut envoyé au format JSON, il est possible d'effectuer la requête directement dans un navigateur, sans passer par Postman. Je vous conseille cependant tout de même de l'utiliser, afin de bien prendre l'habitude de l'utiliser.

Exercice 2 - Génération du modèle⚓︎

Génération du modèle

Il nous faut transformer ce résultat JSON en Java Beans. Nous pourrions créer la couche modèle correspondante à la main, puisque le format du résultat est indiqué ici, mais ce serait fastidieux. Il est possible de générer automatiquement ce code.

On peut pour cela par exemple utiliser le site https://www.jsonschema2pojo.org/. Pour cela :

  1. Copier le résultat (le fichier JSON) de la requête dans la partie gauche.
  2. Dans le champ Package, indiquer le package dans lequels les Java Beans (la couche modèle) va être générée. Ici, on peut par exemple indiquer
    fr.univtours.polytech.location.model.address
    
  3. Dans le champ Class name, indiquer le nom de la classe principale. Ici, on peut par exemple indiquer
    WsAddressResult
    
  4. Dans Source type, sélectionner JSON.
  5. Cliquer sur Zip, télécharger le résultat et le placer dans le dossier 📂src/main/java (le dossier contenant les sources Java) du projet Web.

jsonschema2pojo

Ca y est, notre modèle est généré !

Depuis notre application Java, lorsque nous exécuterons le Web Service, nous récupérerons un objet WsAddressResult, contenant lui même une liste de Feature. Cet objet Feature correspond à une adresse.

Il contient lui-même un objet Properties qui permettra d'accéder aux informations sur chacune des adresses renvoyées.

Erreurs dans le code généré

Il y a des erreurs dans le code généré. En effet, l'annotation Generated est utilisée ici. Mais l'import correspondant est

☕ Code Java
import javax.annotation.Generated;
Il s'agit de l'ancienne version de JEE. Celui-ci doit donc être remplacé par celui de la nouvelle version de JEE :
☕ Code Java
import jakarta.annotation.Generated;
Cette modification est à effectuer dans chaque classe comportant cette erreur. Il suffit pour cela de nettoyer les imports, avec le raccourci AltShiftO par défaut.

Exercice 3 - Création du client Rest⚓︎

Création du client Rest

Il nous reste maintenant à appeler ce Web Service, depuis notre application Java et non plus depuis un navigateur ou depuis Postman uniquement. Plus précisément, il nous reste à appeler ce Web Service depuis la couche DAO de notre application Java.

Commençons donc par créer la couche DAO. Elle ne contient ici qu'une seule méthode, qui renvoie une liste d'adresse (c'est-à-dire une liste de Feature), et qui prend en paramètre une chaîne de caractère : l'adresse recherchée.

Voici donc l'interface fr.univtours.polytech.location.dao.AddressDao :

☕ Code Java
public List<Feature> getAddresses(String search);

Et son implémentation, la classe fr.univtours.polytech.location.dao.AddressDaoImpl. Pour cela, nous allons utiliser les classes fournies par JaxRS. Pour rappel, l'URL que nous devons appeler depuis le DAO, que nous allons donc devoir générer ici, ressemble à cela :

https://api-adresse.data.gouv.fr/search/?q=64%20avenue%20jean%20portalis&limit=15

☕ Code Java
private static String URL = "https://api-adresse.data.gouv.fr";

@Override
public List<Feature> getAddresses(String search) {
    // Instanciation du client.
    Client client = ClientBuilder.newClient();

    // On indique l'URL du Web Service.
    WebTarget target = client.target(URL);

    // On indique le "end point" (on aurait aussi pu directement le mettre dans
    // l'URL).
    // C'est également avec cette méthode qu'on pourrait ajouter des "path
    // parameters" si besoin.
    target = target.path("search");
    // On précise (lorsqu'il y en a) les "query parameters".
    target = target.queryParam("q", search);
    target = target.queryParam("limit", 15);

    // On appelle le WS en précisant le type de l'objet renvoyé, ici un
    // WsAddressResult.
    System.out.println(target.getUri());
    WsAddressResult wsResult = target.request(MediaType.APPLICATION_JSON).get(WsAddressResult.class);
    return wsResult.getFeatures();
}
Remarque sur le code

On pourrait écrire tout cela de manière condensée :

☕ Code Java
private static String URI = "https://api-adresse.data.gouv.fr/search";

@Override
public List<Feature> getAddresses(String search) {
    Client client = ClientBuilder.newClient();

    Result result = client.target(URI).queryParam("q", search).queryParam("limit", 15)
            .request(MediaType.APPLICATION_JSON).get(WsAddressResult.class);
    return result.getFeatures();
}

Exercice 4 - Les couches métier et présentation⚓︎

Les couches métier et présentation

Nous allons ajouter une petite couche présentation, pour utiliser ce que nous avons fait :

  1. Commençons par créer le service métier appelant ce DAO. Pour cela, créer l'EJB (c'est-à-dire la couche métier) fr.univtours.polytech.location.business.AddressBusiness, contenant une méthode :

    ☕ Code Java
    public List<Feature> searchAddresses(String search);
    
  2. Créer l'implémentation de cet EJB, qui appelle la couche DAO.

    ⚠️ Ne pas oublier d'utiliser l'annotation @Stateless.

  3. Créer une servlet, qui sera appelée via l'URI /searchAddress, contenant l'implémentation de doGet et de doPost :

    • doGet redirige l'utilisateur vers une JSP addresses.jsp contenant un champ pour saisir une adresse.
    • doPost récupère cette adresse saisie, appelle l'EJB effectuant la recherche, et redirige l'utilisateur vers addresses.jsp qui affiche cette liste.
    ☕ Code Java - La servlet
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // On place une liste vide dans la requête.
        request.setAttribute("ADDRESSES", new ArrayList<Feature>());
        // On redirige vers la JSP.
        request.getRequestDispatcher("addresses.jsp").forward(request, response);
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // On récupère ce que l'utilisateur à saisi.
        String search = request.getParameter("search");
        // On exécute la requête correspondante.
        List<Feature> addresses = this.addressBusiness.searchAddresses(search);
        // On place le résultat de la recherche dans la requête.
        request.setAttribute("ADDRESSES", addresses);
        // On redirige vers la JSP.
        request.getRequestDispatcher("addresses.jsp").forward(request, response);
    }
    
    ☕ La JSP
    <%@ taglib prefix="c" uri="jakarta.tags.core" %>
    <!DOCTYPE html>
    <html>
    <head>
    <title>Rechercher une adresse</title>
    </head>
    <body>
        <form action="searchAddress" method="post">
            <input type="text" name="search"/>
            <input type="submit" value="Rechercher"/>
        </form>
        <table>
            <tr>
                <th>Adresse</th>
                <th>Ville</th>
                <th>Code postal</th>
            </tr>
            <c:forEach var="address" items="${requestScope.ADDRESSES}">
                <tr>
                    <td>
                        ${address.properties.name}
                    </td>
                    <td>
                        ${address.properties.city}
                    </td>
                    <td>
                        ${address.properties.postcode}
                    </td>
                </tr>
            </c:forEach>
        </table>
    </body>
    </html>
    

La page de recherche est maintenant accessible depuis l'URL http://localhost:8080/location/searchAddress.

Exercice 5 - Intégration de cette API dans notre application⚓︎

Intégration de cette API dans notre application

Intégrer cette API dans l'application, c'est-à-dire que sur la page d'accueil (http://localhost:8080/location/), pour chaque location, les coordonnées GPS (latitude et longitude) doivent apparaître.

La recherche de l'adresse se fera en concaténant les champs address et zipCode du bean, en insérant un espace entre les deux. On vérifiera que l'API renvoie au moins un résultat, et si c'est le cas, on récupérera les informations du premier résultat.

Attention, ces données ne doivent pas être stockées en base, mais recalculées à chaque affichage de la liste.

Exercice 6 - Intégration de la météo⚓︎

Intégration de la météo

L'idée est d'afficher la température actuelle (au moment de la recherche) au lieu où se trouve la location.

Il est demandé d'afficher cette température dans les résultats affiché par l'API Web développée dans le TD précédent, puis vous afficherez également cette information dans la page de recherche (http://localhost:8080/location/).

À partir des résultats fournis par l'API Web adresse (latitude et longitude), vous appellerez l'API Openweathermap : https://openweathermap.org/current

Elle permet de récupérer la température en kelvin (⚠️) étant données la longitude et la latitude.

L'API Web Openweathermap

Pour utiliser l'API Openweathermap, il faut une clef.

Pour cela :

  1. Vous devez vous créer un compte ici : https://home.openweathermap.org/users/sign_up.
  2. Vous pouvez ensuite récupérer votre clef ici : https://home.openweathermap.org/api_keys.

Cette clef sera ajoutée en paramètre de requête (query parameter) à chaque requête.

Pour utiliser cette API Web, vous devrez ajouter trois paramètres de requêtes (dans n'importe quel ordre) :

  1. appid : La clef qui a été générée pour vous.
  2. lat : La latitude de l'endroit où on souhaite connaître la température actuelle.
  3. lon : La longitude de l'endroit où on souhaite connaître la température actuelle.
Conseils
  • Une façon de faire peut être d'ajouter un attribut temperature au bean LocationBean.

    ⚠️ Attention, ce champ ne doit pas être stocké en BDD, en effet, sa valeur change à chaque instant.

  • Ce champ pourra être mis à jour à chaque récupération de locations en base (c'est-à-dire lors de la récupération de toutes les locations, ou lors de la récupération d'une location via son id).

  • Il faudra certainement créer des DAO pour appeler les deux API Web utilisées, mais les 3 DAO qui existeront alors pourront être appelés depuis un seul EJB : LocationBusinessImpl.

    En effet, il n'y a pas de raison de créer d'autres classes dans la couche métier. Fonctionnellement, tout ce que nous faisons concerne les locations (et donc LocationBusinessImpl).

  • Attention, si aucune adresse n'était renvoyée par l'API Web adresse, il ne faudrait pas qu'une exception Java (NullPointerException par exemple) ne soit levée.

    Dans ce cas là, la température devrait être à Null.

Il serait préférable que la clef de l'API Web Openweathermap utilisée soit stockée dans un fichier de configuration (et pas dans une classe Java).

Il serait également préférable sur les températures affichées soient arrondies au degré.