TP CORBA / Mapping Java

Frank Singhoff

 

SOMMAIRE

I. Introduction

II. Mise en place de l'environnement

III. Exercice 1 : premier pas

IV. Exercice 2 : attributs et exceptions

V. Exercice 3 : usines à objets

VI. Exercice 4 : usines à objets (2), énumérations, structures et séquences

VII. Exercice 5 : introduction au service de désignation CORBA

VIII. Exercice 6 : construction d'une arborescence de noms

IX. Exercice 7 : invocation dynamique

X. Exercice 8 : utilisation d'une Interface Repository

XI. Corrections

XII. Logiciel à télécharger pour faire tourner le TP autre part

XIII. Exercice supplémentaire 1 : mise en œuvre d’un jeu de Monopoly

XIV. Exercice supplémentaire 2 : usine à calculatrices

XV. Exercice supplémentaire 3 : le jeu du trivial pursuit

XVI. Exercice supplémentaire 4 : event services avec type any

XVII. Exercice supplémentaire 5 : mise en œuvre d’un jeu de dames

XVIII. Exercice supplémentaire 6 : simulation d’une ruche

XIX. Exercice supplémentaire 7 : simulateur de l’algorithme de Chang et Roberts

XXI. Exercice supplémentaire 8 : Simulateur d’un protocole de cache mémoire

XXII. Exercice supplémentaire 9: mise en œuvre d’un calendrier

XXIII. Projet CC/SOR 2013/2014 : systèmes de fichiers répartis





I. Introduction

L'objectif de ces exercices est de mettre en pratique les concepts abordés lors du cours d'introduction à CORBA. Ce TP est composé de plusieurs exercices en Java. Le premier est un exemple complet d'un client/serveur CORBA utilisant une interface IDL simple. Cet exercice utilise les outils de base nécessaires à la réalisation d'une application CORBA triviale et présente les principales règles du mapping Java/IDL. Dans le deuxième exercice, on vous demande de compléter l'exemple précédent avec de nouvelles méthodes. Le troisième exercice illustre le fonctionnement d'une usine à objets. Le quatrième exercice est un exercice de synthèse : il illustre différents types IDL (énumération, structures et séquences) Les cinquième et sixième exercices étudient le fonctionnement du service de désignation de CORBA. Enfin, les exercices sept et huit concernent les invocations dynamiques de CORBA.

Le standard CORBA peut être consulté
dans ce répertoire.

II. Mise en place de l'environnement

L'ORB que nous allons utiliser est JACORB. Cet ORB est conforme au standard CORBA 2.0. Il implante les interfaces d'invocation statique et dynamique, le mapping Java ainsi que plusieurs services d'objets de l'OMG.

Avant de pouvoir utiliser cet ORB, vous devez mettre à jour votre environnement grâce aux scripts corba.zsh, ou corba.tcsh. Ceux-ci comprennent la définition de variables nécessaires au bon fonctionnement de l'ORB. Pour positionner ces variables, il suffit alors d'utiliser la commande source.

Si vous utilisez csh ou tcsh, tapez :

source corba.tcsh

dans toutes les fenêtres où vous exécuterez des processus CORBA.
corba.zsh est destiné pour les utilisateurs de zsh et de bash.

III. Exercice 1 : premier pas

Le premier exercice consiste en un exemple simple d'application client/serveur CORBA. L'objet hébergé par le serveur est constitué de deux méthodes (incrementer et decrementer) définies dans l'interface IDL calcul (fichier server.idl). Dans cet exercice, vous devez compiler et exécuter l'application. Pour ce faire, il faut :

make

pour générer les souches et squelettes ainsi que pour compiler le client et le serveur. Le compilateur IDL utilisé ici se nomme idl. Le code Java généré par le compilateur IDL est placé dans le répertoire generated/tpcorba/exo1. Les fichiers .class sont placés dans le répertoire classes/tpcorba/exo1.

