Ce cas d'étude à pour objectif de vous faire mettre en oeuvre les techniques aprises pendent l'UE dans un cas d'application concret et relativement complexe.
Ce cas d'étude comporte un aspect théorique ainsi qu'un aspect pratique. Côté théorique, il s'agit de comprendre et d'utiliser des résultats de recherche dans le domaine de la concurrence, plus spécifiquement un algorithme d'exclusion mutuelle. Cet algorithme sera utilisé pour résoudre un problème inspiré de la vie réelle. Du point de vue technique, il faudra implémenter la solution en mettant en oeuvre trois techniques différentes. Dans la première approche, la concurrence est simulée à l'aide de threads Java sur une seule machine. Ensuite vous utiliserez une approche bas-niveau qui utilise les sockets. La dernière approche utilise CORBA, un middleware de haut niveau.
On considère un ensemble d'étudiants qui partagent un album photo numérique. Les étudiants sont distants, et toute communication se fait par envoi de messages. Lorsqu'un étudiant ajoute une photo à l'album, les autres étudiant peuvent la voir.
L'album photo doit avoir une cohérence forte. Cela veut dire qu'il n'existe jamais plus d'une version de l'album. De plus, il est demander d'implémenter l'ablum en utilisant trois technologies de répartition différentes: les threads, les sockets, et CORBA. Nous nous intéressons uniquement à l'accès en écriture à l'album photo; le contenu même de l'album n'est pas considéré.
Afin de garantir la forte cohérence de l'album photo, nous allons mettre en place un accès exclusif en écriture sur l'album, ce qui veut dire qu'un seul étudiant peut écrire dans l'album à la fois.
L'algorithme d'exclusion mutuelle répartie que nous allons utiliser est l'algorithme de Naimi-Trehel. Cet algorithme défini un vérrou réparti qui fonctionne à base de jeton. Il existe un unique jeton dans le système, et seul le noeud propriétaire du jeton est autorisé à accéder à la ressource partagée. L'algorithme de Naimi-Trehel décrit la façon dont les noeuds du système communiquent afin partager le jeton de façon égale et efficace. L'algorithme est basé sur un arbre réparti qui lie les noeuds entre eux. Cet arbre indique le chemin vers le dernier noeud demandeur du jeton (la racine de l'arbre). D'autre part une liste indique le chemin par lequel le jeton sera transmit d'un noeud à l'autre. Lorsqu'un noeud veut demander ou transmettre le jeton, il se sert de ces structures pour determiner à quel noeud doit être envoyé le message.
Dans notre cas d'étude, les étudiants sont les noeuds et l'album photo est la ressource partagée. Chaque étudiant possède deux champs qui servent à définir l'arbre et la liste évoquées ci-dessus:
Lorsqu'un étudiant reçoit une requête (éventuellement la sienne) pour obtenir le jeton, il a le comportement suivant:
La figure suivante donne un exemple classique de scénario:

Ce système consiste d'un verrou réparti qui protège l'accès en écriture sur l'album photo partagé par des acteurs distants. La figure suivant montre une version simplifiée de l'architecture adoptée:

L'architecture que nous adoptons pour un noeud se décompose en 5 composants. Un composant Actor se charge de demander et relâcher le verrou sur l'album, en ponctuant ces demandes par des délais aléatoires. Ces requêtes sont adressés à un composant Proxy qui fournit une interface locale vers le verrou réparti. Il propose la même interface que l'objet partagé (l'album) mais s'interpose entre lui et l'utilisateur afin de masquer la complexité de l'objet réel. En local, un acteur accède au verrou réparti au travers de son proxy. Ainsi, en plus d'attribuer en local le verrou, le proxy devra céder ou reprendre le verrou aux autres noeuds. Le composant Agent se charge de mettre en oeuvre l'algorithme en recevant et en traitant les requêtes venant d'autres noeuds ou de son propre Proxy. Il se charge également lorsque l'algorithme le requiert d'envoyer des requêtes aux agents des autres noeuds ou au composant Proxy de son propre noeud. Notamment ni le proxy ni l'acteur ne se chargent d'envoyer des requêtes mais passent par l'agent. Donc, cet agent soit reprend le verrou au proxy et le transmet à un acteur distant soit fait suivre la requête (éventuellement de son proxy). Le composant Channel se charge d'établir la communication entre les différents noeuds. Le Buffer contient une file de requêtes. Le channel écrit les messages externes dans le buffer et l'agent va y lire. Un composant spécial appelé Système contient des fonctionnalités générales qui ne sont pas spécifiques aux composants cités ci-dessus.
L'architecture sur un noeud est représentée sur la figure ci-dessous:

Les informations concernant les noeuds (nom, localisation) se trouvent stockées dans le fichier nodes. La méthode readConfFile se charge de lire ce fichier et de remplir la table des noeuds.
send, recv, initialize, activate) pour nos différents
cas d'étude (threads, sockets, CORBA)
initialize () pour construire
un maillage entre noeuds et activate () pour autoriser le
démarrage du système (opérations triviales ici). Notamment
activate () ne sera véritablement utilisée que dans la version CORBA.
La figure ci-dessous indique l'interface que fournit le verrou distant:

L'implantation du verrou réparti doit pouvoir fonctionner en présence de plusieurs threads locaux à un noeud (même si notre étude de cas ne dispose que d'un seul acteur par noeud).
locked signifie que le verrou réparti est disponible sur
le noeud et qu'il est bloqué en local par un thread local au noeud.
unlocked signifie que le verrou réparti est disponible sur
le noeud et qu'il n'est pas pris par un thread local au noeud.
La variable release indique que lorsque le verrou devient
unlocked, celui-ci doit être rendu à l'agent et donc que le proxy
passe dans un état removed.
removed signifie que le verrou réparti n'est pas
disponible sur le noeud et a été transféré vers un autre noeud. Aucune
requête n'a encore été faite pour reprendre le verrou.
delivered signifie que le verrou réparti n'était pas
disponible sur le noeud. Une requête pour le récupérer a été effectuée
et celle-ci vient d'être satisfaite.
requested signifie que le verrou réparti n'était pas
disponible sur le noeud. Une requête pour le récupérer a été effectuée
et celle-ci n'a pas encore été satisfaite.
La figure ci-dessous rappelle les différents états du proxy:

Comportement du Proxy
Le code de Proxy se trouve dans Proxy.java.
deliver.
nextOwner != none).
Lorsqu'il reçoit le verrou, l'agent doit non seulement le donner au
proxy mais juste après doit lui demander de le relâcher par la méthode
request. Dans ce cas et si le proxy a pris le verrou (état
locked), la variable release du proxy passe à l'état vrai.
unlock (release passe à true).
Nous fournissons également les codes complets du reste du système. Notamment :
Nous rappellons que certaines classes doivent avoir été conçues lors de travaux pratiques précédents.
Un scénario particulier permet de comprendre certaines subtilités du code demandé dans la version Threads. Nous en donnons le déroulement dans la figure suivante:
