TP3 - Partie 1 - Une bibliothèque - Mobilité des canaux
Table of Contents
1 Introduction - Ressources comme liens, liens comme ressources
On s'intéresse aux hyperliens et à leur utilisation pour contrôler l'état d'une application, suivant le principe HATEOAS ("Hypermedia as the Engine of Application State"). Un hyperlien correspond à un canal de communication. Le fait de pouvoir transmettre des hyperliens garantit alors la mobilité des canaux : elle permet de faire évoluer la topologie des connexions entre les agents et ainsi de produire un contrôle réparti du flot d'exécution d'une application.
Exemple typique : la découverte de services
- Agents
- Un serveur rendant service sur un canal
- Un client cherchant un service
- Un annuaire regroupant des canaux associés à des services
- Protocole
- Le serveur envoie un canal fournissant un service à l'annuaire, qui l'enregistre dans sa base.
- Le client demande à l'annuaire un canal pour un service.
- L'annuaire envoie au client un canal fournissant le service.
- Le client utilise le canal pour obtenir le service.
On développe ci-dessous un exemple simple d'une bibliothèque pour illustrer l'intérêt de la mobilité des canaux. On commence par ajouter des livres à une bibliothèque; pour chaque livre ajouté, on reçoit un hyperlien pointant vers lui. Cet hyperlien peut ensuite être diffusé.
2 Bibliothèque : les règles chimiques
2.1 Serveur bibliothèque
Canaux (décrits par une méthode HTTP et un chemin, accompagnés de leurs arguments) :
- POST@bibliotheque(livre, canalDeRetour) : ajout d'un livre à la bibliothèque
- GET@bibliotheque/id(canalDeRetour) : accès à la sous-ressource livre d'identifiant id
- GET@bibliotheque/id/titre(canalDeRetour) : titre du livre d'identifiant id
- GET@bibliotheque(livre, canalDeRetour) : recherche du livre livre renvoyant un canal pointant vers le livre ou une erreur
- GET@bibliotheque/catalogue(canalDeRetour) : catalogue de la bibliothèque défini par une liste de canaux pointant vers les livres
Etat du serveur :
- Compteur(id) : compteur servant à identifier les livres
- Catalogue(liste) : liste des canaux pointant vers les livres
- Base(id, livre) : base de données contenant des couples (identifiant, livre)
On décrit un livre par un n-uplet (titre, …). On note :: le constructeur infixe de liste : (element :: liste).
2.2 Ajout d'un livre
POST@bibliotheque(livre, ar) & Compteur(id) & Catalogue(liste) -> ar(bibliotheque/id) & Base(id, livre) & Catalogue((bibliotheque/id) :: liste) & Compteur(id+1)
2.3 Récupération d'un livre
// Cas d'un livre présent dans la base GET@bibliotheque/id(ar) & Base(id, livre) -> ar(livre) & Base(id, livre) // Cas d'un livre absent de la base GET@bibliotheque/id(ar) & (Base(id, _) inactive) -> ar(ABSENT)
2.4 Récupération du titre d'un livre
// Cas d'un livre présent dans la base GET@bibliotheque/id/titre(ar) & Base(id, (titreLivre, _)) -> ar(titreLivre) & Base(id, (titreLivre, _)) // Cas d'un livre absent de la base GET@bibliotheque/id/titre(ar) & (Base(id, _) inactive) -> ar(ABSENT)
2.5 Recherche d'un livre à partir d'une description
// Cas d'un livre présent dans la base GET@bibliotheque(livre, ar) & Base(id, livre) -> ar(bibliotheque/id) & Base(id, livre) // Cas d'un livre absent de la base GET@bibliotheque(livre, ar) & (Base(_, livre) inactive) -> ar(ABSENT)
2.6 Catalogue
GET@bibliotheque/catalogue(ar) & Catalogue(liste) -> ar(liste) & Catalogue(liste)
3 Implémentation en Java
3.1 Le modèle Objet
Le modèle Objet, fourni, contient cinq interfaces principales, ainsi que leur implémentation :
- Bibliothèque, décrivant les services d'ajout de livres, de recherche et de production de catalogue,
- Archive, décrivant les sous-ressources livresques de la bibliothèque,
- BibliothèqueArchive, héritière de Bibiliotheque et Archive, correspondant à la ressource principale,
- Livre, décrivant le type de données représentant les livres de la bibliothèque,
- LivreRessource, extension de l'interface Livre, correspondant aux sous-ressources de la bibliothèque.
Le livre est simplement décrit par un titre. Le livre ressource est un livre et contient en plus une représentation du livre : il pourrait posséder d'autres attributs propres à la bibliothèque comme une cote.
Pour représenter les hyperliens, deux classes génériques sont fournies :
- Hyperlien<T> : hyperlien vers une ressource de type T,
- Hyperliens<T> : liste d'hyperliens vers des ressources de type T.
Le type T ne sert qu'au typage. Ces classes sont des variantes de la classe abstraite Link, qui sert à représenter des hyperliens dans JAX-RS 2.0. En les faisant dépendre d'un type, elles fournissent une information utile, le type de la ressource vers laquelle l'hyperlien pointe.
3.2 Serveur
Créer un nouveau projet, de type Dynamic Web Projet.
Ajouter les dépendances suivantes dans le fichier pom.xml. La première permet d'importer Jersey, la seconde permet d'utiliser le format de données JSON. Par défaut, seul le format XML est géré.
<dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet</artifactId> <version>2.23.2</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>2.23.2</version> </dependency>
Dans le fichier WebContent/WEB-INF/web.xml, ajouter après l'élément welcome-file-list la référence de la servlet à utiliser.
<servlet> <servlet-name>Jersey</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> </servlet> <servlet-mapping> <servlet-name>Jersey</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
Importer l'archive codeFourni_ServeurBibliotheque.zip à partir de la racine.
Trois paquets sont importés :
- modele : le modèle objet,
- infrastructure : du code lié à JAXB ou JAX-RS,
- configuration : toutes les constantes utilisées pour configurer le service web.
3.2.1 Annotations JAX-RS de la bibliothèque et des livres
Annoter l'interface Bibliotheque de manière à vérifier les propriétés suivantes. On utilisera les constantes de la classe configuration.JAXRS.
- Chaque méthode est qualifiée par le type idoine de méthode HTTP.
Les chemins relatifs d'accès aux trois méthodes sont les suivants :
- ajouter : vide
- chercher : ?titre=…
- repertorier : catalogue
Ainsi, si l'URI de la ressource est http://localhost:8080/Projet/bibliotheque, une URI sera par exemple : http://localhost:8080/Projet/bibliotheque/catalogue.
(Pour indiquer un chemin C, on utilise l'annotation @Path("C").)
Les données sont produites ou consommées au format application/xml.
(Pour indiquer qu'un résultat est sérialisé suivant un certain format javax.ws.rs.core.MediaType.F, on utilise l'annotation @Produces(MediaType.F) ou Consumes(MediaType.F) après importation de MediaType. On peut utiliser des constantes comme argument, ce qui doit être fait ici.)
L'implémentation proposée, ImplemBibliotheque, implémente l'interface Bibliotheque mais aussi l'interface Archive, via l'interface BibliothequeArchive.
Annoter la classe ImplemBibliotheque de manière à vérifier les propriétés suivantes.
- Cette ressource est un singleton.
- Le chemin relatif d'accès à la ressource est bibliotheque.
(Toutes ces annotations appartiennent au paquet javax.ws.rs.)
3.2.2 Indirection vers des sous-ressources
A ce stade, il n'existe pas de relations entre la ressource principale, la bibliothèque, et ses sous-ressources, les livres (de type LivreRessource). On aurait pu établir cette relation dans l'interface Bibliotheque, avec une méthode permettant d'obtenir un livre, étant donné son identifiant. Cependant, nous avons pris le parti de manipuler les livres de la biliothèque uniquement via leurs adresses, a priori quelconques puisque les livres peuvent être situés n'importe où. C'est pourquoi l'interface Bibliotheque ne renvoie que des hyperliens pointant vers des livres et ne propose pas de telle méthode permettant d'obtenir un livre. En revanche, l'interface Archive donne accès aux livres considérés comme des sous-ressources de la bibliothèque. Cette relation d'indirection est fournie par une méthode, sousRessource.
- Quelles annotations JAX-RS possède-t-elle ?
- Quel est le chemin relatif (complétant l'URI de base http://localhost:8080/Projet) permettant d'obtenir le titre du livre d'identifiant 0 ?
- Quel est le chemin relatif (complétant l'URI de base http://localhost:8080/Projet) permettant d'obtenir la représentation du livre d'identifiant 0 ?
3.2.3 Annotations JAXB
Quelle est l'annotation qui prouve que les classes modele.ImplemLivre, infrastruture.jaxrs.HyperLien et infrastruture.jaxrs.HyperLiens sont traduisibles (marshallable) ?
Remarque : l'interface modele.Livre doit être annotée de la même manière.
Etudier la classe de configuration infrastructure.jaxb.FournisseurTraduction.
3.2.4 Conversion des informations de chemin
Les paramètres de chemin et les requêtes, des chaînes de caractères, doivent pouvoir être traduits en des identifiants ou des livres, et réciproquement. Définir dans les interfaces Livre et IdentifiantLivre une fonction fromString réalisant cette traduction.
3.2.5 Filtrage des réponses
JAX-RS ne respecte pas les conventions habituelles concernant le statut HTTP des réponses, pour les requêtes POST renvoyant un objet, ni pour les requêtes GET renvoyant null. C'est la raison pour laquelle deux filtres ont été développés : voir infrastructure.jaxrs.AdapterReponsesCreatedPOST et infrastructure.jaxrs.AdapterReponsesNull404GET.
Expliquer la transformation des messages que ces deux classes réalisent.
- AdapterReponsesCreatedPOST
- AdapterReponsesNull404GET
Annoter les méthodes des interfaces Bibliotheque et Archive de manière à ce qu'elles utilisent ces filtres.
3.2.6 Déploiement
Dans le fichier web.xml, sous l'élément servlet, référencer la définition du service dans la classe infrastructure.Service.
<init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>infrastructure.Service</param-value> </init-param> <load-on-startup>1 </load-on-startup>
Etudier le code de cette classe. Cette configuration est propre à Jersey.
3.3 Client
Créer un nouveau projet Java.
- Le transformer en projet Maven.
Ajouter les dépendances suivantes dans le fichier pom.xml. Les deux premières donnent les bibliothèques côté client, tandis que les suivantes permettent à Jersey d'utiliser les formats XML et JSON pour échanger des données.
<dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-client</artifactId> <version>2.23.2</version> </dependency> <dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-proxy-client</artifactId> <version>2.23.2</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-jaxb</artifactId> <version>2.23.2</version> </dependency> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>2.23.2</version> </dependency>
Y importer à la racine l'archive codeFourni_ClientBibliotheque.zip.
Quatre paquets sont importés :
- modele : le modèle objet,
- client : les classes principales clientes,
- infrastructure : du code lié à JAXB ou JAX-RS,
- configuration : toutes les constantes utilisées pour configurer le service web.
3.3.1 Importation du modèle Objet
Annoter les interfaces Bibliotheque et Archive conformément aux annotations côté serveur. Noter une différence : l'absence du filtre AdapterReponsesNull404GET, qui n'est présent que sur le serveur. Vérifier les annotations des interfaces Livre et LivreRessource.
Vérifier que les classes ImplemLivre et ImplemIdentifiantLivre implémentent la méthode toString. Pourquoi est-ce indispensable ? Est-ce utile que les interfaces associées fournissent une fonction valueOf ou fromString ?
Bien noter que les implémentations des ressources ne sont pas utiles : on y accède via des proxys (de type BibliothequeArchive et LivreRessource respectivement).
3.3.2 Déploiement et test
Configurer l'adresse dans les deux classes clientes.
Déployer l'application serveur et lancer le serveur.
Lancer le client ClientConcurrence plusieurs fois consécutivement de manière à étudier la concurrence. Qu'observe-t-on ?
Ajouter un verrou (de type java.util.concurrent.locks.Lock, objet de la classe java.util.concurrent.locks.ReentrantLock) dans la classe ImplemBibliotheque du serveur de manière à garantir que toutes les requêtes POST aboutissent effectivement à un ajout d'un livre dans la bibliothèque. Quel est l'avantage d'utiliser un tel verrou plutôt que le verrou associé à l'objet this (via la primitive synchronized) ?
Tester.
Créer une nouvelle classe cliente implémentant le scénario suivant. (On pourra s'inspirer de la classe AppliCliente.)
- Ajouter à la bibliothèque dix livres aux titres deux à deux distincts.
- Chercher un livre présent dans la bibliothèque.
- Chercher un livre absent de la biblitohèque. Récupérer l'exception de type WebApplicationException et afficher un message indiquant la requête qui a échoué en vous aidant de la classe Outils du paquet infrastructure.jaxrs.
- Définir un livre proxy à partir d'un hyperlien en utilisant la classe ClientRessource du paquet infrastructure.jaxrs.
- Afficher son type.
- Récupérer son titre.
- Récupérer le livre associé.
- Afficher son type.
Bien noter la manipulation des hyperliens et la relation entre les livres (de type Livre) et les livres proxy (de type LivreRessource).
A quoi sert la classe ClientRessource ?
3.4 TODO Une fédération de bibliothèques
Cette partie sera à rendre et sera notée.