On vous demande de regarder quelles sont les étapes que le serveur et le client réalisent respectivement, pour initialiser l'objet CORBA, et pour invoquer les méthodes de l'objet. Vous regarderez plus précisément les points suivants :

  1. Quelles sont les interactions entre le serveur et l'OA ?
  2. Quelle est la relation entre la classe d'implémentation (classe calculImpl.java) et le squelette (le squelette est défini par la classe calculPOA.java dans le répertoire generated/tpcorba/exo1) ?
  3. Dans la souche (classe _calculStub.java), où se trouvent la construction et l'émission de la requête vers le serveur ?
  4. Chercher dans le squelette (classe calculPOA.java) où se situent les appels à l'implémentation de l'objet (classe calculImpl.java).
  5. Enfin, identifier dans le fichier calcul.ref (fichier qui contient la référence d'objet) l'adresse IP où se trouve le serveur et le port TCP utilisé pour les interactions entre les clients et le serveur. Vous utiliserez à cet effet la commande :

dior -f calcul.ref

qui affiche à l'écran le contenu de la référence d'objet.

 

Pour vous aider, voici un bref rappel des principales règles du mapping IDL/Java qui vont vous être nécessaires dans ce TP (le mapping complet est accessible à cet endroit) :

void methode(in type t);

en une méthode Java :

void methode(type t);

void methode(out type t1, inout type t2);

en une méthode Java :

void methode(typeHolder t1, typeHolder t2);

typeHolder est une classe encapsulant une variable de type type (attribut value). En effet, les paramètres sont passés par copie dans Java. C'est donc le champ value de la classe typeHolder qui est utilisé pour véhiculer le paramètre du serveur vers le client.

 

IV. Exercice 2 : attributs et exceptions

Pour cet exercice, récupérez les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO2.

On se propose dans ce deuxième exercice d'ajouter une fonctionnalité de ``mémoire'' à notre calculatrice. La mémoire est implantée sous la forme d'un attribut CORBA. On rappel qu'un attribut d'interface CORBA est une donnée encapsulée dans un objet. Un attribut peut être consulté et/ou modifié à distance par un client. Dans notre cas, il s'agit d'un attribut en lecture seule. Vous pourrez constater que l'attribut "memoire" du fichier IDL est implanté en Java sous la forme d'une méthode du même nom. Le client, pour obtenir la valeur de cet attribut, doit donc invoquer cette méthode. Dans le mapping IDL/Java, un attribut en lecture/écriture nécessite la production de deux méthodes dans la souche. Une seule méthode est générée dans le cas d'un attribut en lecture (voir les souches générées pour de plus amples informations).

On vous demande de :

  1. Compléter la description IDL server.idl, en ajoutant les méthodes ajouteMemoire, soustraitMemoire, multiplieMemoire et miseAZero à l'interface calcul. Chacune de ces méthodes prend un unique paramètre en in de type double (sauf miseAZero qui ne prend pas de paramètre). Elles mettent à jour la mémoire mais ne renvoient pas d'information au client. La méthode diviseMemoire doit lever une exception en cas de tentative de division par zéro.

Une exception CORBA est un mécanisme proche d'une exception Java : une exception CORBA est un événement déclenché lors de l'invocation d'un objet et qui est transmis à l'invoqueur de cet objet. Invoqueur et invoqué ne sont pas nécessairement localisés sur la même machine. Une exception est déclarée avec le mot clef raises dans l'IDL. Pour déclarer plusieurs exceptions dans un fichier IDL, il suffit de les séparer par une virgule ; exemple : void methode() raises (exception1, exception2, ...).

  1. Compléter la définition de la classe d'implémentation dans le fichier calculImpl.java.
  2. Compléter le client afin de tester les quatre méthodes implantées. Le client doit afficher l'état initial de la mémoire, puis effectuer des opérations sur celle-ci. Enfin, avant de se terminer, il devra afficher l'état final de la mémoire.


PS : Les zones de code à compléter sont désignées par des étoiles.

V. Exercice 3 : usine à objets

Nous regardons ici comment allouer dynamiquement des objets CORBA. Pour ce faire, nous allons définir une usine à objets. Une usine à objets offre des services permettant de créer et gérer dynamiquement des objets CORBA.

Pour cet exercice, récupérez les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO3.

Soit le fichier IDL server.idl. Ce fichier contient deux interfaces :

  1. L'interface compte.
  2. L'interface allocateur. La méthode nouveau_compte de cette interface permet de créer dynamiquement un compte bancaire. La création dynamique d'un objet CORBA nécessite les mêmes opérations que la création des objets CORBA au lancement du serveur : allocation de l'objet puis activation auprès de la POA grâce à la méthode servant_to_reference.

 

Travail à faire :

  1. Complétez les classes compteImpl.java et allocateurImpl.java qui implantent les interfaces compte et allocateur. Les zones à compléter sont signalées par des étoiles.
  2. Modifiez le client afin de tester les différentes méthodes des deux interfaces CORBA (PS : le client devra créer dynamiquement au moins deux comptes).

 

VI. Exercice 4 : usines à objets (2), énumérations, structures et séquences


Pour cet exercice, récupérez les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO4.

Dans cet exercice, on vous demande, dans un premier, temps d'écrire un serveur qui implante les interfaces IDL contenues dans le fichier server.idl. Dans un deuxième temps, vous regarderez comment sont utilisés les énumérations, les séquences et les structures IDL en Java.

Le fichier server.idl contient deux interfaces. L'interface abonne mémorise les différentes informations d'un abonné à un opérateur téléphonique. L'interface gestionabonnes assure la création/destruction/consultation des instances du type abonne. L'interface gestionabonnes contient les méthodes suivantes :

 
// Desactive l'objet CORBA
//
try {
   byte [] ObjID = poa_.reference_to_id(ref);
   poa_.deactivate_object(ObjID);
   }
catch (Exception e) {
    System.out.println("POA Exception " + e);
}

 

Travail à faire :

  1. Proposez un serveur (classes abonneImpl.java, gestionabonnesImpl.java et Serveur.java) qui implante le fichier IDL server.idl.
  2. Testez votre serveur avec le client Client.java.

On souhaite enrichir les informations de cet annuaire en ajoutant pour chaque abonné son adresse et son mode de facturation (facturation au forfait ou selon sa consommation).

  1. Récupérez la nouvelle version des fichiers suivants. Avec le compilateur IDL, compilez server.idl. Cette nouvelle version du fichier IDL contient un exemple de séquence, de structure et d’énumération. Structures et énumérations sont utilisés pour mémoriser l’adresse et le mode de facturation de l’abonné. Une séquence permet à un client d’obtenir toutes les références d’objets sur les abonnés actuellement enregistrés.
  2. Quelles sont les classes générées par le compilateur IDL pour les types type_adresse, type_abonnement et table_abonne. A quoi servent elles ?
  3. Modifiez les classes Java qui implantent les interfaces IDL en ajoutant le code nécessaire pour les attributs adresse, abonnement et liste_abonnes.
  4. Testez l’utilisation des attributs adresse et abonnement en modifiant Client.java.
  5. Complétez le programme Liste.java. Ce programme client doit, grâce à l’attribut liste_abonnes afficher à l’écran les noms et prénoms de tous les abonnés actuellement enregistrés.

 

VII. Exercice 5 : introduction au service de désignation CORBA

Dans cet exercice, on teste le service de désignation de CORBA dont une description simplifiée est donnée par le module IDL CosNaming. La documentation concernant le service de désignation de CORBA est ici. Pour cet exercice, récupérez les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO5. Dans ces fichiers se trouvent deux exemples de client/serveur utilisant le serveur de noms de CORBA.

make

ns -Djacorb.naming.ior_filename=/home/xxx/ns.ref

/home/xxx/ns.ref est le nom du fichier où vous souhaitez que l'IOR du service de nom soit mémorisé.

jaco tpcorba.exo5.Serveur -ORBInitRef NameService=file:///home/xxx/ns.ref

jaco tpcorba.exo5.Client -ORBInitRef NameService=file:///home/xxx/ns.ref

·      
Questions :

 

VIII. Exercice 6 : construction d'une arborescence de noms


Dans cet exercice, on vous demande d'écrire un client qui utilise le service de désignation de CORBA. Vous devez récupérer les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO6.

Questions :

·  Le programme Init.java crée un ensemble d'objets de type etudiant : les objets e1, e2, e3, e4, e5 et e6. On vous demande de compléter ce programme afin d'enregistrer ces objets dans différents contextes (les contextes "IUP", "IUP1", "IUP2", "IUP3", "DESS" et "Effectif") selon l'arborescence suivante :

 

·  Testez l'arborescence construite avec le programme Init.java. Pour ce faire, utilisez la commande :

nmg -ORBInitRef NameService=file:///home/xxx/ns.ref

ou la commande :

lsns -ORBInitRef NameService=file:///home/xxx/ns.ref

Ces deux outils affichent la liste des liaisons mémorisées par un serveur de noms dont l'IOR est stocké dans le fichier /home/xxx/ns.ref.

 

IX. Exercice 7 : invocation dynamique




Pour cet exercice, récupérez les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO7. Vous disposez d'une application complète (interface, client et serveur) manipulant la DII de CORBA.

 

X. Exercice 8 : utilisation d'une Interface Repository


L'objectif de cet exercice est d'utiliser un service similaire à l'Interface Repository de CORBA. Le service que nous allons étudier est couplé à un service de nom. Nous illustrons l'emploi de ce service avec la DII. Cet exercice met en oeuvre trois entités (cf. figure ci-dessus):

  1. Le serveur applicatif offre un service métier quelconque. Dans cet exercice, on suppose que le serveur applicatif implante un interface IDL offrant des services de calcul (mini calculatrice).
  2. Le client applicatif utilise les services de calcul mis à disposition par le serveur applicatif. Il invoque le serveur applicatif grâce à la DII.
  3. Le client applicatif se connecte au serveur applicatif via l'Interface Repository. Pour ce faire, il utilise la méthode resolve lui permettant de récupérer une référence d'objet à partir d'un nom symbolique. D'autre part, la méthode resolve permet au client de récupérer une description de l'interface mise à disposition par le serveur applicatif. L'analyse de cette description permet au client, grâce à la DII, d'invoquer l'objet hébergé par le serveur applicatif sans utiliser de souche.
  4. Pour que le client puisse découvrir le service, il est nécessaire que le serveur applicatif publie son objet à l'Interface Repository avec la méthode bind. La méthode bind permet aussi au serveur applicatif d'enregistrer auprès de l'Interface Repository les informations décrivant l'interface IDL qu'il met à disposition.


Pour cet exercice, récupérez les fichiers de ce répertoire. Vous les stockerez tous dans le répertoire EXO8, puis, vous compilerez ces programmes grâce au Makefile. Dans ce répertoire, on vous donne :



Le serveur applicatif (Serveur.java) instancie trois objets CORBA (les objets c1, c2 et c3). Ces objets implantent l'interface calcul (cf. application.idl). Puis, le serveur applicatif décrit l'interface calcul grâce à l'Interface Repository. Pour ce faire, le serveur utilise les interfaces du fichier ir.idl.

Le fichier ir.idl contient trois interfaces mémorisant les informations contenues dans un fichier IDL:

Enfin, une quatrième interface (interface IRepository) constitue le point d'entrée du service. Cette dernière interface offre les services suivants :



Question 1 :

Le programme Liste.java contient un début de client pour l'Interface Repository. On suppose que l'on fournit en paramètre du programme Liste.java (via la ligne de commande) le nom symbolique d'un objet préalablement instancié par Serveur.java (exemple : c1 c2 ou c3).
Modifier le programme Liste.java afin qu'il :

  1. Récupère, avec resolve, la description de l'interface de l'objet dont le nom symbolique est passé en ligne de commande.
  2. Affiche à l'écran, grâce à l'attribut operations de l'interface InterfaceDescription, la liste des méthodes de l'interface associée à l'objet passé en ligne de commande pour le programme Liste.java. Vous afficherez la signature de chaque méthode (liste des arguments). Pour mettre en oeuvre ces invocations a l'IR, vous utiliserez des invocations statiques (utilisation d'une souche pour invoquer l'IR)



Question 2 :

Le serveur applicatif instancie des objets de type calcul. L'interface calcul propose différents services. Ainsi, calcul propose des méthodes pour additionner deux doubles (méthode plus), soustraire deux doubles (méthode moins), multiplier deux doubles (méthode multiplie) et diviser deux doubles (méthode divise).

Proposez un deuxième programme Java (Invoque.java) qui :

  1. Récupère une référence sur un objet instancié par Serveur.java dont le nom est passé en ligne de commande.
  2. Invite l'utilisateur à saisir le nom de la méthode à invoquer (qui peut être soit plus, soit moins, soit multiple soit divise).
  3. Invite l'utilisateur à saisir deux doubles (paramètres de la méthode à invoquer).
  4. Invoque la méthode choisie avec ses deux paramètres, puis finalement, affiche le résultat du calcul. Pour cette invocation vous ne devez pas employer de souche : la requête doit être construite avec la DII.


XIII. Exercice supplémentaire 1 : mise en oeuvre d'un jeu de monopoly



Les fichiers associés à ce projet sont disponible
dans ce répertoire.

L'objectif du CC est de réaliser un jeu de Monopoly. Vous trouverez sur Wikipedia toutes les informations nécessaires concernant les règles de ce jeu. Toutefois, il existe de nombreuses variations de ce jeu, tant sur les règles que sur les cases du plateau. Pour ce projet, vous êtes libre d’implanter les règles de votre choix.



L'application à réaliser est constituée de plusieurs entités (cf. figure ci-dessus) :
  1. Une entité par joueur. Il peut y avoir plusieurs joueurs.
  2. Et une entité qui gère le déroulement de la partie : il s'agit du gestionnaire de parties.


Le gestionnaire de parties est un serveur CORBA. Ce serveur implante les interfaces IDL partie et gestionnaire_de_parties. Vous trouverez une ébauche de ces interfaces IDL dans le fichier monopoly.idl. L'interface gestionnaire_de_parties constitue le point d'entrée de l'application. Les objets CORBA partie sont alloués à la demande des joueurs. Ce fichier IDL doit vous servir de point de départ pour réaliser votre projet : vous pouvez le modifier selon vos besoins. Vous serez d'ailleurs obligé de le compléter afin de déterminer comment le jeu mémorise les différentes données associées à une partie et comment les joueurs peuvent transmettre leurs données au gestionnaire de parties.

Chaque entité associée à un joueur est à la fois un serveur CORBA et un client CORBA. En effet, chaque joueur instancie un objet CORBA de type joueur (cf. fichier IDL monopoly.idl) et invoque les objets CORBA du gestionnaire de parties. Les objets CORBA joueur sont invoqués par le gestionnaire de parties afin de transmettre différents messages aux joueurs. Les messages sont simplement affichés à l'écran et soit informe le joueur du déroulement de la partie, soit l’invite à faire une action. Par exemple, lorsque le gestionnaire de partie envoie un message à un joueur lui indiquant que c’est son tour de jouer, le joueur lance un dès et indique au gestionnaire de parties l’action qu’il souhaite faire (ex: acheter une gare). Chaque joueur est aussi un client CORBA car il invoque les méthodes des objets partie et gestionnaire_de_parties du gestionnaire de parties.



Pour la mise en oeuvre des joueurs, vous devez utiliser la classe Orb_Run.java. La classe Orb_Run.java démarre une POA de façon non bloquante et permet au joueur d'invoquer le gestionnaire de parties une fois que l'objet joueur CORBA est correctement initialisé. Le fichier JoueurProcess.java illustre ce fonctionnement. La structure du programme utilisé par un joueur est donc la suivante :

  1. Initialiser un ORB et une POA.
  2. Instancier un objet joueur, puis la classe Orb_Run.java afin de pouvoir servir les requêtes sur l'objet joueur.
  3. Invoquer les méthodes des interfaces/objets partie et gestionnaire_de_parties pendant le déroulement d'une partie.


Le diagramme de séquence ci-dessus décrit le fonctionnement général de l'application à réaliser. Le déroulement d'une partie s'effectue en deux phases :





Travail à faire :

On vous demande :

De proposer des interfaces IDL qui permettent aux différentes entités d'échanger les données nécessaires à ce jeu (ex : position des joueurs, rues/gares/maisons/argent détenus pour chaque joueur, informations  diverses sur l'état de la partie, ...).

De proposer une mise en oeuvre des différentes interfaces IDL et du protocole de communication entre le gestionnaire de parties et les joueurs.

Vous êtes libre d’adapter le fichier IDL ainsi que le protocole de communication entre le gestionnaire de parties et les joueurs comme bon vous semble.



XIV. Exercice supplémentaire 2 : usine à calculatrice

Pour cet exercice, dans un nouveau répertoire, récupérez les fichiers suivants : Makefile, Client.java, calcul_Impl.java, usine_Impl.java, Calculatrice.java, et tpcorba.idl. Vous les stockerez tous dans un répertoire commun.

On revient sur notre calculatrice mais, maintenant, le serveur doit gérer un ensemble de calculatrices. Pour chaque calculatrice, il maintient une référence sur l'objet CORBA, un identifiant (un nom sous la forme d'une chaîne de caractères utilisée par les clients pour désigner une calculatrice particulière) ainsi qu'un nombre d'utilisateurs (nombre de clients qui manipulent la calculatrice). Ces informations doivent être encapsulées dans des instances de la classe Calculatrice.java. L'objectif est de permettre au client de créer et détruire des calculatrices ; mais aussi d'utiliser des calculatrices précédemment créées par d'autres clients.

Dans cet exercice, il vous est demandé de :

  1. Rappel : pour déclarer plusieurs exceptions dans un fichier IDL, il suffit de les séparer par une virgule ; exemple : void methode() raises (exception1, exception2, ...).
    Modifier le fichier
    tpcorba.idl. en rédigeant pour l'interface usine les quatre méthodes suivantes (n'oubliez pas de déclarer les exceptions utilisées par chaque méthode) :
  2. Complétez l'implantation des interfaces usine et calcul (c'est à dire les fichiers usine_Impl.java et calcul_Impl.java ).
  3. Ecrire un serveur permettant de lancer l'usine à objets. Le nombre de calculatrices que le serveur peut allouer durant son fonctionnement doit être précisé par l'utilisateur lors du lancement du serveur.
  4. Tester votre application avec le client.

 

