UP | HOME

TP3 - Mobilité des canaux - Contrôle réparti du flot d'exécution

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 un livre
  • 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 par son titre

// 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 trois interfaces principales, ainsi que leur implémentation :

  • Bibliothèque, 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.

La bibliothèque contient les opérations d'ajout, de recherche et de production du catalogue. Le livre est simplement décrit par un titre. Le livre ressource contient en plus une représentation du livre.

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.

3.2 Serveur

TODO Créer un nouveau projet, de type Dynamic Web Projet. Ajouter la bibliothèque CXF Runtime si elle n'est pas présente.

TODO Importer l'archive tp3_bibliotheque_serveur.zip à partir de la racine. Deux paquets sont importés :

  • modele : le modèle objet,
  • infrastructure : du code lié à JAXB ou JAX-RS.

3.2.1 Annotations JAX-RS de la bibliothèque et des livres

TODO Annoter les méthodes de l'interface modele.Bibliotheque de manière à vérifier les propriétés suivantes.

  • Chaque méthode est qualifiée par le type idoine de méthode HTTP.
  • Les chemins relatifs d'accès aux ressources correspondant aux trois méthodes sont les suivants :
    • ajouter : bibliotheque
    • chercher : bibliotheque?titre=…
    • repertorier : bibliotheque/catalogue

    Ainsi, si l'URI de base est http://localhost:8080/Projet, une URI sera par exemple : http://localhost:8080/Projet/bibliotheque.

    (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.)

    (Toutes ces annotations appartiennent au paquet javax.ws.rs.)

TODO Etudier les annotations JAX-RS des interfaces Livre et LivreRessource. Quels sont les chemins relatifs permettant d'obtenir un titre et une représentation ?

  • Réponse :

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'implémentation de la bibiliothèque que nous proposons dans un premier temps, ImplemBibliotheque, possède des livres vus comme des sous-ressources de la bibliothèque. Cette relation d'indirection est fournie par une méthode publique de la classe ImplemBibliotheque.

TODO

  • Quelle est cette méthode ?
  • Réponse :
  • 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 ?
  • Réponse :
  • 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 ?
  • Réponse :

3.2.3 Annotations JAXB

Les livres (de type Livre) et les hyperliens doivent pouvoir être traduits en documents et inversement. Les identifiants et les livres doivent pouvoir être traduits en chaînes de caractères, pour figurer dans des chemins d'accès. Comme les types utilisés sont des interfaces (sauf pour les hyperliens), il est nécessaire de définir des adaptateurs, permettant de relier les interfaces à des classes d'implémentation : cf les classes dans le paquet infrastruture.jaxb. Il reste à déclarer l'usage de ces adaptateurs par des annotations @XmlJavaTypeAdapter(X.class) (où X est un adaptateur) partout où il est requis.

TODO Quelle est l'annotation qui prouve que les classes modele.ImplemLivre, infrastruture.jaxrs.HyperLien et infrastruture.jaxrs.HyperLiens sont traduisibles (marshallable) ?

  • Réponse :

TODO Comment les classes ImplemIdentifiantLivre et ImplemLivre peuvent-elles être construites à partir d'une String ?

  • Réponse :

TODO Annoter les interfaces IdentifiantLivre et Livre de manière à permettre l'usage des adaptateurs (utiliser l'annotation @XmlJavaTypeAdapter).

3.2.4 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 : cf. infrastructure.jaxrs.AdapterReponsesCreatedPOST et infrastructure.jaxrs.AdapterReponsesNull404GET.

TODO Expliquer la transformation des messages que ces deux classes réalisent.

  • Réponse pour AdapterReponsesCreatedPOST :
  • Réponse pour AdapterReponsesNull404GET :

3.3 Client

TODO

  • Créer un nouveau projet Java en y incluant la bibliothèque de CXF (Runtime).
  • Y importer à la racine l'archive tp3_bibliotheque_client.zip. Deux paquets sont importés :
    • modele : le modèle objet et les classes clientes,
    • infrastructure : du code lié à JAXB ou JAX-RS.

(Les erreurs sont normales.)

3.3.1 Importation du modèle Objet

TODO Recopier dans le paquet modele les interfaces et les classes utiles du serveur :

  • Bibliotheque,
  • Livre et ImplemLivre,
  • IdentifiantLivre et ImplemIdentifiantLivre,
  • LivreRessource.

TODO Supprimer les erreurs subsistantes, qui correspondent à des fonctionnalités n'existant pas côté client.

Bien noter que les implémentations des ressources ne sont pas utiles : on y accède via des proxys (de type Bibliotheque et LivreRessource respectivement).

3.3.2 Déploiement et test

TODO Configurer l'adresse dans les deux classes clientes.

TODO Déployer l'application serveur et lancer le serveur.

TODO Lancer le client ClientConcurrence plusieurs fois consécutivement de manière à étudier la concurrence. Qu'observe-t-on ?

TODO 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) ?

Réponse :

TODO Tester.

