Rappel des conclusions sur l’appel de procédure à distance
2
!! Avantages
!! Abstraction! ("les détails de la communication sont cachés!)"
!!
!! Outils de génération!: "facilitent la mise en œuvre
Intégration dans un langage! : "facilite la portabilité!, "mise au point
!! Limitations
!! La structure de l’application est statique
"! Pas de création dynamique de serveur
"! Pas de possibilité de redéploiement entre sites
!! Pas de passage des paramètres par référence
!! La communication est réduite à un schéma synchrone
!! La persistance des données n’est pas assurée
"!
Il faut la réaliser explicitement par sauvegarde des données dans des fichiers
JAVA RMI
!!
"Des mécanismes plus évolués visent à remédier à ces limitations
!!
"Objets répartis! ("ex! : "Java RMI!, "CORBA!) "pour les 2! "premières limitations
"Bus à messages! ("Message Oriented Middleware!)"
"Composants répartis! ("ex! : "EJB!, "Corba CCM!, ."Net!)"
!!
!!
Intérêt des objets pour la construction d’applications réparties
3
!! Encapsulation
!! L’interface (méthodes + arguments) 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 la récupération et
réutilisation de l’existant
!! Polymorphisme
!! Mises en œuvre diverses des fonctions d’une interface
!! Facilite l’évolution et l’adaptation des applications
Extension du RPC aux objets (1)
4
!! Appel de procédure vs appel de méthode sur un objet
!! Exemple : insérer une entrée dans un annuaire
5
7
Extension du RPC aux objets (2)
!! Phase préalable : création d’instances d’une classe d’objets
!! Notion de fabrique (factory)
Notion de référence d’objet :
Contient tout ce qui est nécessaire pour atteindre l’objet distant
(Localisation, protocole d’accès)
Notion de référence
6
!! Référence : notion clé pour les objets répartis
!!
"Moyen d’accès à un objet distant,
!!
!! Son contenu ne peut pas être directement exploité «!opaque!»,
elle n’est utilisable que via un mécanisme d’appel à distance
"Contenu d’une référence
!!
"Toutes les informations nécessaires pour atteindre physiquement l’objet
"! Site de résidence (adresse IP)
"! Numéro de porte sur le site (Numéro de port)
"! Localisation interne au serveur
"!
!! Exemples! "!
"…
"En Java RMI! : "la référence est le talon client! ("stub!)"
!!
!! En Corba : format de référence spécifié! ("IOR! : "Interoperable Object Reference!)"
!!
"En DCOM! : "format! "“propriétaire”
Java RMI (Remote Method Invocation)
Java RMI : règles d’usage (1/2)
!! Motivation : construction d’applications réparties avec Java
!! Appel de méthode au lieu d’appel de procédure
!! Principe : même schéma que RPC
!! Le programmeur fournit
"! Une (ou plusieurs) description(s) d’interface
Ici pas d’IDL séparé : IDL=JAVA
"!
"! Le programme du serveur
"! Objets implémentant les interfaces
"! Serveur d’objets
"! Le programme du client
!! L’environnement Java fournit
!! Un générateur de talons : rmic
!! Un service de noms : Object Registry
voir http://java.sun.com/docs/books/tutorial/rmi/
8
!! Interface
!! L’interface d’un objet distant (Remote) est celle d’un objet
Java, avec quelques règles d’usage :
!! 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
!! Passage d’objets en paramètre
!! Les objets locaux sont passés par valeur (copie) et doivent
être sérialisables (étendent l’interface java.io.Serializable)
!! Les objets distants sont passés par référence et sont
désignés par leur interface
Java RMI : règles d’usage (2/2)
Java RMI : règles d’écriture du serveur
9
10
!! Implémentation des classes distantes (Remote)
!! Une classe distante doit implémenter une interface elle-
même distante (Remote)
!! Une classe distante doit étendre la classe
java.rmi.server.UnicastRemoteObject (d’autres
possibilités existent)
!! Une classe distante peut aussi avoir des méthodes
Advertisement
appelables seulement localement (ne font pas partie
de son interface Remote)
!! Un serveur est une classe qui implémente l’interface
de l’objet distant
!! Spécifier les objets distants qui doivent être implémentées
!! Définir le constructeur de l’objet distant
!! Fournir l’implémentation des méthodes appelables à distance
!! Créer et installer le gestionnaire de sécurité
!! Créer au moins une instance de la classe serveur
!! Enregistrer au moins une instance dans le serveur de noms
Le compilateur IDL : rmic
11
!! Le compilateur rmic génère les amorces (stub/
skeleton)
!! assurent le rôle d’adaptateurs pour le transport des appels
distants.
!! réalisent les appels sur la couche réseau.
!! réalisent l’emballage/déballage des paramètres
(sérialisation ou marshalling/unmarshalling).
!! Une référence d’objets distant correspond à une
référence d’amorce (stub).
Stub/Skeleton
12
!! Stub (talon client)
!! Représentant local de l’objet distant.
!! Initie une connexion avec la JVM distante
!! Assemble les paramètres pour leur transfert à la JVM distante.
!! Attend les résultats de l’invocation distante.
!! Désassemble la valeur ou l’exception renvoyée.
!! Renvoie la valeur au client (l’appelant).
!! Skeleton (talon serveur)
!! Désassemble les paramètres pour la méthode distante.
!! Fait appel à la méthode demandée.
!! Assemble le résultat (valeur renvoyée ou exception) à destination
de l’appelant.
Serveur de noms : rmiregistry
Étapes d’un appel de méthode distante
13
15
!! Permet d’obtenir une référence d’objet distant
!! S’exécute sur chaque machine hébergeant des objets distants
!! Utilise par défaut le port 1099
!! Accepte comme opérations l'enregistrement, l'effacement et la
consultation (méthodes de la classe java.rmi.Naming)
!! void Naming.bind(String nom, Remote obj) : associe l'objet au nom
spécifié;
!! void Naming.rebind(String nom, Remote obj) : réassocie le nom au nouvel
objet;
!! void Naming.unbind(String nom) : supprime l'enregistrement
correspondant;
!! Remote Naming.lookup(String nom) : renvoie la référence (stub) de
l'objet enregistré sous le nom donné;
!! String[] Naming.list(String nom) : renvoie la liste des noms enregistrés.
Développer une application avec RMI
Exemple : Inversion d’une chaîne de caractères
16
17
1. Définir une interface distante (App.java).
2. Créer une classe implémentant cette interface (AppImpl.java).
3. Compiler cette classe (javac AppImpl.java).
4. Créer une application serveur (AppServer.java).
5. Compiler l’application serveur.
6. Créer les classes stub et skeleton à l’aide de rmic:
AppImpl_Stub.java et AppImpl_Skel.java
7. Démarrage du serveur de noms (registre) avec rmiregistry.
8. Lancer le serveur AppServer pour la création d’objets et leur
enregistrement dans rmiregistry.
9. Créer une classe cliente qui appelle des méthodes distantes de
l’objet distant (AppClient.java).
10. Compiler cette classe et la lancer.
!! Invocation distante de la méthode reverseString() d’un objet
distant qui inverse une chaîne de caractères fournie par
l’appelant.
!! Classes nécessaires:
!! ReverseInterface : interface qui décrit l’objet distant
!! Reverse : qui implémente l’objet distant
!! ReverseServer : le serveur d’objets RMI
!! ReverseClient : le client qui utilise l’objet distant
Interface
19
!! Interface de l’objet
!! Permet de décrire les différentes méthodes de l'objet distant
!! Partagée par le client et le serveur.
!! Doit étendre l’interface Remote définie dans java.rmi.
!! Toutes ses méthodes doivent déclarer l’exception
RemoteException.
!! RemoteException est déclenchée si :
!! connexion refusée à l’hôte distant
!! l’objet n’existe plus,
!! un problème d’assemblage ou de désassemblage.
Interface
20
!
import java.rmi.Remote;!
import java.rmi.RemoteException;!
public interface ReverseInterface extends Remote !
{!
}!
!String reverseString(String chaine) throws RemoteException;!
Implémentation de l’objet distant
Implémentation de l’objet distant
21
22
!! Une classe qui implémente l'interface définie
!! Permettre de rendre un objet accessible à distance par des
clients.
!! Doit permettre l’exposition de l'objet distant:
!! La solution (la plus simple) consiste à étendre la classe
UnicastRemoteObject de java.rmi.server.
import java.rmi.*;!
import java.rmi.server.*;!
public class Reverse extends UnicastRemoteObject implements ReverseInterface {!
public Reverse() throws RemoteException!
! {!
! super();!
Advertisement
}!
! public String reverseString (String ChaineOrigine) throws RemoteException !
! {!
! int longueur=ChaineOrigine.length();!
! StringBuffer temp=new StringBuffer(longueur);!
! for (int i=longueur; i>0; i--) !
! {!
!
! }!
! return temp.toString();!
!temp.append(ChaineOrigine.substring(i-1, i));!
}!
}!
Serveur d’objets
Serveur d’objets
23
24
Les opérations réalisées par le serveur:
1.! Définir un gestionnaire de sécurité : Objet de la classe
RMISecurityManager qui autorisera le chargement depuis une
autre application de classes sérialisables:
System.setSecurityManager (new RMISecurityManager());!
!
2.! Créer les objets distants par instanciation de classes
Remote:
MonObjetDistant Objet = new MonObjetDistant;!
3.! Exposer les objets distants : rendre accessibles dans le
serveur
!! Si la classe de l'objet a été définie comme une extension de
UnicastRemoteObject, l’exposition est automatique.
!! Sinon, l'objet doit être exposé explicitement par appel de la
méthode exportObject de la classe UnicastRemoteObject:
UnicastRemoteObject.exportObject(unObjet);!
!
4.! Faire connaitre l'existence des différents objets au serveur de
noms (rmiregistry) de la machine sur laquelle ce serveur
s'exécutera
Naming.rebind("nomUnObjet", unObjet);!
!
Serveur d’objets
25
import java.rmi.*;
public class ReverseServer {
public static void main(String[] args) {
!System.setSecurityManager(new RMISecurityManager());
try { System.out.println( "Serveur : Construction de l’implémentation ");
Reverse rev= new Reverse();
System.out.println("Objet Reverse lié dans le RMIregistry");
Naming.rebind("rmi://localhost:1099/MyReverse", rev);
System.out.println("Attente des invocations des clients ...");
}
catch (Exception e) {
System.out.println("Erreur de liaison de l’objet Reverse");
System.out.println(e.toString()) }
}}
Le client (1/3)
26
!! Le client récupère le stub de l'objet : il s'adresse au
serveur de noms auprès duquel l'objet a été enregistré.
!! Cette opération est réalisée en invoquant la méthode
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é.
Le client (2/3)
27
28
!! La méthode lookup est susceptible de provoquer une exception
RemoteException.
!! La récupération d'un stub par un client a la forme générale suivante:
try { !
!
!MonInterface stub= (MonInterface) Naming.lookup (adresse_de_l'objet);!
!} !
catch (Exception e){-------- }!
!
!! Une fois le stub est récupéré, un client peut invoquer sur ce stub des
méthodes de l'interface comme il ferait sur un objet local:
objet=stub.méthode(paramètres);
Le client (3/3)
import java.rmi.*;!
public class ReverseClient {!
!public static void main (String [] args) {!
! System.setSecurityManager(new RMISecurityManager());!
! try{!
!
!("rmi://localhost:1099/MyReverse");!
!ReverseInterface rev = (ReverseInterface) Naming.lookup
!
!String result = rev.reverseString (args [0]);!
!System.out.println ("L’inverse de "+args[0]+" est "+result);!
!
!
!
! }!
! catch (Exception e) {!
!
!
}}}!
!System.out.println ("Erreur d’accès à l’objet distant.");!
!System.out.println (e.toString());!
Gestionnaire de sécurité
Gestionnaire de sécurité
29
30
!! Installer un gestionnaire de sécurité
!! Permet de protéger la machine virtuelle java de tout
utilisateur malintentionné qui essaye de faire tourner du
code malicieux sur la machine.
!! Utiliser le gestionnaire de sécurité fournit par RMI:
RMISecurityManager
Advertisement
System.setSecurityManager(new RMISecurityManager());
!! La politique de sécurité spécifie les actions
autorisées, en particulier sur les sockets
!! Les opérations possibles sur les sockets:
!! connect est utilisé pour établir une connexion avec un
serveur.
!! listen est utilisé par un serveur pour indiquer qu'il est
disposé à accepter des connexions.
!! Après un listen, un serveur peut attendre une demande
de connexion d'un client par accept
Gestionnaire de sécurité
Fabrique d’objets (Factory)
31
32
!! Exécuter le programme java utilisant un gestionnaire de sécurité
avec l’option :
java –D java.security.policy=java.policy NomClasse
Où java.policy et le fichier des permissions
!! Exemple de contenu du fichier java.policy:
grant {
permission java.net.SocketPermission "localhost 1024-65535","connect,accept";
permission java.net.SocketPermission !":80", "connect";
};
!! Phase préalable : création d’instances d’une classe d’objets
!! Notion de fabrique (factory)
Notion de référence d’objet :
Contient tout ce qui est nécessaire pour atteindre l’objet distant
(Localisation, protocole d’accès)
Fabrique d’objets (Factory)
Fabrique d’objets (Factory)
33
!! Motivation
34
!! Permettre au client de construire des instances multiples d’une classe C sur le site
serveur
!! Le new n’est pas utilisable tel quel (car il ne gère que la mémoire locale, celle
du client)
!! Solution : appel d’un objet FabriqueC, qui crée localement (sur le serveur) les
instances de C (en utilisant new C)
!! Exemple
/Interface de l’objet distant/
public interface Annuaire extends Remote{
public boolean inserer(String nom, int id) throws RemoteException;
public boolean supprimer(String nom, int id) throws RemoteException;
public Info rechercher(String nom) throws RemoteException;
}
/Interface de fabrique d’objets/
public interface FabAnnuaire extends Remote{
public Annuaire newAnnuaire(String titre) throws RemoteException ;
}
/*L’implementation de l’objet distant
public class AnnuaireImpl implements Annuaire extends UnicastRemoteObject{
private String titre;
public Annuaire(String titre) {this.titre=titre};
public boolean inserer(String nom, Info info) throws RemoteException{…};
public boolean supprimer(String nom) throws RemoteException{…};
public Info rechercher(String nom) throws RemoteException{…};
}
/*L’implémentation de la fabrique d’objets
public class FabAnnuaireImpl implements FabAnnuaire extends UnicastRemoteObject{
public FabAnnuaireImpl{};
public Annuaire newAnnuaire(String titre) throws RemoteException {
return new AnnuaireImpl(titre)};
}
Fabrique d’objets (Factory)
Fabrique d’objets (Factory)
35
/Serveur d’objets/
import java.rmi.*;
public class Server {
public static void main (String [ ] argv) {
/ lancer SecurityManager /
System.setSecurityManager (new RMISecurityManager ()) ;
try {
Naming.rebind ("Fabrique”,new (FabAnnuaireImpl)) ;
System.out.println ("Serveur prêt.") ;
}
catch (Exception e) {
System.out.println("Erreur serveur : " + e) ;
}
}
}
36
/Code Client/
import java.rmi.*;
public class HelloClient {
public static void main (String [ ] argv) {
/ lancer SecurityManager /
System.setSecurityManager (new RMISecurityManager ()) ;
try {
/ trouver une référence vers la fabrique /
FabAbannuaire fabrique = (FabAbannuaire ) Naming.lookup ("rmi://goedel.imag.fr/Fabrique") ;
/ créer un annuaire /
annuaireIMAG = fabrique.newAnnuaire("IMAG");
/ créer un autre annuaire /
annuaireINRIA= fabrique.newAnnuaire("INRIA");
/ utiliser les annuaires /
annuaireIMAG.inserer(…, …);
annuaireINRIA.inserer(…, …);
….
} catch (Exception e) {
System.out.println("Erreur client : " + e) ;
}}}
Fonctionnement d’ensemble de la fabrique
Passage d’objets en paramètres
37
38
Deux cas possibles
!! Passage en paramètre d’un objet local (sur la JVM de l’objet appelant)
!! Passage par valeur : on transmet une copie de l’objet (plus précisément : une
copie de l’ensemble ses variables d’état)
!! Pour cela l’objet doit être sérialisable (i.e. implémenter l’interface
java.io.Serializable)
!! Exemple de l’annuaire : le client transmet un objet de la classe locale Info
!! Passage en paramètre d’un objet non-local (hors de la JVM de l’objet
Advertisement
appelant, par ex. sur un site distant)
!! Passage par référence : on transmet une référence sur l’objet (plus précisément :
un stub de l’objet). Le destinataire utilisera ce stub pour appeler les méthodes
de l’objet.
!! Exemple de l’annuaire : le client passe un objet de type Annuaire (localisé sur le
serveur distant)
Passage d’objets en paramètres
Passage d’objets en paramètres
39
40
!! Notions sur les objets sérialisables
!! Un objet sérialisable (transmissible par valeur hors de sa
JVM) doit implémenter java.io.Serializable
!! Les objets référencés dans un objet sérialisable doivent aussi
être sérialisables
!! Comment rendre effectivement un objet sérialisable ?
"! Pour les variables de types primitifs (int, boolean,..) rien à faire
"! Pour les objet dont les champs sont constitués de telles variables :
rien à faire
"! On peut éliminer une variable de la représentation sérialisée en
la déclarant transient
"! Pour un champ non immédiatement sérialisable (ex. : Array), il faut
fournir des méthodes readObject() et writeObject()
!! Voir javadoc de l’interface Serializable et des classes
ObjectOutputStream et ObjectInputStream
Fonctionnement interne de JavaRMI
Fonctionnement interne de javaRMI
41
!! La classe UnicastRemoteObject
!! Rappel de la règle d’usage: une classe d’objets accessibles à distance étend
la classe java.rmi.UnicastRemoteObject
"! La principale fonction de cette clase se manifeste lors de la création d’une instance
de l’objet
public class AnnuaireImpl extends UnicastRemoteObject! {"…!}"
AnnuaireImpl! ("…!) { /* "le constructeur d’AnnuaireImpl appelle automatiquement
! "le constructeur de la superclasse UnicastRemoteObject*/
…
monAnnuaire! = "new AnnuaireImpl! ("…!) ;"
"! Le constructeur de UnicastRemoteObject crée une instance de stub pour l’objet, en utilisant la
classe Annuaire_Stub engendrée par rmic, 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
42
!! Le serveur de noms (registry)
!! Classes utiles (fournies par java.rmi)
"! Naming : sert de représentant local du serveurs de noms
"! Permet d’utiliser les méthodes bind(), rebind(), lookup(), unbind(), list()
"! LocateRegistry: permet de localiser un serveur de noms
(rmiregistry) et éventuellement d’en créer un. En général invisible
au client (appelé en interne par Naming)
Appel en retour (callback)
Appel en retour : principe
43
!! Le problème
!! Réaliser un appel du serveur vers le client
!! Intérêt
!! Augmenter l’asynchronisme
"! appel client-serveur avec retour immédiat (demande service)
"! rappel serveur-client quand le service est exécuté
!! Augmenter le dynamisme
"! Le serveur peut demander des informations complémentaires au
client en cours d’exécution
"! L’exécution du service peut faire appel à des services fournis par
le client
"! Exemple : traitement d’exceptions
44
!! Le client passe en paramètre au serveur l’objet à rappeler
!! Le serveur exécute un appel asynchrone sur cet objet
Appel en retour : réalisation
(interfaces des objets distants)
45
46
Appel en retour : réalisation (le servant)
Appel en retour : réalisation (le serveur)
Appel en retour : réalisation (le callback)
47
48
Appel en retour : réalisation (un client)
49
Conclusion
50
!! Extension du RPC aux objets
!! Permet l’accès à des objets distants
!! Pas de langage séparé de description d’interfaces (IDL
fourni par Java)
!! Limitations
!! Environnement restreint à un langage unique (Java)
!! Mais passerelles possibles, en particulier RMI/IIOP
!! Services réduits au minimum
!! Service élémentaire de noms (sans attributs)
!! Pas de services additionnels
"! Duplication d’objets
"! Transactions
Ce qui reste à voir
51
!! Parallélisme sur le serveur
!! Un serveur RMI peut servir plusieurs clients. Dans ce cas, un
thread séparé est créé pour chaque client sur le serveur.
C’est au développeur du programme serveur d’assurer leur
bonne synchronisation (exemple : méthodes synchronized)
pour garantir la cohérence des données
!! Activation automatique de serveurs
!! Un veilleur (demon) spécialisé, rmid, peut assurer le
lancement de plusieurs classes serveur sur un site distant
(cette opération est invisible au client)
!! Téléchargement de code
!! Un chargeur spécial (ClassLoader) peut être développé
pour assurer le téléchargement des classes nécessaires