XV. Exercice supplémentaire 3 : le jeu du trivial pursuit

Pour cet exercice, dans un nouveau répertoire, récupérez les fichiers suivants : trivial.idl, Orb_Run.java,

Dans cet exercice, on souhaite implanter le jeu du trivial pursuit. Ce jeu peut être joué par plusieurs joueurs. Il consiste à répondre chacun son tour à une question. Le premier joueur qui a repondu convenablement à six questions remporte la partie (chaque bonne réponse permet au joueur de remporter un camembert ; la victoire d'un joueur est donc atteinte lorsqu'il détient 6 fromages !). Les questions sont triées par catégories (ex : sports, sciences, géographie ...). On suppose ici que le joueur peut choisir la catégorie lorsqu'on lui pose une question.



L'application est composée d'un serveur de parties et d'un ensemble de joueurs.

Le serveur de parties stocke l'état de la partie de trivial pursuit. Il s'agit d'un serveur CORBA qui gère des objets CORBA de type partie et jeux. L'interface jeux constitue le point d'entrée de l'application. Les objets CORBA partie sont alloués à la demande des joueurs.

Chaque joueur est un serveur CORBA car il instancie un objet de type joueur. Le joueur est aussi un client CORBA car il invoque les méthodes des objets partie et jeux du serveur de parties. Vous devez utiliser la classe Orb_Run.java pour démarrer la POA de façon non bloquante afin que le joueur puisse invoquer le serveur de parties une fois que son objet CORBA est initialisé. Cette classe implante un thread dont le rôle est de servir les requêtes sur l'objet de type joueur.

