Objectif Principe

publicité
Objectif
L’objectif de ce TP est de mettre en œuvre le mécanisme d’invocation de méthodes distantes RMI
du langage Java.
Le but de RMI est de permettre l'appel, l'exécution et le renvoi du résultat d'une méthode exécutée
dans une machine virtuelle différente de celle de l'objet l'appelant. Cette machine virtuelle peut être
sur une machine différente pourvu qu'elle soit accessible par le réseau.
La technologie RMI se charge de rendre transparente la localisation de l'objet distant, son appel et le
renvoi du résultat.
En fait, elle utilise deux classes particulières, le stub et le skeleton, qui doivent être générées avec
l'outil rmic fourni avec le JDK.
Le stub est une classe qui se situe côté client et le skeleton est son homologue coté serveur. Ces
deux classes se chargent d'assurer tous les mécanismes d'appel, de communication, d'exécution, de
renvoi et de réception du résultat.
Principe
Le «package» java.rmi et ses sous-packages (java.rmi.server, et java.rmi.dgc) contiennent la
définition de classes et d'outils permettant l'implémentation et l'utilisation d'objets distants.
Le mécanisme proposé permet à une application s'exécutant sur une machine M1 de créer un objet
et de le rendre accessible à d'autres applications: cette application (et la machine M1) joue donc le
rôle de serveur.
Les autres applications manipulant un tel objet sont des clients. Pour manipuler un objet distant, un
client récupère sur sa machine une représentation de l'objet appelé talon/souche (stub): ce talon
implémente l'interface de l'objet distant et c'est via ce talon que le client pourra invoquer des
méthodes sur l'objet distant. Une telle invocation sera transmise au serveur (le protocole TCP est
utilisé) afin d'en réaliser l'exécution.
Du côté du serveur un skeleton/squelette a en charge la réception des invocations distantes, de leur
réalisation et de l'envoi des résultats.
Architecture client / serveur
Les différentes étapes à suivre
Spécifier l'interface de l'objet
L'interface à définir doit hériter de l'interface java.rmi.Remote. Cette interface ne contient aucune
méthode mais indique simplement que l'interface peut être appelée à distance.
La communication entre le client et le serveur lors de l'invocation de la méthode distante peut
échouer pour diverses raisons telles qu'un crash du serveur, une rupture de la liaison, etc ...
Ainsi chaque méthode appelée à distance doit déclarer qu'elle est en mesure de lever l'exception
java.rmi.RemoteException.
import java.rmi.*;
public interface MonInterface extends Remote {
/* spécification des méthodes de l'interface */
public String sayHello() throws RemoteException;
}
Il est important de ne pas perdre de vue que seules les méthodes de la classe implémentant
l'interface Remote seront accessibles aux clients.
Implémenter l'interface du côté serveur
Cette classe correspond à l'objet distant. Elle doit donc implémenter l'interface définie et contenir le
code nécessaire.
Cette classe doit obligatoirement hériter de la classe UnicastRemoteObject qui contient les
différents traitements élémentaires pour un objet distant dont l'appel par le stub du client est unique.
Le stub ne peut obtenir qu'une seule référence sur un objet distant héritant de la classe
UnicastRemoteObject.
Comme indiqué dans l'interface, toutes les méthodes distantes doivent indiquer qu'elles peuvent
lever l'exception RemoteException mais aussi le constructeur de la classe. Ainsi, même si le
constructeur ne contient pas de code il doit être redéfini pour inhiber la génération du constructeur
par défaut qui ne lève pas cette exception.
import java.rmi.*;
import java.rmi.server.*;
public class MonObjetDistant extends java.rmi.server.UnicastRemoteObject
implements MonInterface {
/* définition de la classe (objet, méthodes) */
public MonObjetDistant () throws RemoteException {
super();
}
public String sayHello() throws RemoteException {
return "Hello";
}
Générer le talon et le squelette de l'objet distant
A partir du bytecode correspondant à la classe implémentant l'interface, le talon et le squelette sont
générés au moyen de l'utilitaire rmic : le nom des deux fichiers est déduit de celui de la classe
implémentant l'interface et ils ont respectivement _Stub.class et _Skel.class comme suffixes.
--> javac MonInterface.java
--> javac MonObjetDistant.java
--> rmic MonObjetDistant
Définir l'objet (l'exposer) afin de le rendre visible aux clients
La dernière opération consiste à créer l'objet distant et à enregistrer l'objet créé dans le registre de
nom en lui affectant un nom. Ce nom est fourni au registre sous forme d'une URL constitué du
préfix rmi://, du nom du serveur (hostname) et du nom associé à l'objet précédé d'un slash.
Le nom du serveur peut être fourni « en dur » sous forme d'une constante chaîne de caractères ou
peut être dynamiquement obtenu en utilisant la classe InetAddress pour une utilisation en locale.
C'est ce nom qui sera utilisé dans une URL par le client pour obtenir une référence sur l'objet
distant.
L'enregistrement se fait en utilisant la méthode rebind de la classe Naming. Elle attend en
paramètre l'URL du nom de l'objet et l'objet lui même.
public static void main(String[] args) {
MonObjetDistant testRMIServer = new MonObjetDistant();
Naming.rebind("rmi://"+java.net.InetAddress.getLocalHost() +/TestRMI,
testRMIServer);
}
Sur le serveur, le registre de nom RMI doit s'exécuter avant de pouvoir enregistrer un objet ou
obtenir une référence.
Ce lancement peut-être réalisé de deux façons ;
1. En ligne de commande
rmiregistry &
2. En Java
java.rmi.registry.LocateRegistry.createRegistry(1099);
Réaliser les appels distants au travers d'applications clientes
Le client doit tout d'abord s'adresser au serveur de noms auprès duquel l'objet auquel il souhaite
accéder a été enregistré afin de récupérer un talon de l'objet (objet de la classe Remote). Cette
opération est réalisée en invoquant la méthode statique lookup de la classe Naming. Pour cela, il
doit nommer complètement l'objet concerné au travers d'une URL de la forme générale suivante :
rmi://machine:port/nom
Dans une telle URL,
si le nom de machine est omis, la machine locale est considérée;
si le numéro de port est omis, le numéro par défaut (1099) est considéré.
La méthode lookup() va rechercher dans le registre du serveur l'objet et retourner un objet stub.
L'objet retourné est de la classe Remote (cette classe est la classe mère de tous les objets distants).
Il faut réaliser ensuite un cast vers l'interface qui définit les méthodes de l'objet distant. Pour plus de
sécurité, on vérifie que l'objet retourné est bien une instance de cette interface.
Un fois le « cast » réalisé, il suffit simplement d'appeler la méthode.
public static void main(String[] args) {
Remote r = Naming.lookup("rmi://"+java.net.InetAddress.getLocalHost()
+/TestRMI) ;
if(r instanceof MonInterface){
String s = ((MonInterface)r).sayHello() ;
System.out.println("chaine renvoyée"+s) ;
}
}
Exercice 1
Implémentez un serveur permettant de gérer un compte bancaire. Le serveur propose une interface
(Compte.java) qui permet aux clients de :
•
Débiter ou créditer le compte d'un montant donné.
•
Consulter le solde du compte.
Implémentez la classe CompteServeur.java qui hérite de l'interface Compte.java et qui démarre un
serveur permettant de répondre à des clients RMI distants.
•
Implémentez la classe Client.java.
•
Testez votre serveur.
Exercice 2
1. On souhaite mettre en place un service d’annuaire permettant d’enregistrer des noms et des
numéros de téléphone et permettant de retrouver un numéro à partir d’un nom. L’annuaire doit être
accessible à distance par RMI. On considère que le nom et le numéro de téléphone sont des chaînes
de caractères.
•
Définir une interface RMI répondant à ces spécifications.
•
Développer un objet serveur implémentant cette interface.
•
Développer un client interrogeant l’objet serveur précédent.
2. On souhaite, en plus du numéro de téléphone, stocker pour chaque personne son prénom, son
adresse et le nombre de fois où cette personne à été recherchée dans l’annuaire.
•
Définir une classe Personne stockant ces informations.
•
Modifiez les programmes de l’exercice 1 afin que l’annuaire stocke pour chaque individu,
une instance de cette classe.
Téléchargement