Systèmes distribués Plusieurs technologies existent : Les sockets Les remote procedure call (RPC) Remote Method Invocation (RMI)
Les Sockets L'utilisation des Sockets nécessite De gérer le codage et le décodage des messages De concevoir et de développer le protocole d'échange Le concepteur de l'application est conduit à gérer les détails de bas niveau de la communication inter-processus
Remote Procedure Call (RPC) Le programmeur a l'illusion d'appeler une procédure locale RPC s'occupe d'encoder les arguments, de les transmettre sur le réseau, de décoder le résultats de l'appel Il y a plusieurs technologies RPC
Java RMI (Remote Method Invocation) Java RMI est la projection des RPC dans l'environnement Java. Java RMI a pour but de permettre aux développeurs d'écrire des applications distribuées de la même manière qu'ils écrivent des application non-distribuées.
Java RMI L'architecture RMI définit : Comment les objets se comportent; Quand et comment les exceptions peuvent survenir; Comment la mémoire est gérée; Comment les paramétres sont passés aux méthodes distantes; Comment les méthodes distantes retournent les résultats d'exécution.
Java RMI Le code qui définit le service est séparé du code qui implémente le service. La définition du service distant est faite par une interface Java. L'implémentation du service distant est faite par une classe Java. Programme client Programme serveur Interface Implémentation RMI
Notion de Proxy Le proxy c'est la représentation légère d'un vrai objet Dans les logiciels de création de présentation, quand on déplace un groupe d'objets, ce n'est pas chaque objet qui est redessiné à mesure que l'objet est déplacé mais une enveloppe de ce groupe d'objets. Cette enveloppe est le proxy. Le proxy respecte l'interface de l'objet lourd mais redirige les appels de méthodes à l'objet lourd. Dans le contexte RMI, le programme client joue le rôle du proxy et le programme serveur joue le rôle du vrai objet.
Java RMI : Stub et Skeleton Le stub c'est le proxy en RMI. Il a pour fonctions: d'initier la communication avec la JVM distante De traduire les paramètres dans un format qui permette de les transmettre De transmettre les paramètres à la JVM distante D'attendre les résultats De lire les résultats ou les exceptions obtenus et de les transformer en objets java De retourner les résultats à l'appelant
Java RMI : stubs et skeleton Le skeleton réside sur la partie serveur : Il lit les paramètres de l'invocation appelle la méthode invoquée traduit le résultat dans un format qui permette de le transmettre transmet le résultat à la JVM distante
Couche transport Même si deux JVMs tournent sur la même machine, elle communiquent en utilisant la pile TCP/IP de la machine hôte. Une entité communicante est identifiée par (une adresse IP ou un nom de domaine ) et un numéro de port : toto.titi.fr:8880 bonjour.cheztoi.fr:8090 Au dessus de TCP/IP, RMI utilise le protocole RMI-IIOP qui est basé sur un standard de l'OMG
Passage de paramètres Quand un type primitif (int, float, double, ...) est passé à un objet distant, RMI passe le paramètre par valeur. Quand un résultat de type primitif est retourné, il est passé par valeur. Quand un objet est passé à un objet distant, il est passé par valeur : c'est une copie de l'objet qui est passée. Quand l'objet contient d'autres objets, c'est le graphe complet qui est passé par valeur. Quand c'est un objet qui est retourné, il est passé par valeur.
Sérialisation - Désérialisation La sérialisation c'est l'action de transformer un objet en un format qui permette de le transférer sur le réseau. RMI utilise la sérialisation pour le faire passer sur le réseau La désérialisation c'est l'action de recréer un objet à partir du flux capté sur le réseau. RMI utilise la désérialisation pour retrouver un objet à partir d'un flux. Une méthode distante peut renvoyer des objets remote, des types primitifs ou des objets Java. Dans ce dernier cas, ils doivent implémenter l'interface java.io.Serializable. L'interface java.io.Serializable impose d'implémenter les méthodes suivantes : private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
Garbage collector distribué RMI utilise un garbage collector distribué RMI maintient pour chaque client un compteur de référence : le nombre d'objets client qui ont demandé une référence sur cet objet. Quand ce compteur passe à 0, l'objet peut être nettoyé RMI détruit les objets auxquels on n'a pas accédé depuis un intervalle de temps configurable et fixé par défaut à 10 minutes. Il est possible de définir des comportements qui permettent de réagir au nettoyage de l'objet.
Remote Référence Layer RMI contient un service de nommage: le service de registry rmiregistry. C'est le RMI registry qui permet d'obtenir une référence sur chaque service hébergé par le serveur. Côté client, le registry RMI est accédé à travers la classe static Naming. Cette classe fournit la méthode lookup() que le client utilise pour interroger le registry. Lookup prend en paramètre une url qui identifie le serveur et le nom du sevice désiré. Lookup renvoie une référence distante sur l'objet qui va permettre d'accéder au service. L'URL est de la forme: rmi://<host_name>[:<name_service_port>]/<service_na me>
Lancer la registry public static Registry createRegistry(int port) throws RemoteException ==> permet de lancer la registry public static void rebind(String name, Remote obj) throws RemoteException, MalformedURLException ==> Permet d'associer une url à un objet serveur. Il faudra écrire un programme qui va appeler ces deux méthodes en préalable au traitement.
Obtenir une référence sur un objet distant public static void main(String[] args) { try { Remote r = Naming.lookup("rmi://vaio/127.0.0.1/TestRMI"); MonObjectRemote monObjetRemote = (MonObjetRemote) r ; String s = monObjetRemote.getData(); System.out.println("chaine renvoyée = " + s); } catch (Exception e) { }
Registry RMI Le registry RMI tourne sur chaque machine qui héberge des services RMI Sur la machine hôte, le programme serveur crée un service RMI de la façon suivante: Il crée un objet qui implémente le service sur la machine serveur Il exporte l'objet vers RMI RMI crée un service qui va écouter les demandes des clients qui vont demander une connection au service Le serveur enregistre l'objet dans la registry RMI sous un nom public.
Invocation d'une méthode sur un objet distant client serveur Lorsqu'un client désire invoquer une méthode d'un objet distant, il effectue les opérations suivantes : Il localise l'objet distant grâce à un service d’annuaire (Registry) 1- Il obtient dynamiquement une image virtuelle de l'objet distant (stub). 2- Le stub possède exactement la même interface que l'objet distant. 3- Le stub sérialise les appels de la méthode distante, puis les transmet au serveur sous forme de flux de données. 4- Le Skeleton"désérialise" les données envoyées par le stub , puis appelle la méthode en local. 5- Le Skeleton récupère les résultats puis les séralize. 6- Le stub démarshalise les données provenant du Skeleton et les transmet au client
Obtenir une référence sur un objet distant public static void main(String[] args) { try { Remote r = Naming.lookup("rmi://vaio/127.0.0.1/TestRMI"); MonObjectRemote monObjetRemote = (MonObjetRemote) r ; String s = monObjetRemote.getData(); System.out.println("chaine renvoyée = " + s); } catch (Exception e) { }
Définir l'interface d'un objet distant L'interface doit être publique L'interface doit étendre directement ou indirectement java.rmi.Remote Chaque méthode doit inclure une exception java.rmi.RemoteException ou une de ses super- classes java.io.IOException or java.lang.Exception dans sa clause throws en plus des exceptions dépendantes de l'application. Elle peut étendre une interface qui n'étend pas java.rmi.Remote à condition que chaque méthode satisfait à la règle précédente.
Définir une Interface distante (1) public interface BankAccount extends java.rmi.Remote { public void deposit(float amount) throws java.rmi.RemoteException; public void withdraw(float amount) throws OverdrawnException, java.rmi.RemoteException; public float getBalance() throws java.rmi.RemoteException; } Les clauses throws des méthodes de l'objet distant utilisent java.rmi.RemoteException L'interface hérite de java.rmi.Remote
Définir une interface distante (2) public interface Alpha { public final String okay = "constants are okay too"; public Object foo(Object obj ) throws java.rmi.RemoteException; public void bar() throws java.io.IOException; public int baz() throws java.lang.Exception; } public interface Beta extends Alpha, java.rmi.Remote { public void ping() throws java.rmi.RemoteException; Beta respecte aussi la règle des clauses throws Beta étend Alpha mais aussi java.rmi.Remote Alpha respecte la régle des clause throws La définition est correcte
Classe RemoteException La classe java.rmi.RemoteException est la super-classe de toutes les exceptions suceptible d'être lancée par le runtime RMI pendantl'invocation de méthode. Une RemoteException est lancée lorsque une invocation de méthode distante échoue pour une raison quelconque. Entre autres: Échec de la communication ; Échec de la sérialisation ou de la désérialisation ; Erreur de protocole. java.rmi.RemoteException est une exception vérifiée (« checked »). Cela signifie qu'elle doit être gérée par l'appelant et que cela est vérifiée par le compilateur Java.
Les classes Serveur Les fonctions de serveur RMI sont fournies par la classe java.rmi.server.RemoteObject et ses sous-classes java.rmi.server.RemoteServer et java.rmi.server.UnicastRemoteObject et java.rmi.activation.Activatable. La classe java.rmi.server.RemoteObject fournit des implémentations des méthodes de java.lang.Object hashCode, equals, and toString qui sont utilisées pour la gestion des objets distants. Les classes nécessaires à la création d'objets distants et à les rendre disponibles pour les clients sont fournies par les classes UnicastRemoteObject et Activatable. Ces sous-classes introduisent des différences dans le cycle de vie de l'objet distant: La classe java.rmi.server.UnicastRemoteObject définit l'objet distant comme un singleton (c'est à dire une classe qui n'a qu'une seule instance) dont les références ne sont valides que tant que le process serveur est en vie. La classe java.rmi.activation.Activatable définit l'objet distant qui ne devient actif lorsque une de ses méthodes est invoquée et peut se désactiver si nécessaire. Utilisée en règle générale
Les classes Serveur Les règles pour implémenter une interface distante sont les suivantes: Elle implémente java.rmi.server.UnicastRemoteObject ou java.rmi.server.UnicastRemoteObject la plupart du temps ou héritant des comportements définis par les classes java.rmi.server.RemoteObject et java.rmi.server.RemoteServer. La classe implémente un nombre quelconque d'interface distantes; La classe peut étendre une autre classe d'implémentation La classe peut définir des méthodes qui n'apparaissent pas dans l'interface distante mais ces méthodes ne peuvent être utilisées que localement et ne sont pas disponibles pour les clients distants.
Exemple d'implémentation package mypackage; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class BankAccountImpl extends UnicastRemoteObject implements BankAccount { private float balance = 0.0; public BankAccountImpl(float initialBalance) throws RemoteException balance = initialBalance; } public void deposit(float amount) throws RemoteException { ... public void withdraw(float amount) throws OverdrawnException, RemoteException { public float getBalance() throws RemoteException {
%JAVA_HOME%/bin/rmic -classpath . Bonjour La marche à suivre 0. Mettre toutes les classes dans le même répertoire 1. Compiler le projet avec le compilateur java Utiliser netbeans 2. Générer le stub et le skeleton avec l'outil rmic avec en paramètre le fichier interface %JAVA_HOME%/bin/rmic -classpath . Bonjour 3. Démarrer le service de registry RMI. Deux solutions: Exécuter le programme rmiregistry livré avec le jdk en se plaçant dans le répertoire où trouver les fichiers .class Exécuter une classe qui effectue le chargement 4. Lancer l'application pour la tester
Exercices Que font les classes du répertoire Bonjour RMI ? Tenter de compiler et d'éxécuter ... Que font les classes du répertoire Banque RMI ? Compiler et exécuter le pojet BanqueRMI Faire en sorte d'accèder à un serveur BankSystemServeur sur une machine distante.
Exercice : Chat On vous demande d'écrire un programme de chat. On lance le client en précisant à la ligne de commande son nom d'utilisateur Le client affiche la liste des utilisateurs connectés Le client attend ensuite une commande : Nom utilisateur > xxxxxxxxxx Quand l'utilisateur tape entrée, le client envoie le message et le destinataire au serveur. Le serveur renvoie alors la liste des messages qui lui ont été envoyés. Le client affiche alors: De Nom utilisateur : xxxxxxxxxx Définissez les interfaces et proposez une implémentation pour le client et le serveur.