Le diagramme de séquence ci-dessus décrit le fonctionnement général de l'application avec deux joueurs. Le serveur de parties commence par instancier l'interface jeux, puis, attend l'arrivée des joueurs. Le premier joueur demande la création d'une partie au serveur de parties. Puis, le deuxième joueur s'enregistre auprès du serveur. Lorsqu'un joueur s'enregistre, il donne au serveur une référence d'objet sur son objet CORBA de type joueur.
Une fois que tous les joueurs sont connus par le serveur de parties, alors celui-ci invite un des joueurs à jouer. Cette invitation consiste à envoyer un message au joueur grâce à la méthode a_toi_de_jouer. En réponse à ce message, le joueur concerné récupère la question grâce à la méthode recevoir_la_question et renvoie sa réponse grâce à la méthode donner_la_reponse. Le serveur informe alors le joueur du nombre de points (ou camemberts) qu'il a gagné puis, passe au joueur suivant.

XVI. Exercice supplémentaire 4 : event services avec type any

Voir le répertoire EXOSUPP4.

 

XVII. Exercice supplémentaire 5 : mise en œuvre d’un jeu de dames



L'objectif du CC est de réaliser un jeu de dames. Vous trouverez sur Wikipedia toutes les informations nécessaires concernant les règles de ce jeu. L'application à réaliser est constituée de 3 entités (cf. figure ci-dessus) :

Le gestionnaire de parties est un serveur CORBA. Ce serveur implante les interfaces IDL partie et gestionnaire_de_parties. Vous trouverez une ébauche de ces interfaces IDL dans le fichier dames.idl. L'interface gestionnaire_de_parties constitue le point d'entrée de l'application. Les objets CORBA partie sont alloués à la demande des joueurs. Ce fichier IDL doit vous servir de point de départ pour réaliser votre projet : vous pouvez le modifier selon vos besoins. Vous serez d'ailleurs obligé de le compléter afin de déterminer comment le jeu mémorise les différentes données associées à une partie et comment les demandes de déplacement de pions d'un joueur peuvent être transmises au gestionnaire de parties.


Chaque joueur est à la fois un serveur CORBA et un client CORBA. En effet, chaque joueur instancie un objet de type joueur (cf. fichier IDL dames.idl). Ces objets CORBA joueur sont invoqués par le gestionnaire de parties afin de transmettre un message aux joueurs. Le message doit simplement être affiché à l'écran afin d'avertir le joueur du déroulement de la partie. Chaque joueur est aussi un client CORBA car il invoque les méthodes des objets partie et gestionnaire_de_parties du gestionnaire de parties. Pour la mise en oeuvre des joueurs, vous devez utiliser la classe Orb_Run.java. La classe Orb_Run.java démarre une POA de façon non bloquante et permet au joueur d'invoquer le gestionnaire de parties une fois que l'objet joueur CORBA est correctement initialisé. La structure du programme utilisé par un joueur est donc la suivante :

  1. Initialiser un ORB et une POA.
  2. Instancier un objet joueur, puis la classe Orb_Run.java afin de pouvoir servir les requêtes sur l'objet joueur.
  3. Invoquer les méthodes des interfaces/objets partie et gestionnaire_de_parties pendant le déroulement d'une partie.



Le diagramme de séquence ci-dessus décrit le fonctionnement général de l'application à réaliser. Le déroulement d'une partie s'effectue en trois phases :

  1. Phase 1: initialisation de la partie.
    Le gestionnaire de parties commence par instancier l'interface
    gestionnaire_de_parties, puis, attend l'arrivée des joueurs. Le premier joueur demande la création d'une partie. Puis, le deuxième joueur s'enregistre auprès du gestionnaire. Lorsqu'un joueur s'enregistre, il donne au serveur une référence d'objet sur son objet CORBA de type joueur.
  2. Phase 2: déroulement de la partie.
    Une fois que tous les joueurs sont connus par le gestionnaire de parties, alors celui-ci invite le joueur ayant des pions blancs à jouer. Cette invitation consiste à envoyer un message au joueur grâce à la méthode
    a_toi_de_jouer. En réponse à ce message, le joueur concerné propose un déplacement de pions grâce à la méthode faire_un_déplacement. Le serveur effectue le déplacement, puis, vérifie si la partie est terminée. Le cas échéant, il invite le joueur suivant à jouer.
  3. Phase 3: fin de la partie.
    Après chaque déplacement, le gestionnaire de partie doit vérifier si la partie est terminée. Si la partie est terminée, alors il avertit le gagnant et le perdant en invoquant les méthodes
    partie_perdue et partie_gagnee respectivement. La partie est alors finie.




Travail à faire :

On vous demande :

·  De proposer une interface IDL qui permette aux différentes entités d'échanger les données nécessaires (ex : position des pions lors d'un déplacement, information sur l'état du jeu, ...).

·  De proposer une mise en oeuvre des trois entités.

Vous pouvez travailler seul ou en binome. Le projet est à rendre par mail à Frank Singhoff pour le 23 mars au plus tard. On vous demande de faire un mini-rapport dans lequel vous décrirez le mode d'emploi de votre logiciel, ses fonctionnalités ainsi que sa conception (structure du logiciel). Vous serez évalué sur :

 

 

XVIII. Exercice supplémentaire 6 : simulation d'une ruche

 

XVIII.1 Architecture de l’application répartie à implanter

 


L'objectif du CC est de réaliser un outil de simulation. L’outil de simulation offre des moyens pour étudier le comportement d’une ruche. La figure ci-dessus décrit l’architecture de l’environnement de simulation. Cette architecture est constituée de 3 composants :

 

XVIII.2 Phénomène que l’on cherche à simuler

 

 

On cherche à simuler le comportement d’une ruche. Une ruche est constituée de plusieurs rayons. Chaque rayon contient des cellules. On suppose qu’un rayon est un tableau de 1000 cellules (50 cellules en largeur sur 20 cellules en hauteur). Une cellule peut:

 

