|
|
|
|
|
|
Développons en Java v 1.60 | |
| Copyright (C) 1999-2011 Jean-Michel DOUDOUX |
![]() |
![]() |
![]() |
RMI (Remote Method Invocation) est une technologie développée et fournie par Sun à partir du JDK 1.1 pour permettre de mettre en oeuvre facilement des objets distribués.
Ce chapitre contient plusieurs sections :
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 machine sur laquelle s'exécute la méthode distante est appelée serveur.
L'appel coté client d'une telle méthode est un peu plus compliqué que l'appel d'une méthode d'un objet local mais il reste simple. Il consiste à obtenir une référence sur l'objet distant puis à simplement appeler la méthode à partir de cette référence.
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.
Le développement coté serveur se compose de :
Le développement côté client se compose de :
Enfin, il faut générer les classes stub et skeleton en exécutant le programme rmic avec le fichier source de l'objet distant
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.
L'interface doit contenir toutes les méthodes qui seront susceptibles d'être appelées à 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.
| Exemple ( code Java 1.1 ) : |
package test_rmi;
import java.rmi.*;
public interface Information extends Remote {
public String getInformation() throws RemoteException;
} |
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. On peut supposer qu'une future version de RMI sera capable de faire du MultiCast, permettant à RMI de choisir parmi plusieurs objets distants identiques la référence à fournir au client.
La hiérarchie de la classe UnicastRemoteObject est :
java.lang.Objectjava.rmi.Server.RemoteObjectjava.rmi.Server.RemoteServerjava.rmi.Server.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.
| Exemple ( code Java 1.1 ) : |
package test_rmi;
import java.rmi.*;
import java.rmi.server.*;
public class TestRMIServer extends UnicastRemoteObject implements Information {
protected TestRMIServer() throws RemoteException {
super();
}
public String getInformation()throws RemoteException {
return "bonjour";
}
} |
Ces opérations peuvent être effectuées dans la méthode main d'une classe dédiée ou dans la méthode main de la classe de l'objet distant. L'intérêt d'une classe dédiée et qu'elle permet de regrouper toutes ces opérations pour un ensemble d'objets distants.
La marche à suivre contient trois étapes :
Cette opération n'est pas obligatoire mais elle est recommandée en particulier si le serveur doit charger des classes qui ne sont pas sur le serveur. Sans security manager, il faut obligatoirement mettre à la disposition du serveur toutes les classes dont il aura besoin (Elles doivent être dans le CLASSPATH du serveur). Avec un security manager, le serveur peut charger dynamiquement certaines classes.
Cependant, le chargement dynamique de ces classes peut poser des problèmes de sécurité car le serveur va exécuter du code d'une autre machine. Cet aspect peut conduire à ne pas utiliser de security manager.
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
try {
System.out.println("Mise en place du Security Manager ...");
System.setSecurityManager(new java.rmi.RMISecurityManager());
} catch (Exception e) {
System.out.println("Exception capturée: " + e.getMessage());
}
} |
Cette opération est très simple puisqu'elle consiste simplement en la création d'un objet de la classe de l'objet distant
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
try {
System.out.println("Mise en place du Security Manager ...");
System.setSecurityManager(new java.rmi.RMISecurityManager());
TestRMIServer testRMIServer = new TestRMIServer();
} catch (Exception e) {
System.out.println("Exception capturée: " + e.getMessage());
}
} |
La dernière opération consiste à 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 seveur (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.
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
try {
System.out.println("Mise en place du Security Manager ...");
System.setSecurityManager(new java.rmi.RMISecurityManager());
TestRMIServer testRMIServer = new TestRMIServer();
System.out.println("Enregistrement du serveur");
Naming.rebind("rmi://"+java.net.InetAddress.getLocalHost()+
"/TestRMI",testRMIServer);
// Naming.rebind(";rmi://localhost/TestRMI", testRMIServer);
System.out.println("Serveur lancé");
} catch (Exception e) {
System.out.println("Exception capturée: " + e.getMessage());
}
} |
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 registre peut être lancé en tant qu'application fournie dans le JDK (rmiregistry) comme indiqué dans un chapitre suivant ou être lancé dynamiquement dans la classe qui enregistre l'objet. Ce lancement ne doit avoir lieu qu'une seule et unique fois. Il peut être intéressant d'utiliser ce code si l'on crée une classe dédiée à l'enregistrement des objets distants.
Le code pour exécuter le registre est la méthode createRegistry de la classe java.rmi.registry.LocateRegistry. Cette méthode attend en paramètre un numéro de port.
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
try {
java.rmi.registry.LocateRegistry.createRegistry(1099);
System.out.println("Mise en place du Security Manager ...");
System.setSecurityManager(new java.rmi.RMISecurityManager());
...
}
} |
L'appel d'une méthode distante peut se faire dans une application ou dans une applet.
Comme pour le coté serveur, cette opération est facultative.
Le choix de la mise en place d'un sécurity manager côté client suit des règles identiques à celui du côté serveur. Sans son utilisation, il est nécessaire de mettre dans le CLASSPATH du client toutes les classes nécessaires dont la classe stub.
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
} |
Pour obtenir une référence sur l'objet distant à partir de son nom, il faut utiliser la méthode statique lookup() de la classe Naming.
Cette méthode attend en paramètre une URL indiquant le nom qui référence l'objet distant. Cette URL est composée de plusieurs éléments : le préfix rmi://, le nom du serveur (hostname) et le nom de l'objet tel qu'il a été enregistré dans le registre précédé d'un slash.
Il est préférable de prévoir le nom du serveur sous forme de paramètres de l'application ou de l'applet pour plus de souplesse.
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).
Si le nom fourni dans l'URL n'est pas référencé dans le registre, la méthode lève l'exception NotBoundException.
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try {
Remote r = Naming.lookup("rmi://vaio/127.0.0.1/TestRMI");
} catch (Exception e) {
}
} |
L'objet retourné étant de type Remote, il faut réaliser 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.
| Exemple ( code Java 1.1 ) : |
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try {
Remote r = Naming.lookup("rmi://vaio/127.0.0.1/TestRMI");
if (r instanceof Information) {
String s = ((Information) r).getInformation();
System.out.println("chaine renvoyée = " + s);
}
} catch (Exception e) {
}
} |
L'appel d'une méthode distante est la même dans une application et dans une applet.
Seule la mise en place d'un security manager dédié dans les applets est inutile car elles utilisent déjà un sécurity manager (AppletSecurityManager) qui autorise le chargement de classes distantes.
| Exemple ( code Java 1.1 ) : |
package test_rmi;
import java.applet.*;
import java.awt.*;
import java.rmi.*;
public class AppletTestRMI extends Applet {
private String s;
public void init() {
try {
Remote r = Naming.lookup("rmi://vaio/127.0.0.1/TestRMI");
if (r instanceof Information) {
s = ((Information) r).getInformation();
}
} catch (Exception e) {
}
}
public void paint(Graphics g) {
super.paint(g);
g.drawString("chaine retournée = "+s,20,20);
}
} |
Pour générer ces classes, il suffit d'utiliser l'outil rmic fourni avec le JDK en lui donnant en paramètre le nom de la classe.
| Attention la classe doit avoir été compilée : rmic à besoin du fichier .class. |
| Exemple ( code Java 1.1 ) : |
rmic test_rmi.TestRMIServer |
rmic va générer et compiler les classes stub et skeleton respectivement sous le nom TestRMIServer_Stub.class et TestRMIServer_Skel.class
La mise en oeuvre et l'utilisation d'objet distant avec RMI nécessite plusieurs étapes :
La commande rmiregistry est fournie avec le JDK.
Il faut la lancer en tache de fond :
Sous Unix : rmiregistry&
Sous Windows : start rmiregistry
Ce registre permet de faire correspondre un objet à un nom et inversement. C'est lui qui est sollicité lors d'un appel aux méthodes Naming.bind() et Naming.lookup()
Il faut exécuter la classe qui va instancier l'objet distant et l'enregistrer sous son nom dans le registre précédemment lancé.
Pour ne pas avoir de problème, il faut s'assurer que toutes les classes utiles (la classe de l'objet distant, l'interface qui définit les méthodes, le skeleton) sont présentes dans un répertoire défini dans la variable CLASSPATH.
![]() |
|
La suite de cette section sera développée dans une version future de ce document
|
|
|
|
|
|
|
Développons en Java v 1.60 | ||
| Copyright (C) 1999-2011 Jean-Michel DOUDOUX |