TODO Créer une nouvelle classe cliente implémentant le scénario suivant. (On pourra s'inspirer de la classe Client.)

  • 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).

TODO A quoi sert la classe ClientRessource ?

  • Réponse :

TODO Comment l'améliorer pour prendre en compte des filtres ?

  • Réponse :

3.4 Une fédération de bibliothèques

Grâce à la mobilité des canaux représentés par les hyperliens, il est possible d'implémenter une bibliothèque par une fédération de bibliothèques existantes.

TODO

  • Réaliser une copie du projet correspondant au serveur.
  • Réaliser une nouvelle implémentation FederationBibliotheques de l'interface Bibliotheque, en remplacement de l'implémentation ImplemBibliotheque.
    • La classe contient un attribut supplémentaire, une liste de proxys vers des bibliothèques distantes. Pour initialiser les proxys, définir à partir du code développé dans les classes de test une fonction prenant en argument l'adresse de la bibliothèque.
    • On utilisera deux bibliothèques aux adresses suivantes : http://localhost:8080/Bibliotheque1 et http://localhost:8080/Bibliotheque2.
    • Implémenter la méthode chercher de manière à réaliser une recherche non seulement locale mais aussi distante dans les bibliothèques agrégées.
    • Implémenter la méthode repertorier de manière à répertorier les livres locaux et ceux distants.
  • Modifier le fichier de configuration restful-beans.xml de manière à utiliser cette nouvelle ressource.
  • Configurer le serveur pour déployer trois bibliothèques (double-click sur le serveur Tomcat, puis onglet "Modules", puis "Add Web Module").
    • Première bibliothèque : premier projet, chemin /Bibliotheque1
    • Seconde bibliothèque : premier projet, chemin /Bibliotheque2
    • Troisième bibliothèque : second projet (fédération), chemin /Federation
  • Définir une nouvelle classe de test dans le projet client.
      public class ClientFederation {
        private static final String ADRESSE1 = "http://localhost:8080/Bibliotheque1";
        private static final String ADRESSE2 = "http://localhost:8080/Bibliotheque2";
        private static final String ADRESSEF = "http://localhost:8080/Federation";
        private static Bibliotheque proxyBibliotheque(String adresse){
          List<Object> filtres = new LinkedList<Object>();
          filtres.add(new AdapterReponsesCreatedPOST());
          return JAXRSClientFactory.create(adresse, Bibliotheque.class, filtres);
        }
        public static void main(String[] args) {
          System.out.println("*** 1. Ajouter des livres ***");
    
          Bibliotheque bib1 = proxyBibliotheque(ADRESSE1);
          Livre l1 = new ImplemLivre("Restful Java with JAX-RS");
          Livre l2 = new ImplemLivre("Restful Java with JAX-RS 2.0");
          bib1.ajouter(l1);
          bib1.ajouter(l2);
    
          Bibliotheque bib2 = proxyBibliotheque(ADRESSE2);
          Livre l3 = new ImplemLivre("Restful Web Services Cookbook");
          bib2.ajouter(l3);
    
          Bibliotheque bibF = proxyBibliotheque(ADRESSEF);
          Livre l4 = new ImplemLivre("SOA");
          bibF.ajouter(l4);
    
          System.out.println("*** 2. Répertorier tous les livres ***");
          System.out.println("Catalogue : " + bibF.repertorier()); 
    
          System.out.println("*** 3. Chercher un livre ***");
          Livre l0 = new ImplemLivre("Absent");
          try {
            System.out.println("R1 - SOA - uri : " + bibF.chercher(l4)); 
          } catch (WebApplicationException e) {
            System.out.println("R1 - exception - "
                            + Outils.messageErreur(e.getResponse()));
          }
          try {
            System.out.println("R2 - Cookbook - uri : " + bibF.chercher(l3)); 
          } catch (WebApplicationException e) {
            System.out.println("R2 - exception - "
                    + Outils.messageErreur(e.getResponse()));
          }
          try {
            System.out.println("R3 - JAX-RS - uri : " + bibF.chercher(l2)); 
          } catch (WebApplicationException e) {
            System.out.println("R3 - exception - "
                    + Outils.messageErreur(e.getResponse()));
          }
          try {
            System.out.println("R4 - JAX-RS - uri : " + bibF.chercher(l1)); 
          } catch (WebApplicationException e) {
            System.out.println("R4 - exception - "
                    + Outils.messageErreur(e.getResponse()));
          }
          try {
            System.out.println("R5 - Absent - uri : " + bibF.chercher(l0)); 
          } catch (WebApplicationException e) {
            System.out.println("R5 - Absent - exception - "
                    + Outils.messageErreur(e.getResponse()));
          }
          System.exit(0);
      }
    }
    

Avec une recherche qui est à la fois locale et distante, le temps d'exécution peut devenir important, en raison de la latence du réseau.

TODO Proposer une solution à ce problème et la décrire en quelques lignes.

  • Réponse :

Last Updated 2016-04-20T14:12+0200. Comments or questions: Send a mail.