On suppose par ailleurs que :

 

XVIII.3 Déroulement d’une simulation

 

Une simulation se déroule de la façon suivante :

  1. Les 3 serveurs de calcul sont d’abord démarrés, puis, le séquenceur est initialisé.
  2. Le séquenceur envoie ensuite à chaque serveur de calcul les données initiales. Ici, les données initiales sont l’état de chaque rayon. Spécifier l’état d’un rayon consiste à spécifier l’état de chacune de ses cellules.
  3. Puis, le séquenceur instancie autant de threads que de serveurs de calcul (et donc de rayons).
  4. La simulation est itérative et est guidée par le temps : chaque itération représente l’écoulement d’une unité de temps (jours, minutes, années selon le phénomène simulé).  Dans notre cas, une itération (aussi appelé « pas de simulation ») représente l’écoulement d’un jour dans la ruche. La simulation itérative est conduite par le séquenceur comme suit :
    1. Le séquenceur démarre l’itération n de la simulation en demandant à chacun de ses threads d’invoquer la méthode faire_un_pas_de_simulation du serveur de calcul associé. Calculer une itération d’un rayon de la ruche consiste à faire évoluer chaque cellule du rayon, c’est à dire :
        1. Vieillir les œufs et asticots déjà présents. Faire naître les abeilles prêtes à naître.
        2. Pondre (si possible) les nouveaux œufs. Si toutes les cellules sont pleines sur le rayon, alors les œufs ne sont pas pondus et sont perdus.
        3. Stocker (si possible) les nouvelles cellules de miel. Si toutes les cellules sont pleines sur le rayon, alors les nouvelles cellules de miel récoltées sont perdues.
        4. Stocker (si possible) les nouvelles cellules de pollen. Si toutes les cellules sont pleines sur le rayon, alors les nouvelles cellules de pollen récoltées sont perdues.
    2. Lorsque la méthode faire_un_pas_de_simulation est terminée pour tous les rayons, le séquenceur demande à ses threads de lancer l’itération n+1. La simulation progresse donc au même rythme pour tous les rayons: c’est une simulation dite « synchrone ».

 


Le diagramme de séquence ci-dessus résume les différents échanges de message durant une simulation. On peut bien sur imaginer que le séquenceur guide la simulation avec d’autres primitives plus élaborées. Ainsi, la primitive simuler_jqa de ruches.idl doit permettre de faire tourner la simulation pendant plusieurs jours.

  1. L’interface de supervision peut être lancée à tout moment, à la demande l’utilisateur. L’interface de supervision doit pouvoir interroger les différents serveurs de calcul, à la demande de l’utilisateur, afin de récupérer les résultats de simulation pour chaque rayon (population d’abeilles, œufs, larves, miels, …).


XVIII.4 Travail à faire :

On vous demande :

 

1.     une première interface qui modélise les données d’un rayon afin que l’interface de supervision puisse les récupérer (population d’abeille, miel et pollen perdus, nombre de larves, nombre d’œufs pondus et perdus, position des œufs/larves/miel/pollen sur le rayon, …).

2.     une deuxième interface qui permette de contrôler les simulations de chaque rayon, interface utilisée par le séquenceur.

 

 

 

XIX. Exercice supplémentaire 7 : Simulateur de l’algorithme de Chang et Roberts avec CORBA

 

Cet exercice est noté et constitue la note de projet de l'UE Systèmes répartis. Vous pouvez travailler seul ou en binome. Le projet est à rendre par mail à Frank Singhoff le 25 mars au plus tard. On vous demande de faire un rapport de deux pages maximum dans lequel vous décrirez le mode d'emploi de votre logiciel, ses fonctionnalités ainsi que sa conception (structure du logiciel). Vous serez évalué sur :

Nous nous contenterons ici de résumer le principe de l'algorithme d’élection de Chang et Roberts : 

 

Travail à faire :  

On vous demande de proposer une application Java/CORBA qui implante l’algorithme ci-dessus.

-        Le programme Java simule une des machines du système réparti. Les machines sont organisées en anneau unidirectionnel. Pour ce faire, le programme Java sera lancé autant de fois que de machine dans l’anneau. Chaque machine simulée par le programme CORBA doit avoir son UID (qui est un entier) et qui peut être obtenu, par exemple,  grâce à un argument en ligne de commande.

-        Chaque programme hébergera un objet CORBA qui permet à un programme d’envoyer un entier (l’UID) à la machine successeur.

-        Chaque programme sera donc à la fois client et serveur CORBA : client car il peut envoyer un entier sur la demande de l’utilisateur et serveur car il peut recevoir simultanément l’identifiant d’un autre programme.

-        L’élection est déclenchée par l’utilisateur grâce au clavier.

-        Chaque programme doit afficher toutes les secondes s’il est la machine élue.

-        Vous devrez utiliser la classe Orb_Run.java. Instancier la classe Orb_Run.java permet de démarrer une POA de façon non bloquante, ce qui permettra au programme Java de lire les saisies clavier tout en servant simultanément les requêtes CORBA. Le fonctionnement du programme sera le suivant :

1.     Le programme commence par initialiser l’objet CORBA permettant aux différentes machines de dialoguer.

2.     Puis, il lance une POA non bloquante.

3.     Enfin, il attend la saisie au clavier d’un caractère qui va déclencher l’algorithme d’élection.

-        Vous testerez votre programme avec un anneau de 3 machines au moins.

 

Quand un processus tombe en panne (par exemple, quand un processus est arrêté par l’utilisateur), la boucle de machines est brisée. On vous demande de modifier votre mise en œuvre de sorte que les machines vérifient périodiquement la panne de leur successeur. En cas de panne d’une machine, son prédécesseur doit reconstruire la boucle en se connectant au successeur de la machine en panne. Lorsque la boucle est reconstituée, une phase d’élection doit être automatiquement déclenchée.

Modifiez votre programme CORBA de sorte que :

-        La panne d’un programme peut être détecté grâce à CORBA : un programme qui invoque son successeur reçoit au bout de quelques secondes une exception org.omg.CORBA.TRANSIENT si celui-ci est en panne.

