Systèmes et Applications Répartis
Java RMI
ENSI – 2012/2013
Contenu de la présentation
Introduction (Client-Serveur)
Intérêt des objets pour la construction d’applications réparties client-serveur à objet
Architecture de Java RMI
Schéma global, rôle des talons Architecture, rôle des couches RRL et transport
Mode opératoire
Exportation de l’objet Service de nommage Codage : interface, serveur, client Compilation et activation
Passage des paramètres
objet local, objet distant
Appel en retour Chargement dynamique Activation d’objets distants Garbage collector Conclusion
2
Intérêt des objets pour la construction d’applications réparties
Encapsulation
L’interface (méthodes + attributs) est la seule voie d’accès à l’état
interne, non directement accessible
Classes et instances
Mécanismes de génération d’exemplaires conformes à un même
modèle Héritage
Mécanisme de spécialisation : facilite récupération et réutilisation de
l’existant Polymorphisme
Mises en œuvre diverses des fonctions d’une interface Remplacement d’un objet par un autre si interfaces “compatibles” Facilite l’évolution et l’adaptation des applications
ENSI
Systèmes et applications répartis
3
Client-serveur « à objet »
objets "langage"
– représentation propre au
langage : instance d'une classe
– exemple : Java RMI
objets "système"
– représentation "arbitraire" définie par l'environnement d'exécution
– exemple : CORBA
Motivations
– propriétés de l’objet (encapsulation,
modularité, réutilisation, polymorphisme, composition)
– objet : unité de désignation et de
distribution
Eléments d'une "invocation"
– référence d'objet – identification d'une méthode – paramètres d'appel et de retour (y
compris signal d'exception)
• passage par valeur : types élémentaires et
types construits
• passage par référence
4
Appel de méthode à distance Remote Method Invocation (RMI)
Solution proposée par SUN pour adapter le principe des RPC à la
programmation orientée objet.
Technologie fondée sur le langage Java (à partir du JDK 1.1)
Localisation d'objets distants
Les échanges respectent un protocole propriétaire : Remote Method Protocol.
Invocation de méthodes sur des objets distants
Transfert d'objets en paramètres ou en retour (serialization)
ENSI
Systèmes et applications répartis
5
RMI : architecture logique
ENSI
Systèmes et applications répartis
6
La couche des références distantes
Plusieurs protocoles d’invocation possibles :
Invocation point à point (Unicast) Invocation vers des groupes de réplication (Multicast)
Stratégie de reconnexion (si un objet devient inaccessible)
Deux composantes coopérantes :
la composante côté client (client-side) la composante côté serveur (server-side)
Transmission de données entre couches transport par une
connexion orientée flux
ENSI
Systèmes et applications répartis
7
La couche transport
La couche transport de Java-RMI effectue :
Mise en place, gestion, surveillance de la connexion
Ecoute des appels arrivants
Maintien d’une table d’objets distants pour l’espace d’adressage
Localisation du dispatcher pour la cible d’un appel distant et transmission de la
connexion
Elle réalise les connexions réseaux basées sur les flux entre les JVM.
Elle emploie un protocole de communication propriétaire (JRMP : Java
Remote Method Protocol) basé sur TCP/IP.
ENSI
Systèmes et applications répartis
8
Personnaliser la couche Transport des RMI Par défaut, TCP est utilisé par la couche de transport RMISocketFactory (classes java.net.Socket et
java.net.SocketServer)
Motivation
utiliser d’autres classes (que Socket et SocketServer) basées sur
TCP pour de la compression, du chiffrage, …
– Remarque : RMI over SSL utilise cette technique
ou non (UDP)
Etape 1
Écrire 2 sous classes de
java.rmi.RMIClientSocketFactory, java.rmi.RMICServerSocketFactory
qui utilisent 2 autres classes de transport que
java.net.Socket Java.net. ServerSocket
Etape 2
Spécifier les factories dans le constructeur de l’objet distant qui hérite de la classe UnicastRemoteObject protected UnicastRemoteObject(int port, RMIClientSocketFactory csf, RMICServerSocketFactory ssf)
ENSI
Systèmes et applications répartis
9
Appel de méthode à distance Remote Method Invocation (RMI)
objet client
objet serveur
Talon client
Talon serveur
Système de communication
état
Methode_1
.
. Methode_n
Référence d'objet + méthode + arguments
Résultat ou exception
désignation envoi de requêtes exécution de requête retour de résultat
référence
appel
10
Java RMI Mode opératoire côté serveur
rmiregistry
Naming
Serveur
Skeleton
JVM Client
JVM Serveur
Client
11
Java RMI Mode opératoire côté serveur
1 - L'objet serveur s'enregistre auprès du Naming de sa JVM
(méthode rebind)
2 - L ’objet skeleton est créé, celui-ci crée le port de
communication et maintient une référence vers l'objet serveur
3 - Le Naming enregistre l'objet serveur, et le port de communication utilisé auprès du serveur de noms
L'objet serveur est prêt à répondre à des requêtes
12
Java RMI Mode opératoire côté client
rmiregistry
Naming
Client
Stub
JVM Client
13
Naming
Serveur
Skeleton
JVM Serveur
Java RMI Mode opératoire côté client
4 - L'objet client fait appel au Naming pour localiser
l'objet serveur (méthode lookup)
5 - Le Naming récupère les "références" vers l'objet
serveur, ...
6 - crée l’objet Stub et ... 7 - rend sa référence au client 8 - Le client effectue l'appel au serveur par appel à
l’objet Stub
14
La souche client :
La souche client (stub)
Initialise l’appel vers un objet distant (via la couche des références
distantes )
Sérialise les arguments sous forme d’un unique flot de sortie
Informe la couche des références distantes que l’appel peut être
invoqué
Désérialise le flot d’entrée contenant la valeur de retour ou une
exception
Informe la couche des références distantes que l’appel est achevé
ENSI
Systèmes et applications répartis
15
La souche serveur (skeleton)
La souche serveur :
Reçoit l’appel par une méthode unique (aiguilleur)
Désérialise le flot d’entrée des arguments
Prépare l’appel vers l’implémentation réelle de l’objet distant
Distribue l’appel vers la bonne méthode cible
Sérialise les valeurs de retour ou les exceptions dans le flot de
sortie
ENSI
Systèmes et applications répartis
16
Exportation de l'objet
Pour exporter un objet : Utilisation de la classe UnicastRemoteObject
L'objet reste alors actif et accessible en permanence Si la JVM dans laquelle il s'exécute est arrêtée, l'objet disparaît et sa référence
également
Variante possible : Activatable
Publicité
Permet de n'activer l'objet que quand il est sollicité Via un mécanisme et une JVM particuliers Permet de limiter le nombre d'objets actifs en même temps : gain de mémoire
au besoin
Plus complexe à utiliser
ENSI
Systèmes et applications répartis
17
La classe UnicastRemoteObject
Serveurs démarrés explicitement Utilise des comm TCP Pour des comm. P2P entre processus actifs Unicast : l'objet de la classe implémentant l'interface n'existe qu‘en
un seul exemplaire sur une seule machine
L'objet meurt avec la fin de l'exécution du serveur qui le lance
ENSI
Systèmes et applications répartis
18
La classe UnicastRemoteObject
La classe abstraite RemoteObject redéfinit plusieurs méthodes de
Object et en ajoute pour les objets distants hashCode, equals, toString getRef, toStub, writeObject, readObject
La classe abstraite RemoteServer définit des méthodes
communes à tous les serveurs unexportObject, getClientHost, …
La classe UnicastRemoteObject implémente les objets distants
non répliqués (mono-serveur) exportObject, …
ENSI
Systèmes et applications répartis
19
La classe UnicastRemoteObject
La principale fonction de cette classe se manifeste lors de la création d’une instance de l’objet distant étendant cette classe. Le constructeur crée une instance de stub pour l’objet et retourne ce stub comme résultat de la création
Le contenu d’un stub
Un stub contient essentiellement une variable ref de type RemoteRef qui contient la localisation de l’objet (adresse IP, port)
Un appel de méthode se fait par appel de ref.invoke(…) qui
utilise les sockets pour la communication
ENSI
Systèmes et applications répartis
20
Le service de nommage: Enregistrement d'un service
ENSI
Systèmes et applications répartis
21
Le service de nommage: Accès à une référence distante
ENSI
Systèmes et applications répartis
22
Le service de nommage
Une URL est formée pour l'enregistrement puis l'accès sous la forme :
rmi://<host_name>[:port]/[service_name]
Le port de liaison par défaut est le port TCP 1099 La classe Naming encaspule le dialogue avec plusieurs objets serveurs
de liaison Peut utiliser un ou plusieurs registry par application distribuée On doit enregistrer un objet sur un registry local 5 opérations statiques pour enregistrer des objets et récupérer leurs références
ENSI
Systèmes et applications répartis
23
Le service de nommage
L’ objet serveur de liaison
réalise la correspondance nom avec stub Interface d’un objet serveur de liaison
Implémentation par défaut de l’objet serveur de liaison
La classe LocateRegistry
localise ou active un objet serveur de liaison En général invisible au client (appelé en interne par Naming) Méthodes Statiques
Registry createRegistry(int port)
– crée un objet serveur de liaison sur le port spécifié
Registry getRegistry(int port)
– récupère l’objet serveur de liaison qui a été crée Registry getRegistry(server name, port number)
– essaie de trouver un démon rmiregistry sur la machine server name et sur le port port
ENSI
number
Systèmes et applications répartis
24
Le service de nommage
ENSI
Systèmes et applications répartis
25
La notion d’interface
L’interface constitue le contrat - abstrait liant objets serveurs et
objets clients Elle est destinée à être implémentée par l’OD et constitue la base
d’appel pour les objets clients
Elle définie les signatures (noms, types de retour, paramètres) d’un
ensemble de méthodes et seules ces méthodes seront accessibles par un objet client
Pour RMI, c’est une interface Java traditionnelle … mais dérivant de la
classe java.rmi.Remote
ENSI
Systèmes et applications répartis
26
Java RMI Manuel d'utilisation
Codage
Description de l’interface du service Ici pas d’IDL séparé : Java sert d’IDL
Ecriture du code du serveur qui implante l’interface Ecriture du client qui appelle le serveur
Compilation
Compilation des sources (javac) Création des Stub et Skel par rmic (faite automatiquement à partir de JDK 5.0)
Activation
Lancement du serveur de noms (rmiregistry)
L'accès à une référence distante chez le client (par java.rmi.registry.Registry)
Lancement du serveur Lancement du client
27
Java RMI Manuel d'utilisation
Définition de l'interface de l'objet réparti interface : "extends java.rmi.Remote" methodes : "throws java.rmi.RemoteException " paramètres sérialisables : "implements Serializable "
Ecrire une implémentation de l'objet serveur
classe : étend une des sous-classes de java.rmi.server.RemoteServer comme
java.rmi.server.UnicastRemoteObject
Pas de spécialisation et le constructeur doit exécuter l'instruction suivante :
UnicastRemoteObject.exportObject(this);
5 Packages
java.rmi : pour accéder à des objets distants java.rmi.server : pour créer des objets distants java.rmi.registry : lié à la localisation et au nommage d’objets distants java.rmi.dgc : ramasse-miettes pour les objets distants java.rmi.activation support pour l ’activation d ’objets distants.
28
Java RMI : écriture de l’interface
Mêmes principes de base que pour l’interface d’un objet local
Principales différences
L’interface distante doit être publique
L’interface distante doit étendre l’interface java.rmi.Remote
Chaque méthode doit déclarer au moins l’exception java.rmi.RemoteException
Tout objet distant passé en paramètre doit être déclaré comme une interface
(passage de la référence de l’objet)
Tout objet local passé en paramètre doit être sérialisable
29
Java RMI Exemple : Interface
fichier Hello.java
public interface Hello extends java.rmi.Remote { String sayHello() throws java.rmi.RemoteException; }
Description de l ’interface
30
Java RMI : écriture du serveur
Serveur = la classe qui implémente l’interface
Spécifier les interfaces distantes qui doivent être implémentées objets locaux passés par copie (il doivent implémenter l’interface
java.io.serializable)
objets distants passés par référence (actuellement référence à un stub)
C’est un objet java standard
définir le constructeur de l’objet fournir la mise en œuvre des méthodes pouvant être appelées à distance ainsi que celle des méthodes n’apparaissant dans aucune interface
implémentée
Créer au moins une instance du serveur Enregistrer au moins une instance dans le serveur de nom (rmiregistry)
31
Java RMI : écriture du serveur
L’objet distant servant peut être d’une sous classe de la classe
implémentant l’interface
Le serveur peut créer et enregistrer plusieurs objets appartenant à
une ou plusieurs classes
L’enregistrement peut se faire auprès de plusieurs rmiregistry
Il faut prévoir une procédure d’arrêt (shutdown) du serveur
Arrêt des objets UnicastRemoteObject public static void Naming.unbind(Stringname) public static boolean UnicastRemoteObject.unexportObject(Remote,
boolean force)
ENSI
Systèmes et applications répartis
32
Java RMI Exemple : Implémentation de l’objet distant
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject;
fichier HelloObj.java
public class HelloObj extends UnicastRemoteObject implements Hello { private String msg;
// Constructeur public HelloObj(String msg) throws RemoteException {
this.msg = msg;
}
// Implémentation de la méthode distante. public String sayHello() throws RemoteException {
return "Hello world: " + msg;
Réalisation Du serveur
}
}
33
Java RMI Exemple : Implémentation de l’objet distant
fichier HelloObj.java
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject;
public class HelloObj implements Hello { private String msg;
// Constructeur public HelloObj(String msg) throws RemoteException {
this.msg = msg; UnicastRemoteObject.exportObject(this);
}
// Implémentation de la méthode distante. public String sayHello() throws RemoteException {
return "Hello world: " + msg;
}
} 34
Réalisation Du serveur (autre approche)
Java RMI Exemple : Serveur
fichier HelloServeur.java
import java.rmi.*;
public class HelloServeur {
public static void main(String args[]) {
try {
// Crée une instance de l ’objet serveur. HelloObj obj = new HelloObj("HelloServeur"); // Enregistre l'objet créer auprès du serveur de noms. Naming.rebind(Hello.class.getName(), obj); System.out.println("HelloServer" + " bound in registry");
Réalisation du Serveur d’objets
} catch (Exception exc) {… }
}
}
ATTENTION : dans cet exemple le serveur de nom doit être activé avant la création du serveur
35
Java RMI Activation du serveur de nom par le serveur
fichier HelloServeur.java
public static void main(String args[]) { int port; String URL;
try { // transformation d ’une chaîne de caractères en entier Integer I = new Integer(args[0]); port = I.intValue(); } catch (Exception ex) { System.out.println(" Please enter: Server <port>"); return; }
try { // Création du serveur de nom - rmiregistry Registry registry = LocateRegistry.createRegistry(port);
Réalisation du serveur (autre approche)
// Création d ’une instance de l ’objet serveur HelloObj obj = new HelloObj("Coucou, je suis le serveur de port : "+port);
// Calcul de l ’ ’URL du serveur URL = "//"+InetAddress.getLocalHost().getHostName()+":"+port+
Hello.class.getName();
registry.rebind(URL, obj); } catch (Exception exc) { ...}}
36
Java RMI Exemple : Client
import java.rmi.Naming;
fichier HelloClient.java
public class HelloClient { public static void main(String args[]) { try { // Récupération d'un stub sur l'objet serveur. Hello obj = (Hello) Naming.lookup("//suldrun/"+
Hello.class.getName());
// Appel d'une méthode sur l'objet distant. String msg = obj.sayHello(); // Impression du message. System.out.println(msg); } catch (Exception exc) { … } }}
Réalisation du client
On récupère en fait une référence sur une instance de HelloObj_Stub
Le talon local côté client qui sert de proxy pour l'accès à l'instance de HelloObj côté serveur
37
Java RMI Exemple : Client (Recherche du rmiregistry)
fichier HelloClient.java
import java.rmi.Naming;
public class HelloClient { public static void main(String args[]) { try { // Récupération d'un stub sur l'objet serveur.
Publicité
if (args.length > 0) obj = (Hello) LocateRegistry
.getRegistry(args[0]).lookup(Hello.class.getName());
else obj = (Hello)LocateRegistry.getRegistry()
.lookup(Hello.class.getName());
// Appel d'une méthode sur l'objet distant. String msg = obj.sayHello(); // Impression du message. System.out.println(msg); } catch (Exception exc) { … } } }
38
Réalisation du client
Java RMI Compilation
Compilation de l’interface, du serveur et du client
javac Hello.java HelloObj.java HelloServeur.java
HelloClient.java
Génération des talons
Générés automatiquement à partir du JDK 5.0 rmic HelloObj (JDK 1.4 ou antérieur) skeleton dans HelloObj_Skel.class stub dans HelloObj_Stub.class. Les fichiers .java générés sont effacés sauf si l'option -
keepgenerated est spécifiée.
39
Java RMI Déploiement
1) Activation du serveur de nom
start rmiregistry (Windows) ou rmiregistry & (Unix)
2) Activation du serveur java HelloServeur L'exécution d'un serveur RMI nécessite de fixer quelques propriétés:
L'hôte et le chemin d'accès des classes (de l'interface, éventuellement des implémentations) du
serveur
– -Djava.rmi.server.codebase=<repertoire> – Exemple :
» java -Djava.rmi.server.codebase=http://hostname/… HelloServeur » -Djava.rmi.server.codebase=file://dev/hello/myclasses/ » -Djava.rmi.server.codebase=file:/c:\dev\hello\myclasses/ » path indiquant à quel endroit la machine virtuelle cliente va pouvoir chercher le code du stub
Unix Win32
– -Djava.rmi.server.hostname=<hote>
» Nécessaire si le client et le serveur ne sont pas sur la même station
Le fichier définissant la politique de sécurité – -Djava.security.policy=<fichier_policy>
3) Activation du client java HelloClient
40
Passage d'arguments
Il y a 3 possibilités de passage d'arguments lors de l'invocation d'une méthode
distante Le paramètre est d'un type primitif : passage par valeur Le paramètre est un objet
Il est sérialisé et envoyé au serveur
– il doit donc implémenter java.io.Serializable ou java.io.Externalizable
Une référence distante est envoyée :c’est l’objet Stub qui est sérialisé et envoyé à l’objet distant : quand l’objet distant invoque un méthode sur le paramètre, l’invocation est distante.
– Pour cela 3 conditions doivent être respectées :
» L'objet en question hérite de la classe UnicastRemoteObject; » Il implémente une interface partagée avec l'autre partie (client ou serveur); » Sa référence distante (Stub) est accessible par l'autre partie.
Idem pour la valeur de retour d'une méthode distante
ENSI
Systèmes et applications répartis
41
Sérialisation d'objets
La sérialisation est une opération (on parle aussi de marshalling et
d'unmarshalling) qui consiste à transformer un objet dans un format transférable par un flux de données (cf. les classes ObjectInputStream et ObjectOutputStream du package java.io). On s'en sert principalement dans 2 cas : Sauvegarder un objet dans un fichier Déplacer un objet d'une machine virtuelle vers une autre
C'est donc une copie de l'objet qui est envoyée au serveur (ou au client
dans le cas d'une valeur de retour).
Pour être sérialisable, un objet doit implémenter l'interface
java.io.Serializable;
Tous les objets contenus dans l'objet seront aussi sérialisés (ils doivent
donc être aussi sérialisables);
La sérialisation est effectuée automatiquement.
ENSI
Systèmes et applications répartis
42
Java RMI Passage en paramètre d’un objet local
Java VM
Java VM
O2
Objet objet1 m ( O2 )
clone_O2
Skeleton R_objet1
Client R_objet1.m ( O2 )
Stub R_objet1
43
Java RMI Passage en paramètre d’un objet local dont la classe est inconnue du serveur
ENSI
Systèmes et applications répartis
44
Java RMI Passage en paramètre d’un objet local dont la classe est inconnue du serveur
ENSI
Systèmes et applications répartis
45
Java RMI Passage en paramètre d’un objet local : exemple
ENSI
Systèmes et applications répartis
46
Java RMI Passage en paramètre d’un objet distant
Objet O2
Java VM
Java VM
Client R_objet1.m ( R O2 )
Stub R_O2
Objet objet1 m ( R_objet )
Stub R_objet1
47
Skeleton R_objet1
Java RMI Passage en paramètre d’un objet distant
Objet O2
Java VM
Java VM
Client R_objet1.m ( R O2 )
Stub R_O2
Objet objet1 m ( R objet )
Stub R_O2
Stub R_objet1
Skeleton R_objet1
48
Java RMI Passage en paramètre d’un objet distant
Si jamais le type n’est pas disponible
localement, il est chargé dynamiquement (RMIClassLoader)
Pour les paramètres qui seraient déjà des références d’OD,
l’objet amorce (Stub) est passé : impossible de passer en paramètre un objet uniquement visible par les classes serveurs
ENSI
Systèmes et applications répartis
49
Java RMI Passage en paramètre d’un objet distant
Méthode 1
Le Stub est celui d’un objet distant déjà servi
Méthode 2 : Objet Distant Temporaire
But:
L’objet distant est local au client et n’est utilisé que pour la durée d’une ou plusieurs invocations distantes depuis
le client
Solution
Exporter l’objet avec la méthode
UnicastRemoteObject.exportObject()
L’objet doit implémenter l’interface Unreferenced
pour être ramassé par le GC
ENSI
Systèmes et applications répartis
50
Objet Distant Temporaire : Le client
ENSI
Systèmes et applications répartis
51
Objet Distant Temporaire Le serveur temporaire
ENSI
Systèmes et applications répartis
52
Objet Distant Temporaire L’objet distant et son serveur
ENSI
Systèmes et applications répartis
53
Objet Distant Temporaire Déroulement de l’exécution
ENSI
Systèmes et applications répartis
54
Appels en retour
La partie serveur peut avoir besoin d'appeler
une méthode sur la partie client Callback : appel en retour
Deux moyens pour le serveur de connaître le
client Rechercher sa référence via son nom dans
le registre
Récupérer sa référence via l'appel de la
méthode
Exemple : implémentation du patron Observer
Un élément (l'observé) gère une donnée/un
état susceptible de changer
D'autres éléments (les observateurs)
informent l'observé qu'ils veulent être tenus au courant des changements de valeur de la donnée
ENSI
Systèmes et applications répartis
55
Appels en retour : Exemple
Interface d'opérations, côté observateur
Une opération qui sera appelée par l'observé
quand la donnée observée (un entier ici) changera de valeur
ENSI
Systèmes et applications répartis
56
Appels en retour : Exemple
Interface d'opérations côté observé
Une méthode pour s'enregistrer comme observateur de la valeur et une
pour se désenregistrer
Le paramètre est de type IChangeValue
C'est-à-dire un objet implémentant l'interface permettant de
signaler un changement de valeur
ENSI
Systèmes et applications répartis
57
Appels en retour : Exemple
Implémentation de l'observateur
ENSI
Systèmes et applications répartis
58
Appels en retour : Exemple
Implémentation de l'observé
ENSI
Systèmes et applications répartis
59
Appels en retour : Exemple
Implémentation de l'observé (suite)
ENSI
Systèmes et applications répartis
60
Appels en retour : Exemple
Lancement de l'observé
Lancement d'un observateur
ENSI
Systèmes et applications répartis
61
Appels en retour
Note 1
Quand un observateur appelle subscribe sur l'observé distant, il passe sa
référence « distante » car il implémente une interface Remote
L'appel de newValue se fait donc bien sur l'observateur distant (distant du
point de vue de l'observé qui appelle newValue)
Note 2
On peut noter que les observateurs ne se sont pas enregistrés via le registry On peut en effet communiquer avec un objet distant à partir du moment où
il s'est exporté
Publicité
L'enregistrement via le registry n'est utile que pour récupérer sa référence à
partir de son nom associé
Inutile ici car les observateurs passent eux-mêmes leurs références à
l'observé
ENSI
Systèmes et applications répartis
62
Appels concurrents d'opérations
Les méthodes de l'observé sont marquées avec synchronized
Pour éviter des incohérences en cas d'appels concurrents
Côté servant
En interne de RMI, une opération invoquée dynamiquement ou par le
skeleton l'est dans un thread créé à cet usage
Accès concurrent possible si plusieurs clients distants demandent en
même temps à exécuter une opération
Toujours avoir cela en tête quand on implémente un servant même si on
ne voit pas explicitement l'aspect concurrent
ENSI
Systèmes et applications répartis
63
Chargement dynamique des stubs
Rappel : un objet client ne peut utiliser un objet distant qu’au
travers des stubs
RMI permet l’utilisation des OD dont les stubs ne sont pas
disponibles au moment de la compilation
A l’exécution, RMI réclamera au serveur le stub client manquant et
le téléchargera dynamiquement (byte code) Le serveur est lancé en précisant un codebase Le client est lancé sans codebase et retrouvera les classes via les
informations envoyées par le registry
ENSI
Systèmes et applications répartis
64
Chargement dynamique des classes
Plus généralement, le système RMI permet le chargement
dynamique de classes comme les stubs, les interfaces distantes et les classes des arguments et valeurs de retour des appels distants
C’est un chargeur de classes spécial RMI qui s’en charge :
java.rmi.server.RMIClassLoader
ENSI
Systèmes et applications répartis
65
Charger des classes de manière dynamique
Les définitions de classe sont hébergées sur un serveur Web. Les paramètres, les stubs sont envoyés au client via une connexion
au serveur Web.
Pour fonctionner, une application doit télécharger les fichiers de
classe.
Chargement dynamique Cela évite de disposer localement de toutes les définitions de
classe.
Les mêmes fichiers de classe (même version) sont partagés par tous
les clients.
On ne charge que les classes dont on a besoin.
ENSI
Systèmes et applications répartis
66
Fichiers nécessaires si pas de chargement dynamique
Côté Client
Côté Serveur
– l’interface : Hello – le stub : HelloObj_Stub – le client : HelloClient
– l’interface : Hello – l’objet : HelloObj – le serveur d’objets :
HelloServeur – Le skeleton :
HelloObj_Skel
ENSI
Systèmes et applications répartis
67
Fichiers nécessaires si chargement dynamique
Côté client
Côté serveur
– Le client : HelloClient
– Le serveur HelloServeur
Récupérer les fichiers de classe à partir du serveur web
Serveur Web : Interface : Hello
Stub : HelloObj_stub Objet : HelloObj
ENSI
Systèmes et applications répartis
68
Le client peut être lui même dynamique
Côté Client Le client : DynamicClient Chargement dynamique du client et
Côté Serveur Le serveur d’objets :
HelloServeur
de l’interface à partir d’un répertoire local. Si non disponibles localement, ils sont recherchés sur le serveur Web spécifié.
L’exécution de HelloClient permet d’obtenir la référence de l’objet HelloObj et l’appel distant de sa méthode
Chargement dynamique de l’objet HelloObj à partir du serveur Web
Enregistrement de l’objet dans
RMIregistry (bind)
Attente de requêtes des clients
Serveur Web : Interface : Hello
Stub : HelloObj_stub Objet : HelloObj Client : HelloClient
ENSI
Systèmes et applications répartis
69
Principe du chargement dynamique
A l’enregistrement (dans rmiregistry) de l ’objet distant, le codebase est
spécifié par java.rmi.server.codebase. URL est de type « file », « ftp » ou « http » Exemples
$ java -Djava.rmi.server.codebase=file:///home/TPRMI/test-rmi/ Serveur $ java -Djava.rmi.server.codebase=http://www.SAR.ensi.rnu.tn/TPRMI/test-rmi/ Serveur
A l’appel de bind(), le registre utilise ce codebase pour trouver les fichiers
de classe associés à l’objet.
Le client recherche la définition de classe du stub dans son classpath. S’il ne
la trouve pas, il essayera de la récupérer à partir du codebase. La méthode LoadClass de RMIClassLoader charge la classe à partir du codebase
spécifié.
Une fois que toutes les définitions de classe sont disponibles, la méthode
proxy du stub appelle les objets sur le serveur.
ENSI
Systèmes et applications répartis
70
Principe du chargement dynamique
ENSI
Systèmes et applications répartis
71
Principe du chargement dynamique : L ’enregistrement de l ’objet
ENSI
Systèmes et applications répartis
72
Principe du chargement dynamique : La récupération du Stub
ENSI
Systèmes et applications répartis
73
Principe du chargement dynamique : Invocation d ’une méthode
ENSI
Systèmes et applications répartis
74
Les différentes étapes d’un chargement dynamique
Écrire les classes correspondant respectivement à l’interface et à l’objet. Les compiler. Générer le Stub correspondant à l’objet. Installer tous les fichiers de classe sur un serveur Web. Écrire le serveur dynamique. Installer rmiregistry au niveau de la machine du serveur. Lancer le serveur en lui précisant l’URL des fichiers de classe afin qu’il puisse
charger dynamiquement le fichier de classe correspondant à l’objet, l’instancier (le créer) et l’enregistrer auprès de rmiregistry.
Sur la machine du client, écrire le code du client. Compiler le client statique et l’installer éventuellement sur le site Web. Compiler le client dynamique et le lancer en précisant l’URL des fichiers de classe.
ENSI
Systèmes et applications répartis
75
Chargement dynamique et sécurité
Si le code du stub n’est pas présent sur le site local, le protocole RMI prévoit le chargement dynamique du stub en utilisant un serveur web et le protocole HTTP java -Djava.rmi.server.codebase=http://<hostname>/… HelloServeur
Si chargement dynamique…
le chargeur dynamique utilisé par RMI (RMIClassLoader) regarde si la classe
demandée correspond au niveau de sécurité requis
utilisation d’un SecurityManager C’est la classe java.rmi.RMISecurityManager
créer et installer le « gestionnaire de sécurité » (dans le client) System.setSecurityManager(new RMISecurityManager());
76
Sécurité et RMI
L'ensemble des permissions accordées par ce SecurityManager sont définies soit :
en surchargeant certaines méthodes de l'instance (cf. la classe
java.lang.SecurityManager)
en écrivant un fichier externe indiqué dans l'option d'exécution
-Djava.security.policy=<fichier_policy> Example :
java -Djava.security.policy=client.policy HelloClient
Syntaxe simplifiée d'un fichier policy :
grant { permission <permission_class_name> [target_name] [,action] permission <permission_class_name> [target_name] [,action] ... };
ENSI
Systèmes et applications répartis
77
Exemples de fichiers policy
grant { permission java.security.AllPermission; }; grant { permission java.io.FilePermission "/tmp/*",
"read,write";
permission java.util.PropertyPermission
"user.country", "read";
permission java.net.SocketPermission
"localhost:1099", "connect,accept,resolve";
};
ENSI
Systèmes et applications répartis
78
Exemple : serveur dynamique
fichier DynamicServer.java
import java.rmi.Naming; import java.rmi.Remote; import java.rmi.RMISecurityManager; import java.rmi.server.RMIClassLoader; import java.util.Properties; import java.lang.reflect.Constructor; public class DynamicServer { public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try {
Properties p= System.getProperties(); String url=p.getProperty("java.rmi.server.codebase"); Class ClasseServeur = RMIClassLoader.loadClass(url, "HelloObj"); Constructor[] C = ClasseServeur.getConstructors(); Naming.rebind("rmi://sinus.rnu.ensi.tn:1099/MyHello", (Remote)C[0].newInstance(new Object[]{args} )); System.out.println("Objet Hello lié dans le RMIregistry"); System.out.println("Attente des invocations des clients …"); }
catch (Exception e) {
System.out.println("Erreur de liaison de l'objet HelloObj"); System.out.println(e.toString());
} } // fin du main } // fin de la classe
79
Exemple : Le client
fichier HelloClient.java
import java.rmi.*; public class HelloClient { public HelloClient () { try{ Hello obj = (Hello) Naming.lookup ("rmi://sinus.rnu.ensi.tn:1099/MyHello"); // Appel d'une méthode sur l'objet distant. String msg = obj.sayHello(); // Impression du message. System.out.println(msg); } catch (Exception e) { System.out.println ("Erreur d'accès à l'objet distant "); System.out.println (e.toString()); } } }
80
Exemple : Le client dynamique
fichier DynamicClient.java
import java.rmi.RMISecurityManager; import java.rmi.server.RMIClassLoader; import java.util.Properties; import java.lang.reflect.Constructor; public class DynamicClient { public DynamicClient (String [] args) throws Exception { Properties p = System.getProperties(); String url = p.getProperty("java.rmi.server.codebase"); Class ClasseClient = RMIClassLoader.loadClass(url, "HelloClient"); // lancer le client Constructor [] C = ClasseClient.getConstructors(); C[0].newInstance( new Object[]{args} ); } // vérifier le passage de paramètres public static void main (String [] args) { System.setSecurityManager(new RMISecurityManager()); try{
DynamicClient cli = new DynamicClient(args) ;
} catch (Exception e) {
System.out.println (e.toString());
}}}
81
Exemple : Compilation et exécution
sinus> ls HelloObj.java Hello.java DynamicServer.java sinus> javac *.java sinus> rmic -v1.2 HelloObj sinus> mv Hello*.class /var/www/html/TP/rmi
Le répertoire destination des fichiers de classe doit être accessible par http.
sinus> ls *.class DynamicServer.class sinus>rmiregistry & sinus>java -Djava.security.policy=client1.policy -Djava.rmi.server.codebase=
http://sinus.ensi.rnu.tn/TP/rmi DynamicServer HelloServeur
Objet lié Attente des invocations des clients …
----------------------------------
ENSI
Systèmes et applications répartis
82
Exemple : Compilation et exécution
cosinus>ls *.java HelloClient.java DynamicClient.java cosinus>javac *.java cosinus>java -Djava.security.policy=client1.policy -Djava.rmi.server.codebase=
http://sinus.ensi.rnu.tn/TP/rmi DynamicClient
Hello world : HelloServeur Le chargement de HelloClient se fera à partir du répertoire local alors que Hello et HelloObj_Stub seront chargés à partir du serveur Web spécifié dans la commande.
Note
Si à l'exécution des exceptions UnmarshallException, AccessDeniedException ou
ClassNotFoundException sont levées
Correspond souvent à des tentatives de téléchargement de classes mais sans avoir les
permissions suffisantes ou sans savoir où aller les récupérer
Si c'est du côté serveur : le registry n'arrive pas à récupérer le stub, le codebase a été