-        On suppose qu’une seule panne arrive dans le système : lorsqu’une machine en panne est détectée, on suppose qu’aucune panne supplémentaire ne peut intervenir. Votre solution devra donc proposer une solution lorsque le système est victime d’une panne de machine au maximum.

-        Vous testerez cette nouvelle mise en œuvre avec un anneau de 3 machines au moins.

 

 

XXI. Exercice supplémentaire 8 : mise en œuvre d’un protocole de cache mémoire

Nous nous contenterons ici de résumer le principe de l'algorithme réparti par diffusion de Kai Li et Paul Hudak.

 

Principes de l'algorithme

 

L'objectif de cet algorithme est de permettre à des processeurs faiblement couplés de partager des données par un système de mémoire partagée répartie, en s'assurant que cette mémoire est cohérente. Pour Li et Hudak, une mémoire est cohérente si une opération de lecture à une adresse retourne toujours la dernière valeur écrite à cette adresse, quels que soient les processeurs effectuant ces opérations de lecture et d'écriture.

 

Cet algorithme met en jeu un ensemble de processus qui s'exécutent sur des sites distincts, qui ne peuvent pas avoir de défaillances. Ces sites sont faiblement couplés, en ce sens qu'ils ne peuvent s'échanger des informations que sous forme de messages transmis par un canal de communication asynchrone.

 

Cet algorithme s'appuie sur deux types de communication. Un message envoyé en diffusion est reçu par tous les sites impliqués dans l'algorithme. Un message envoyé en point à point à un émetteur unique et un destinataire unique.

 

Un ensemble de processus se partagent des pages de mémoire. Les accès aux pages peuvent se faire en lecture ou en écriture ; à tout moment, sur une page donnée, il peut y avoir plusieurs lecteurs ou exclusivement un seul rédacteur. Un défaut de page est donc effectué soit en lecture, soit en écriture.

 

Pour chaque page, à un moment quelconque, il existe un site propriétaire qui assure la gestion des accès à la page et sert les demandes des autres sites. Le propriétaire est défini comme le dernier site ayant effectué une écriture sur ladite page ; il n'y a donc pas de propriétaire fixe.

Chaque site possède une table des pages notée Ptable renseignant, pour chaque page, les valeurs suivantes :

 

 

Description de l'algorithme

 

L'algorithme de Li et Hudak est structuré en cinq éléments. Chacun de ces éléments existe sur tous les sites.

Les algorithmes ci-dessous présentent les différents modules de l'algorithme de Li et Hudak, tels qu'ils sont décrits dans l’article disponible ici. Ils exposent le cas de requêtes sur une page p. Ces requêtes sont éventuellement distantes ; elles sont dans ce cas émises par le site s. Le site local, où sont exécutés ces algorithmes, est désigné par ego.

 

Les gestionnaires de défaut en lecture et en écriture gèrent les défauts de page effectués localement. De même, les serveurs en lecture et en écriture délivrent les pages demandées par d'autres sites via le réseau. Un serveur d'invalidation reçoit et applique les requêtes d'invalidation de pages.

 

 

Gestionnaire de défauts en lecture :

    Verrouiller (PTable[p].verrou)

    Diffuser une demande en lecture pour p

    Attendre la réception de p

    PTable[p].accès := lecture

    Déverrouiller (PTable[p].verrou)

 

Serveur en lecture :

    Verrouiller (PTable[p].verrou)

    Si je suis le propriétaire de p

       Alors

          PTable[p].copyset := PTable[p].copyset + s

          PTable[p].accès := lecture 

          Envoyer p au site s

    Fin si

    Déverrouiller (PTable[p].verrou)

 

Gestionnaire de défauts en écriture :

    Verrouiller (PTable[p].verrou)

    Diffuser une demande en écriture pour p

    Attendre la réception de p et de son copyset

    Invalider (p,PTable[p].copyset)

    PTable[p].accès := écriture

    PTable[p].copyset := ensemble vide

    PTable[p].propriétaire := ego

    Déverrouiller (PTable[p].verrou)

 

Serveur en écriture :

    Verrouiller (PTable[p].verrou) 

    Si je suis le propriétaire de p

       Alors

          Envoyer p et PTable[p].copyset au site s

          PTable[p].accès := nil

          PTable[p].propriétaire := s

   Fin Si

   Déverrouiller (PTable[p].verrou)

 

Fonction d'invalidation(p, copyset) :

  Pour i dans copyset Faire

   Envoyer une requête d'invalidation de p au site i

  Fin Pour

 

Serveur d'invalidation :

    PTable[p].accès := nil

 

 

Travail à faire :

 

 

 

 

 

XXII. Exercice supplémentaire 9 : mise en œuvre d’un calendrier

Cet exercice est tiré d’un précédent examen.


On souhaite implanter un calendrier en CORBA/Java. Pour ce faire, vous disposez de l’interface IDL  ical.idl ainsi que du Makefile suivant.

Cette application fonctionne de la façon suivante :

1.     De créer de nouveaux calendriers grâce à la méthode creer_calendrier. Cette méthode requiert le nom du calendrier à créer, puis, alloue un objet CORBA de type calendrier. L'argument ref permet au client de récupérer  la référence sur l'objet ainsi instancié. Par la suite, l'objet calendrier permet au client d'ajouter des données grâce à la méthode ajouter_donnee.

2.     De se connecter à un calendrier existant grâce à son nom via la méthode rechercher_calendrier.

 

 

Travail à faire :

 

1.     Proposer un serveur et les classes Java qui implantent les interfaces IDL calendrier et usine.

2.     Proposer un client qui utilise ce service et qui fonctionne de la façon suivante :

o   Le client récupère, grâce à la méthode rechercher_calendrier, la référence d’un objet calendrier dont le nom est passé en ligne de commande.

o   Puis, il liste, grâce à l'attribut/séquence calendrier_entier, pour le calendrier passé en argument, les données actuellement stockées chez le serveur. Vous afficherez le contenu de chaque structure stockée dans cette séquence (description, année, jour, mois et le libellé associé). Pour tester cette fonctionnalité, vous devrez écrire un premier client qui crée un calendrier et lui ajoute des données.

3.     Questions qui avaient été demandées lors de l’examen (et que l’on ne vous demande pas de traiter ici) ;

o   Cette interface IDL utilise des séquences, énumérations et structures : expliquez comment ces trois types de données IDL sont traduites en Java.

o   A partir de cette interface IDL, le compilateur Java générera différents fichiers Java : lister ces fichiers et indiquez leur utilisation.





XXIII. Projet CC/SOR 2013/2014 : systèmes de fichiers répartis



Dans cet projet, on vous demande d'implanter un serveur qui permet d'accèder via CORBA à un mini système de fichiers.
Les interfaces IDL de ce serveur sont les suivantes.

Le serveur permet de créer, lire ou écrire dans des fichiers qui peuvent être, soit des fichiers réguliers, soit des répertoires. On suppose que les fichiers réguliers sont des fichiers textes. Chaque fichier accessible par le serveur l'est au travers d'objets CORBA. Pour mettre à disposition ce système de fichiers, deux interfaces devront être implantées :

  1. L'interface regular_file qui permet aux clients de manipuler un fichier régulier particulier.
  2. L'interface directory qui permet aux clients de manipuler un répertoire donné. A partir d'un objet CORBA représentant un répertoire donné, il est possible d'accéder aux fichiers réguliers et aux sous répertoires qu'il contient.



Un client qui souhaite manipuler des fichiers doit donc obtenir des références d'objets CORBA représentant ces dits fichiers.

L'interface regular_file contient les méthodes suivantes :

  1. La méthode read. Cette méthode est une demande de lecture du fichier de size caractères. Le résultat de la lecture est déposé dans la chaîne de caractères data et la méthode retourne le nombre de caractères effectivement lus. Chaque objet de type regular_file mémorise un offset à partir duquel les lectures et écritures doivent être effectuées.
  2. La méthode write. Cette méthode permet d'effectuer une écriture dans le fichier de size caractères. La chaîne de caractères à écrire dans le fichier est contenue dans data. La méthode retourne le nombre de caractère effectivement écrits.
  3. La méthode seek positionne l'offset du fichier à la position new_offset.
  4. Enfin, la méthode close permet de fermer le fichier. Tout accès au fichier grâce à la référence d'objet à partir de laquelle la méthode close a été invoquée devient alors interdit.



L'interface directory contient les méthodes suivantes :

  1. L'attribut number_of_file permet au client de connaître le nombre de fichiers réguliers et de sous répertoires inclus dans le répertoire associé à un objet CORBA de type directory (répertoire que nous désignerons dans la suite par le terme de "répertoire courant").
  2. La méthode open_regular_file permet d'ouvrir un fichier régulier existant dans le répertoire courant. Grâce à cette méthode, le client obtient une référence d'objet CORBA associée au fichier régulier ouvert. name est le nom du fichier régulier à ouvrir et m le mode d'ouverture du fichier. Le fichier peut être ouvert :
    1. En lecture seule (mode read_only) : l'offset est alors positionné au début du fichier.
    2. En écriture seule (modes write_append et write_trunc). Avec le mode write_append, l'offset est positionné sur la fin de fichier. Ce mode permet d'ajouter de nouvelles données dans le ficher. Avec le mode write_trunc, l'offset est positionné au début du fichier et le fichier est vidé à l'ouverture. Ce mode permet de réinitialiser le contenu d'un fichier régulier.
    3. En lecture et en écriture (mode read_write). L'offset est alors positionné sur le début du fichier mais le fichier n'est pas vidé. Les données qui seront écrites remplaceront alors celles précédemment mémorisées.
  3. La méthode open_directory permet d'obtenir une référence sur un objet CORBA associé à un sous répertoire existant dans le répertoire courant. name est le nom du sous répertoire à ouvrir.
  4. La méthode create_regular_file permet de créer, dans le répertoire courant, un nouveau fichier régulier dont le nom est name.
  5. La méthode create_directory permet de créer, dans le répertoire courant, un nouveau sous répertoire dont le nom est name.
  6. La méthode delete_file supprime le sous répertoire ou le fichier régulier du répertoire courant dont le nom est name.
  7. La méthode list_files permet d'obtenir la liste des fichiers réguliers et des sous répertoires contenus par le répertoire courant. La méthode retourne le nombre total de fichiers réguliers et de sous répertoires contenus dans le répertoire courant. La référence d'objet CORBA de type file_list est un itérateur permettant d'obtenir, par appel successif à la méthode next_one, les noms des différents sous répertoires et fichiers réguliers du répertoire courant. Lorsque la méthode next_one retourne false, la liste de sous répertoires et de fichiers réguliers a été entièrement parcourue. A chaque appel de la méthode next_one, une structure directory_entry renseigne pour chaque fichier son nom ainsi que son type (répertoire ou fichier régulier).

 

Travail à faire :

  1. Proposez un serveur qui implante les interfaces directory, regular_file et file_list. Les méthodes doivent tenir compte des conditions d'erreur suivantes :


Vous êtes libre d'ajouter d'autres exceptions si vous estimez que certains cas d'erreurs doivent être traités. Par ailleurs, vous pouvez étendre l'interface IDL donné, mais votre serveur doit au minimum offrir les services décrits ci-dessus.

Vous donnerez un client illustrant les différents services offerts par votre serveur: ce client donc faire office de jeu de test.

Vous pouvez travailler seul ou en binôme. Le projet est à rendre par mail à Frank Singhoff pour le 17 mars au plus tard. On vous demande de faire un rapport. Ce rapport de 3 pages maximum doit contenir le mode d'emploi de votre logiciel, ses fonctionnalités ainsi que sa conception (structure du logiciel). Vous serez évalué sur :





Page maintenue par Frank Singhoff (singhoff@univ-brest.fr)u>
Dernière mise à jour le 20 janvier 2014