Vous êtes sur la page 1sur 142

Les RMI

Master RSSI/Réseaux et Systèmes Répartis


1ème année (Semestre 2)
UDL-SBA

Dr BOUAMAMA Samah

2016/2017 1
Sommaire
• Rappel et Limites des RPC (Remote Procedure Call)
• Principe des RMI
• Etapes de développement et d ’exécution
• Paramètres des méthodes
• Objet Activable
• Personnalisation de la couche de transport
• Aspects avancés de RMI:

2
Rappel des RPC
• RPC (Remote Procedure Call)
• modèle client/serveur
• appel de procédures à distances entre un client et un serveur
• le client appelle une procédure
• le serveur exécute la procédure et renvoie le résultat
• la souche et le squelette ouvre un socket et encode/décode les paramètres
• Couche de présentation XDR (eXchange Data Representation)
format pivot de représentation des données de types primitifs et structurés (tableau de
longueur variable, structures) quelque soit
l ’architecture (Little Endian/Big Endian, IEEE, …)
le langage (ordre ligne/colonne dans les tableaux C et les tableaux Fortran)
ou le système (ASCII, IBM ’ECDCII, ...)

3
Limites des RPC
• Limitations
• paramètres et valeur de retour sont des types primitifs
• programmation procédurale
• dépendance à la localisation du serveur
• pas d’objet
• pas de « référence distante »
• Evolutions
• CORBA
• Multilangage, multi-plateforme (architecture+OS), MuliVendeurs
• Java RMI
• mono-langage : Java, multiplateforme : de JVM à JVM
• DCOM / Object RPC / .NET Remoting
• multi-langages, plateforme Win32 principalement, il existe des implémentations (non MicroSoft) pour Unix,
Propriétaire
• .NET Remoting
• multi-langages (CLR), plateforme Win32 principalement
• Normalisé à l’ECMA et à l’ISO
• SOAP (Simple Access Object Protocol)
• multi-langages, multi-plateforme
• Réponse et requête en XML (DTD SOAP), Transport sur HTTP, IETF
4
Java RMI
Remote Method Invocation

Permet la communication entre machines virtuelles Java (JVM)


(Homogène) qui peuvent se trouver physiquement sur la même
machine ou sur deux machines distinctes.

5
Présentation

RMI est un système d’objets distribués constitué uniquement d’objets java ;

• RMI est une Application Programming Interface (intégrée au JDK 1.1 et plus à
partir de 1995) ;

• Développé par JavaSoft ;

6
Présentation
• Mécanisme qui permet l’appel de méthodes entre objets Java qui
s’exécutent éventuellement sur des JVM distinctes ;

• L ’appel peut se faire sur la même machine ou bien sur des machines
connectées sur un réseau ;

• Utilise les sockets ;

• Les échanges respectent un protocole propriétaire : Remote Method


Protocol ;

• RMI repose sur les classes de sérialisation.


7
Principes des RMI
• RPC à la java
• Invoquer de façon simple des méthodes sur des objets distribués.
• Outils
• Pour la génération des stub/skeleton, l ’enregistrement par le nom et l ’activation
• Tous les détails ( connexion, transfert de données ..) sont transparents pour le développeur grâce au
stub/skeleton généré
• Mono-langage et multiplateforme.
• Java : de jvm à jvm (les données et objets ont la même représentation qqs la jvm)
• Orienté objet
• Les rmis utilisent le mécanisme standard de sérialisation de JAVA pour l ’envoi d ’objets.
• Dynamique
• Les classes des stubs et des paramètres peuvent être chargées dynamiquement via HTTP (http://) ou
NFS (file:/)
• Sécurité
• Un securitymanager vérifie si certaines opérations sont autorisés par le serveur

8
RMI pour rendre transparents les échanges

• Sans RMI : Le concepteur de l’application fait tout le travail de transmission


• Tâche de programmation lourde, dangereuse (nombreuses sources d’erreur)
• Avec RMI : mise en œuvre automatique du protocole de communication

9
Java RMI
Architecture
Objet Client Objet Serveur

Proxy Skeleton

JVM Remote Reference Layer JVM

Transport

10
Structure des couches RMI
l ’architecture logique
Client RMI Serveur RMI
(Application / Applet / Servlet) HelloServer, rmid
HelloClient,HelloApplet
Invocation de
Interface Distante méthodes Implémentation Distante
Hello sayHello(...) HelloImpl

Souche ou Stub Squelette ou Skeleton


HelloImpl_Stub HelloImpl_Skel

Couche de Référence Couche de Référence


java.rmi.Naming java.rmi.Naming

Couche de Transport Couche de Transport


java.net.Socket pour TCP java.net.SocketServer pour TCP
Réseau
(IP)

11
La couche des souches (Amorces)

• Constitue l’interface application /Système RMI.


• Ceux sont des classes qui encapsulent et masquent toute la complexité des
traitements liés à l’invocation des méthodes distantes.
• Prend en charge le codage des arguments d’appel et de retour leur transmission
sur le réseau.
• Comporte les souches serveur implémentant toutes les interfaces de l’objet distant.
• Soumet les données à la couche des références distantes sous forme de flots de
données entrants et sortants, après sérialisation.

12
La couche des souches (Amorces)
• Souche ou Stub (sur le client)
– Représentant local de l’objet distant qui implémente les méthodes “exportées” de l’objet
distant.
– “Marshalise” les arguments de la méthode distante et les envoie en un flot de données
au serveur
– “Démarshalise” la valeur ou l ’objet retournés par la méthode distante
– La classe xx_stub peut être chargée dynamiquement par le client (applet)

• Squelette ou Skeleton (sur le serveur)


– “Démarshalise” les paramètres des méthodes
– Fait un appel à la méthode de l’objet local au serveur
– “Marshalise” la valeur ou l ’objet renvoyé par la méthode

13
Couche des références distantes Remote
Reference Layer
– Traduit la référence locale au stub en une référence à l’objet distant.

– C’est une couche intermédiaire entre le Stub, le Skeleton et les protocoles de


communication (gérés par la couche de transport).

– Elle est servie par un processus tier : rmiregistry, qui est unique par JVM et possède
une table de hachage dont les clés sont des noms et les valeurs sont des objets
distants), il s ’exécute sur chaque machine hébergeant des objets distants ;

– Dans la version 1.1 de JDK, cette couche connecte les clients aux objets distants par
une connexion de type un à un (Unicast). Avant qu’un client ne puisse utiliser les
services d’un objet distant, le service doit être instancié sur le serveur et exporté au
système RMI.

– A partir de JDK 1.2 la gestion des objets activables a été rajoutée à cette couche. 14
La couche transport
- Ecoute les appels entrants;

- Etablit et gère les connexions avec les sites distants;

- java.rmi.UnicastRemoteObject utilise les classes Socket et SocketServer (TCP)


cependant d ’autres classes peuvent être utilisées par la couche transport (Compression sur TCP,
SSL sur TCP, UDP);

- Les connexions sont basées sur les adresses IP et les numéros de port. Un nom DNS peut être
utilisé à la place de l’adresse IP.

- En parallèle avec TCP/IP, RMI utilise un protocole propriétaire de bas niveau appelé Java
Remote Method Protocol (JRMP). Deux versions de JRMP existent, la première est associée à la
aversion 1.1 de JDK. Elle oblige l’utilisation d’un Skeleton du côté serveur. La deuxième version
est apparue avec la version 1.2 de JDK et élimine l’utilisation du Skeleton (en utilisant la
réflexion). Le compilateur rmic peut généré les classes de la couche Proxy selon les deux
versions (avec le paramètre –v1.1 ou –v1.2).
15
Structure d’une application RMI
Remote Machine

bind
• Le serveur lie son nom à un objet RMI Server

Registry Registry
• Le client recherche le nom du serveur skeleton
et établie une connexion (lookup).
• Le talon (Stub) sérialise les paramètres return call lookup
au squelette,
• Le squelette invoque la méthode
distante et sérialise le résultat en retour stub
vers le talon.
RMI Client

Local Machine
16
Etapes d’un appel de méthode distante

Client Serveur
Serveur
de noms
3. Interroger
2. Publier

1. Exposer Objet
4. Récupérer distant

Serveur d’objets
7. Appel méthode
6. Arguments
5. Appel de méthode sérialisation

Stub
10. Retour 8. Retour
9. Résultat

17
Création et manipulation d'objets distants

• 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.

18
Création et manipulation d'objets distants
Etapes du développement :
1- Spécifier et écrire l'interface de l'objet distant.
2- Ecrire l'implémentation de cette interface.
3- Ecrire le serveur qui instancie l'objet implémentant
l'interface, exporte son Stub puis attend les requêtes via le
Skeleton.
5- Ecrire le client qui réclame l ’objet distant, importe le
Stub et invoque une méthode de l'objet distant via le
Stub.
19
Classes et interfaces utilisées par Java RMI

20
1- Spécifier l’interface d'un objet distant
• 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 (nom, types de retours, 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, dérivant de la classe java.rmi.Remote.

• Doit être publique.

• Chaque méthode de l’interface doit émettre l’exception java.rmi.RemoteException qui


peut se produire si la connexion a été interrompue ou si le serveur distant ne peut être
trouvé.
21
1- Spécifier l’interface d'un objet distant

Exemple:
package examples.hello;
public interface Hello extends java.rmi.Remote{
String sayHello() throws java.rmi.RemoteException;
}

22
2- Implémentation de l’objet distant
• Pour pouvoir être exporté (accepter les requêtes, écouter sur une socket
TCP), un objet doit implémenter l’interface Remote. Il y a alors deux
possibilités pour être exporter:
1. Hériter de la classe java.rmi.server.UnicastRemoteObject. L’appel
au constructeur exportera automatiquement l’objet courant.
2. Appeler la méthode exportObjet() de la classe
UnicastRemoteObject sur l’objet à exporter.
• Lorsqu’un objet est exporté un ensemble de threads est créé pour
attendre les appels de méthodes.
• java.rmi.activation.Activable est utilisée (à la place de
UnicastRemoteObject) pour l’activation d’objets distants.
• Le serveur peut avoir des méthodes locales, absentes de l’interface
distante.
23
2- Implémentation de l ’objet distant

package examples.hello;
public class HelloImpl extends java.rmi.server.UnicastRemoteObject implements Hello {
private String message;
public HelloImpl(String s) throws java.rmi.RemoteException{
super();
message=s; }
public String sayHello() throws java.rmi.RemoteException {
return message ; }

24
2- Implémentation de l ’objet distant
en utilisant UnicastRemoteObject.expotObject

package examples.hello;
public class HelloImpl implements Hello {
private String message;
public HelloImpl(String s) throws java.rmi.RemoteException{
UnicastRemoteObject.expotObject(this);
message=s; }
public String sayHello() throws java.rmi.RemoteException{
return message;
}

25
3- Implémentation du serveur de l’objet distant
• Crée un ou plusieurs objets distants (une ou plusieurs instances)
• Naming.bind() : les enregistre auprès du serveur (registre des noms ) de liaison
rmiregistry, qui gère les associations entre noms symboliques et références d’objets.
• Utilise java.rmi.registry.Registry et la classe Naming.
• Cette classe manipule des noms qui sont des URL de la forme rmi: //hôte :port/nom.
• Où rmi, hôte et port sont optionnels, localhost et 1099 sont par défaut.
Exemple:
 rmi://hostname:4242/Odname
 rmi://:4242/Odname
 //:4242/Odname
 /Odname
 Odname

26
3- Implémentation du serveur de l’objet distant
• La classe Namming comporte les méthodes suivantes:
1. static void bind (String nom, Remote obj):associe un nom à l’objet obj.
Exp: Naming.bind("Hello1", new HelloImpl("Hello world") );
2. static void rebind (String nom, Remote obj):réassocie un nom à l’objet obj.
3. static void unbind (String nom):désassocie un nom à l’objet obj. Elle permet
de désactiver le serveur.
4. static Remote lookup (String nom):renvoie l’objet Remote associé au nom.
Exp: Naming.lookup("rmi://localhost/Hello1");
5. Static String[] list(): renvoie le tableau des noms présents dans l’annuaire.

27
3- Implémentation du serveur de l’objet distant

Remarques:
• On ne peut utiliser bind ou unbind sur un rmiregistry, que
depuis une VM sur le même host (sécurité).
• On peut utiliser le lookup depuis n’importe quel host.
• L’enregistrement dans l’annuaire est bloquant. Le serveur reste
en attente de requêtes.
• L’utilisation de bind ou rebind implique la création de deux
threads, l’un exécutant le bind (rebind), l’autre continuant
l’exécution de l’application.
• Pour désactiver un serveur, on peut utiliser unbind.
28
3- Implémentation du serveur de l’objet distant

Remarques:
• L ’objet distant servi peut être d’une sous classe de HelloImpl
public class HelloImpl extends HelloImpl {
public HelloImpl() throws java.rmi.RemoteException{ super(Hello); }
}
// et dans HelloServer
java.rmi.Naming.rebind("Hello1", new HelloImpl());

• Le serveur peut créer et enregistrer plusieurs objets appartenant à une


ou plusieurs classes.

• l ’enregistrement peut se faire auprès de plusieurs rmiregistry.


29
3- Implémentation du serveur de l’objet distant
Exemple:
Import java.rmi.*;
package examples.hello;
public class HelloServer {
public static void main(String args[]) { // argument : l ’hôte/port du rmiregistry
try {
// instancie l ’objet
HelloImpl obj = new HelloImpl("Hello World");
// Enregistre l ’objet sous le nom " Hello1" auprès de rmiregistry
Naming.bind(" Hello1", obj);
System.out.println("Sereveur prêt:");
} catch (Exception e) { e.printStackTrace(); }
}}

30
4- Implémentation d’un client invoquant l’objet distant

• Demande un stub auprès du serveur de liaison rmiregistry


• Invoque des méthodes sur le stub chargé

Exemple (Application):
Import java.rmi.*;
package examples.client;
public class HelloClient {
public static void main(String args[]) {
String message = "blank";
try { // récupère le stub de l’objet enregistré au nom de «Hello1»
Hello obj = (Hello) Naming.lookup("rmi://localhost/Hello1");
message = obj.sayHello();
System.out.println(message);
} catch (Exception e) { e.printStackTrace(); }
}}
31
4- Implémentation d ’un client invoquant l ’objet distant

Exemple (Applet)
import java.rmi.Naming;
public class HelloApplet extends java.applet.Applet{
String message = "blank";
public void init() {
try { // récupère le stub de l ’objet enregistré au nom de « HelloObject »
// Remarque : rmiregistry et le serveur Web doit être sur la même machine (même #IP)
Hello obj = (Hello)Naming.lookup("//" + getCodeBase().getHost() + "/HelloObject");
message = obj.sayHello();
} catch (Exception e) { // sortie d ’erreur sur la console
System.out.println("HelloApplet exception:" + e.getMessage()); e.printStackTrace();
}
}
public void paint(java.awt.Graphics g) { g.drawString(message, 25, 50); }
}
32
Déploiement avant compilation
Chez le serveur:
Chez le client:
Hello.java
Hello.java
HelloImpl.java
HelloClient.java
HelloServer.java

• Il faut compiler toutes les classes du coté serveur et client par la commande javac
« nom_de_la_classe».
• Créer les talons client et serveur par la commande rmic HelloImp, celle-ci génère
HelloImp_Stub.class (talon client) et HelloImp_Skel.class (talon serveur).
• A partir de jdk 1.2 le skeleteon n’est plus indispensable.
• A partir de jdk 1.5 le stub n’est plus indisponsable (donc rmic).

33
Déploiement avant compilation

Chez le client: Chez le serveur:


Hello Hello
HelloClient HelloImpl
HelloImpl_Stub Pour un jdk < 1.5 HelloServer
HelloImpl_Stub Pour un jdk < 1.5
HelloImp_Skel Pour un jdk <1.2

34
L ’exécution Coté Serveur
• Lancer le serveur de noms (le port de liaison par défaut
est le port TCP 1099, doit être >=1024)
• rmiregistry & (sur unix)
• start rmiregistry (sous windows)

• Lacer le serveur : C:\Serveur >java HelloServer


Server prêt:

35
L ’exécution coté client
• Lancer le client : C:\Client >java HelloClient
Hello Wolrd
C:\Client>

36
L ’exécution Coté Client
• L’applet
• l ’élément HTML applet doit spécifier le codebase
<HTML><title>Hello World</title><center> <h1>Hello World</h1> </center>
The message from the HelloServer is:<br>
<applet codebase="myclasses/"
code="examples.client.HelloApplet" width=500 height=120>
</applet> </HTML>
• seuls les sockets vers hostwww sont autorisés par la sandbox
• donc hostreg = hostser = hostwww

hostcli> appletviewer http://hostwww/hello/hello.html

37
Résumé - Java RMI Manuel d'utilisation

• Définition de l'interface de l'objet réparti


• interface : "extends java.rmi.Remote"
• méthodes : "throws java.rmi.RemoteException"
• paramètres sérializable : "implements Serializable"
• paramètres référence : "implements Remote"
• Ecrire une implémentation de l'objet serveur
• classe : "extends java.rmi.server.UnicastRemoteObject"

38
Résumé - Java RMI - Mode opératoire
• codage
• description de l’interface du service
• écriture du code du serveur qui implante l’interface
• écriture du client qui appelle le serveur
• compilation
• compilation des sources (javac)
• génération des stub et skeleton (rmic)
• activation
• lancement du serveur de noms (rmiregistry)
• lancement du serveur
• lancement du client

39
Aspects avancés des RMI
1- Activation du serveur de noms par le serveur
2- Sécurité
3- Le passage de paramètres
4- Mécanisme du ramasse-miettes distribué
5- RMI et le multithread
6- Callbacks (appels en retour)
7- Fabriques (usines) d’objets
8- Chargement dynamique des classes
9- Activation d’objets
10- Sockets personnalisés et SSL, RMI face aux pare-feux

40
L ’activation du serveur de noms par le serveur

Serveur de noms démarrable:


• De façon autonome dans un shell avec l'outil rmiregistry
• Dans un programme par appel de la méthode
static java.rmi.registry.LocateRegistry.createRegistry(int port)

41
L ’activation du serveur de noms par le serveur
import java.rmi.*;
import java.net.*; /*fichier HelloServeur.java*/
public class AddServer2{
public static void main(String[] argv) {
int port;
try {// transformation d ’une chaîne de caractères en entier
Integer I = new Integer(argv[0]);
port = I.intValue();
} catch (Exception ex) { System.out.println(" Donner: Server <port>"); return; }
try { // Création du serveur de nom - rmiregistry
java.rmi.registry.LocateRegistry.createRegistry(port);
InetAddress adrLocale=InetAddress.getLocalHost();
Naming.rebind(‘’//:’’+port+’’/Additionneur’’,new AddImpl());
System.out.println(‘’Serveur attend sur= rmi://’’+adrLocale.getHostAddress()+getHostName()+’’:’’+port+’’/Additionneur’’);
System.out.println(‘’Equivalent à = rmi://’’+InetAddress.getLocalHost().getHostName()+’’:’’+port+’’/Additionneur’’);
} catch (Exception e) {System.out.println(‘’Erreur serveur:’’+e);} } }
42
L ’activation du serveur de noms par le serveur

Après compilation et déploiement l’activation du serveur et du client :

• Côté serveur:
Lancer le serveur: java HelloServeur

• Côté client:
Lancer le client: java HelloClient

43
Sécurité
• Accepter des connexions réseau de machines distantes ou exécuter
du code téléchargé peut être dangereux.
• Si aucun Security Manager n’est installé, alors, il faut obligatoirement
mettre à la disposition du serveur et du client toutes les classes.
Seules les classes accessibles depuis le CLASSPATH peuvent être
chargées.
• Avec un Security Manager, le serveur et le client peuvent charger
dynamiquement certaines classes.

44
Sécurité
• Une façon de charger un Security Manager s’il n’y en a pas déjà:

if (System.getSecurityManager() == null) {
System.setSecurityManager(new java.rmi.RMISecurityManager()); }

• La spécification des actions autorisées passe par la définition d’un fichier de sécurité:
nomfichier.policy, exemple: java.policy.

• Sans ce fichier, les connexions externes seront refusées.

45
Sécurité
• Exemple de java.policy:
grant{
permission java.net.SocketPermission « 1024-65535", "connect, accept, resolve";
// This allows RMI clients to make network connections to the public ports on any host
permission java.net.SocketPermission "*:1099", "connect, accept , resolve";
// This allows RMI clients to contact the RMIRegistry of any host
};

grant{
permission java.net.SocketPermission « 1024-65535", "connect, accept";
permission java.net.SocketPermission "*:80", "connect";
// This allows connection to the default web server on any host
//needed for stub downloading, among other things.
};

grant{
permission java.security.AllPermission; //Ceci est dangereux!!
}; 46
Sécurité
Programme du serveur: Exemple Hello World

Import java.rmi.*;
package examples.hello;
public class HelloServer {
public static void main(String args[]) { // argument : l ’hôte/port du rmiregistry
System.setSecurityManager(new RMISecurityManager());
// lance le Security Manager
try {
HelloImpl obj = new HelloImpl("Hello World");
Naming.bind(" Hello1", obj);
System.out.println("Sereveur prêt:");
} catch (Exception e) { e.printStackTrace(); }
}}

47
Sécurité
Programme du Client: Exemple Hello World
Import java.rmi.*;
package examples.client;
public class HelloClient {
public static void main(String args[]) {
System.setSecurityManager(new RMISecurityManager());
// lance le Security Manager
try {
Hello obj = (Hello) Naming.lookup("rmi://localhost/Hello1");
System.out.println(obj.sayHello());
} catch (Exception e) { e.printStackTrace(); }
}}

48
Sécurité: Exécution
Lancer le serveur:
java - Djava.security.policy=java.policy HelloServer

Lancer le client:
java - Djava.security.policy=java.policy HelloClient

Le fichier java.policy doit se trouver dans le répertoire courant du serveur et du client (il
n’y a pas eu de chargement dynamique de code).

49
Sécurité: Exécution
On peut ne pas placer un SecurityManager au niveau du server et le
prévoir au moment du lancement du rmiregistry:

rmirestry - j- Djava.security.policy=java.policy

50
Le passage de paramètres

Type des arguments et des résultats

• Pratiquement tous les types d'arguments et de résultats sont acceptés


• Types primitifs
• Objets distants
• ceux qui implémentent java.rmi.Remote
• Types non-primitifs sérialisables
• ceux qui implémentent java.io.Serializable

51
Le passage de paramètres

Mode de passage de paramètre et de résultat


• Objets Distants (Remote)
• Passage par référence (stub)
• Seules les méthodes de l'interface Remote sont disponibles
• Les modifications sont faites sur l'objet original
• Objets locaux
• Passage par copy (à l'aide de la sérialisation)
• Les modifications sont faites sur la copie

52
Le passage de paramètres
• L’argument ou la valeur de retour d’une méthode distante peut être de
n’importe quel type Java: type simple, objet local ou objet distant.
• Pour les paramètres locaux (objets ou type primitifs), le transfert se fait
nécessairement par copie.
• S’il s’agit d’un objet (X), l’ensemble de ses variables est copié par
sérialisation, il doit donc implémenter java.io.Serializable. La déclaration
de la classe X est:
Public class X implements java.io.Serializable{…..}
La classe X doit se trouver chez le client et chez le serveur.

53
Le passage de paramètres
Exemple : Paramètre de type objet local

import java.rmi.*; import java.io.Serializable;

public interface AddInterface extends Remote public classe Matrice implements Serializable
{public Matrice add(Matrice a, Matrice b) { public int m[][] =new int [][];
Throws RemoteException;} public void remplirmatrice()
{…}

public void affichermatrice()


{….}
}
54
Le passage de paramètres

Paramètre de type objet distant

• Les objets distants sont passés par référence et sont


désignés par leur interface. Le stub est réalisé et envoyé au
client (impossible de passer en paramètre un objet
uniquement visible par les classes serveurs).

• Il faut simplifier les arguments et les valeurs de retour des


objets distants car la sérialisation consomme du temps.

55
Le passage de paramètres
Exemple: Paramètre de type objet distant
Fichier I1.java
import java.rmi.*
public interface I1 extends Remote {
Public l2 getI2() throws RemoteException;
}

Fichier I2.java
import java.rmi.*
public interface I2 extends Remote { }

56
Le passage de paramètres
Exemple: Paramètre de type objet distant
Fichier Client.java
import java.rmi.*
public class client{
public static void main(String args[]){
try{
I1 obj= (I1) Naming.lookup(’’rmi://1050’’+ args[0]+’’/Hello’’);
I2 msg= obj.getI2();
}catch (Exception e)
{System.out.println(’’Client exception’’+e);}}}

Remarque: les deux interfaces I1 et I2 doivent se trouver chez le client et chez le


serveur, leurs implémentations uniquement chez le serveur.

57
Résumons
• Toute variable primitive est passé par copie.
• Tout objet est passé par copie.
• Mais comment copier un objet?
• Il ne faut pas copier que l’objet.
• Il faut aussi copier toutes ses références.
• Très fastidieux à faire à la main.
• Heureusement, une API le fait pour nous: la Serialization.

• Toute objet distant (abus de langage, on devrait dire référence distante)


est passé par référence.
61
La Sérialisation
• Mécanisme générique transformant un graphe d’objets en flux d’octets
– L’objet passé en paramètre est converti en tableau
– Ainsi que tout ceux qu’il référence
– Processus récursif (copie profonde)
• Fonctionnement de base
– Encode le nom de la classe
– Encode les valeurs des attributs

Serialization

62
La Désérialisation

• Processus symétrique de la sérialisation


– Prend en entrée un flux d’octets
– Crée le graphe d’objet correspondant
• Fonctionnement de base
– Lis le nom de la classe
– Fabrique un objet de cette classe
– Lis les champs dans le flux, et met à jour leur valeur dans la nouvelle instance

Deserialization

63
Gestion des cycles
• La sérialisation est un processus récursif
• Que se passe-t-il quand il y a un cycle dans les graphe d’objets?

• Un algorithme naïf bouclerait à l’infini


• Solution:
– Repérer les cycles
• La sérialisation se souvient des objets déjà sérialisés
– Si on veut en sérialiser un à nouveau, alors met juste une référence
64
Gestion des cycles
• Le flux contient donc 3 types d’information
– Le nom de la classe
– Les valeurs des attributs
– Des références vers d’autres parties du flux

3
1 Serialization
2 4

1 2 3 4 Ref2

65
Utiliser la sérialisation
• Par défaut, un objet n’est pas sérialisable
– Problème de securité: la sérialisation ignore les droits d’accés (private…)
– Levée d’une NotSerializableException
• Il faut donc explicitement indiquer qu’un objet est sérialisable
• Marquage au niveau de la classe
– Toutes les instances seront sérialisable
– Les sous classes d’une classe sérialisable sont sérialisable
• Utilisation de l’interface java.io.Serializable
– Interface marqueur, aucune méthode à implémenter

66
Utiliser la sérialisation
• RMI fait appel à la sérialisation
– Totalement transparent
• Mais on peut aussi l’utiliser manuellement
– Très pratique pour copier des objets
• Étapes
– Bien vérifier que les objets sont sérialisables
– Créer des flux d’entrée et de sortie (input et output streams)
– Utiliser ces flux pour créer des flux objets (object input et object output streams)
– Passer l’objet à copier à l’object output stream
– Le lire depuis l’object input stream

67
Contrôler la sérialisation

• Marquer une classe avec l’interface Serializable indique que tout


ses champs seront sérialisés
• Pas forcément acceptable
– Sécurité
– Efficacité (pourquoi copier ce qui pourrait être recalculé plus rapidement?)
• Possibilité de contrôle plus fin
– Marquage d’attributs comme étant non sérialisables: mots clé transient
– Donner à un objet la possibilité de se sérialiser

69
Contrôler la sérialisation

• Pour modifier la sérialisation par défaut, il faut implémenter 2 méthodes


– writeObject() : sérialisation
– readObject() : désérialisation
• Leur signature est
– private void writeObject(ObjectOutputStream s) throws IOException
– private Object readObject(ObjectInputStream o) throws ClassNotFoundException,IOException
• Elles seront automatiquement appelées et remplaceront le comportement par
défaut
• On écrit dans ces méthodes du code spécifique à l’objet

70
Contrôler la sérialisation

• Dans les méthodes readObject/writeObject il est possible de tout faire


– pas de limitation théorique
– Manipulation/modification des attributs de l’objet possibles
• Basé sur les flots (streams de java.io)
– Implémentation FIFO
– Donc lecture dans le même ordre que l’écriture
• Symétrie
– Normalement, lire tout ce qui a été écrit
– En pratique, RMI délimite le flux et évite les mélanges

71
Écriture - Lecture

• Utilisation des méthodes de ObjectOutputStream et ObjectInputStream


– Types primitifs
• {write|read}Double, {write|read}Int…
– Objets
• {write|read}Object
• Provoque une nouvelle serialization
• Possible de rappeler l’ancienne implémentation
– Méthodes defaultWriteObject() et defaultReadObject() des streams
– Très pratique pour ajouter une fonctionnalité

72
Exemple: comportement par défaut
public class Defaut implements Serializable {
public Defaut() { }

private void writeObject(ObjectOutputStream s) throws IOException {


s.defaultWriteObject();
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {


s.defaultReadObject();
}
}

73
Exemple: sauvegarde d’un entier

public class Defaut implements Serializable {


private int valeur;
public Defaut() { }

private void writeObject(ObjectOutputStream s) throws IOException {


s.writeInt(valeur);
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {


valeur = s.readInt();
}
}

74
Sérialisation et héritage
• Les sous classes d’une classe sérialisable sont sérialisables
• Mais une classe peut-être sérialisable, alors que son parent ne
l’est pas
– La sous-classe est responsable de la sauvegarde/restauration des
champs hérités
– Lors de la désérialisation, les constructeurs sans paramètres seront
appelés pour initialiser les champs non sérialisables
– Le constructeur vide de la classe parent sera appelé
• Source de bugs très difficiles à identifier
75
Mécanisme du ramasse-miettes:
Cas d’un objet local
• Un objet local est considéré vivant si au moins une variable le
référence.

• Quand toutes les références locales sont perdues l’objet est


candidat au ramassage. La méthode gc() de la classe System peut
forcer l’activation du GC (GARBAGE Collector).

• Si un objet implante la méthode finalize(), elle est appelée par le GC


en cas de restitution de la mémoire allouée par cet objet.
76
Exemple de fonctionnement du GC:
Cas d’un objet local
Public class Objet
{private static int nbreObjets;//par défaut initialisé à 0

public objet()
{nbreObjets++;
System.out.println(‘’je suis l’objet’’+nbreObjets);} Une exécution possible:
Je suis l’objet 1
Protected void finalize() {nbreObjets--;} Je suis l’objet 2
} Je suis l’objet 3
Je suis l’objet 3
Je suis l’objet 3
Public classe TestObjet
{ public static void main (String[]args)
{Objet o;
For (int i=0; i<5;i++)
{o=new Objet();//l’objet précédemment référencé par o devient candidat au ramasse-miettes
System.gc();} //force le Ramasse-miettes à libérer la mémoire 77
}}
Mécanisme du ramasse-miettes:
Cas d’un objet distant
Le DGC interagit avec le GC locaux et utilise deux mécanismes reference-
counting et lease periode:

Concernant le reference-counting:

• Lorsqu’un OD est référencé (est passé en paramètre à un autre OD )


ref_count++.
• Lorsqu'un stub n'est plus référencé weak reference
• Lorsque le GC du client libère le stub, sa méthode finalize est appelée et informe
le DGCref_count.
• Lorsque le nombre de référence d’un OD= 0 (ref_cont=0 et stub n’est plus
référencé), on parle de weak reference; le GC du serveur peut alors libérer
l’OD.
• Si référence à un OD libéréeRemoteException 78
Mécanisme du ramasse-miettes:
Cas d’un objet distant
Concernant la lease-perdiod:
• Lorsqu’un client obtient une référence vers un OD, il obtient un
bail pour cet objet pour un certain temps (lease period) qui est par
défaut 10mn. Si le client ne renouvelle pas d’appels vers l’OD
avant l’expiration du bail, le mécanisme du DGC suppose que
l’objet n’est plus référencé et devient donc candidat au ramasse
miettes.
• On peut modifier la valeur du bail par:

-Djava.rmi.dgc.leaseValue=1000 (bail=1sc)
java -Djava.rmi.dgc.leaseValue=1000 HelloServer 79
Mécanisme du ramasse-miettes:
Cas d’un objet distant
• Si la valeur du bail est très petite, le serveur lui-même peut se
terminer avant que le client n’est eu le temps de le référencer.

• Si un objet distant implante l’interface Unreferenced, la méthode


Unreferenced de cette interface est invoquée quand toutes les
références distantes sont perdues.

• Pour empêcher un objet de disparaître, il faut maintenir une référence


explicite vers l’objet.

• L’intérêt du compteur apparait dans le cas où l’objet est relâché avant


80
l’expiration du délai.
Mécanisme du ramasse-miettes distribué
Exemple de Hello World
Import java.rmi.*;
Import java.rmi.server.*;
public class HelloImpl extends UnicastRemoteObject implements Hello, Unreferenced {
private String message;
public HelloImpl(String s) throws RemoteException{
message=s; }
public String sayHello() throws RemoteException {
return message;
}
Public void finalize() {System.out.println (‘’Fin d’un objet local’’);}
Public void unreferenced() {System.out.println(‘’Fin d’un objet distant’’);}
}

81
Exécution Hello World

Chez le serveur:
java –Djava.rmi.dgc.leaseValue= 1000
HelloServer

Serveur Prêt:
Fin d’un objet distant
Fin d’un objet local
C:\Serveur>

Remarques: après 1000 ms, le client n’a toujours pas été lancé.
La présence des stubs est nécessaire et donc rmic HelloImpl (même avec java 1.5)
82
Mécanisme du ramasse-miettes distribué
Exemple pour un objet distant
import java.rmi.*;
public interface Hello extends Remote{
public Msg getMsg() throws Remote Exception;
}

import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements Hello {
public HelloImpl() throws RemoteException{
super();
}
public Msg getMsg() throws RemoteException {return (Msg)new MsgImp();}
83
}
Mécanisme du ramasse-miettes distribué
Exemple pour un objet distant
import java.rmi.Remote;
public interface Msg extends Remote{ }

import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.Unreferenced;
public class MsgImpl extends UnicastRemoteObject implements Msg, Unreferenced {
public static int couner;
private int id;
public MsgImpl() throws RemoteException{
super();
this.id=++MsgImpl.counter;
System.out.println(’’ Message created:’’+MsgImpl.counter);
}
Public void unreferenced() {
System.out.println(’’ Methode unreferenced() called by’’+this.id); 84
}}
Mécanisme du ramasse-miettes distribué
Exemple pour un objet distant
import java.rmi.Naming;
public class Server{
public static void main(String args[ ]) {
String host=‘’ ‘’;
try {
if (args.length==0) {host=‘’localhost’’;} else {host=args[0];}
Hello obj=new HelloImpl();
System.out.println (‘’HelloImpl instanciated.’’);
Naming.rebind(‘’rmi://’’ +host+ ‘’/Hello’’,obj);
System.out.println (‘’HelloImpl registred.’’);
}catch (Exception e){
System.out.println(e);
}}} 85
Mécanisme du ramasse-miettes distribué
Exemple pour un objet distant
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
public class Client{
public static void main(String args[ ]) {
String host=‘’ ‘’;
try {
if (args.length==0) {host=‘’localhost’’;} else {host=args[0];}
Hello obj=(Hello) Naming.lookup(‘’rmi://’’ +host+ ‘’/Hello’’);
for(int i=0;i<3;i++){
Msg msg=obj.getMsg();
}
}catch (Exception e){
System.out.println(‘’Client exception: ’’+e); 86
}}}
Mécanisme du ramasse-miettes distribué
Exemple pour un objet distant
java –Djava.rmi.dgc.leaseValue= 100 Server
HelloImpl instanciated.
HelloImpl registered.
Message created:1
Message created:2
Methode unreferenced() called by 2
Methode unreferenced() called by 1
Message created:3
Methode unreferenced() called by 3
C:\Serveur>

Remarques: La présence des stubs HelloImpl_stub est nécessaire et donc rmic HelloImpl (même avec java 1.5).
Pour avoir ces affichages, le client doit être lancé dans les 100 ms.
87
Mécanisme du ramasse-miettes distribué
Exemple pour un objet distant

java –Djava.rmi.dgc.leaseValue= 100 Server

HelloImpl instanciated.
HelloImpl registered.
C:\Serveur>

Remarques: Après100 ms, le client n’a toujours pas été lancé.

88
RMI et le Multithread

• En java RMI, plusieurs clients peuvent exécuter des


méthodes distantes simultanément, en mode multithread.

• La gestion des accès concurrents reste, cependant, à la


charge du programmeur.

89
RMI et les threads

• Un appel RMI est initié par un thread côté appelant.

• Mais exécuté par un autre thread côté appelé.


• Le thread de l’appelant est bloqué jusqu’à ce que le thread du côté
appelé ai fini l’exécution.
• Si multiples appelants, multiples threads côté appelé
– Un objet distant est par essence multithread!
– Il faut gérer la synchronisation des threads (synchronized, wait, notify)

• Pas de lien entre le thread appelant et le thread côté appelé.

90
Quelques mots sur les Threads
Un thread permet l’exécution d’un programme.
Une application peut avoir de multiples threads qui s ’exécutent concurremment
(chaque thread a une priorité).
Chaque thread a un nom. Plusieurs threads peuvent avoir le même. Le nom est
généré si non spécifié.
Il y a 2 façons de créer un nouveau thread d’exécution.
• Déclarer une sous classe de thread et surcharger la méthode run. Une
instance de la sous classe peut alors être allouée et démarrer.
• Déclarer une classe qui implémente runnable et donc la méthode run. Une
instance de la classe peut être allouée, passée comme argument à la création
d’un thread et démarrée.
RMI et le Multithread
Exemple du Producteur / Consommateur

/*Fichier BufferInterface.java*/

Public interface BufferInterface extends java.rmi.Remote


{
Public void put(Object item) throws java.rmi.RemoteException;
Public Object get() throws java.rmi.RemoteException;
}

92
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier BufferInterface.java*/
public class BufferInterfaceImpl extends java.rmi.server.UnicastRemoteObject
implements BufferInterface {
Object buffer[];
int taille;
int ecrire;
int lire;
int nombre;
BufferInterfaceImpl(int n) throws RemoteException{
buffer =new Object[n];
taille=n; ecrire=0; lire=0; nombre=0;
} 93
RMI et le Multithread
Exemple du Producteur / Consommateur

public synchronized void put (Object item) throws RemoteException {


while (nombre ==taille)
try {wait();} catch (Exception e) { }
nombre++;
System.out.println(‘’*** Producteur dépose ‘’ +item+ ‘’nouvelle taille =‘’+nombre);
buffer[ecrire]=item;
ecrire= (ecrire+1)% taille;
notify();
}

94
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier BufferInterfaceImpl.java*/

public synchronized Object get () throws RemoteException {


Object item;
while (nombre == 0)
try {wait();} catch (Exception e) { }
item=buffer[lire];
nombre--;
System.out.println(‘’*** Consommateur extrait ‘’ +item+ ‘’nouvelle taille =‘’+nombre);
lire= (lire+1)% taille;
notify();
Return item; 95
}
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier ServeurBuffer.java*/
import java.rmi.*;
import java.rmi.server.*;

public class ServeurBuffer {


public static void main (String [] args) {
try{
BufferInterfaceImpl buffer =new BufferInterfaceImpl(6);
Naming.rebind(‘’ServeurBuffer’’,buffer);
System.out.println(‘’le buffer est prêt:’’);
}catch (Exception e){System.err.println(e);}
}
} 96
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier Consommateur.java*/
import java.util.*;

class Consommateur extends Thread{


private BufferInterface mbox;
public Consommateur (BufferInterface m) {mbox=m;}
public void run() {
Date message;
while (true){
Client.dormir();
message=new Date();
try{
message=(Date)mbox.get();
if(message !=null){
System.out.println(‘’Consommateur a lu’’+ message);}
}catch (Exception e){System.err.println(e);}
}} 97

}}
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier Producteur.java*/
import java.util.*;

class Producteur extends Thread{


private BufferInterface mbox;
public Producteur (BufferInterface m) {mbox=m;}
public void run() {
Date message;
while (true){
Client.dormir();
message=new Date();
try{
System.out.println(‘’Demande de dépôt:’’+ message);
mbox.put(message);}
catch (Exception e){System.err.println(e);}}
}}
98
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier Client.java*/
import java.rmi.*;
import java.util.*;

public class Client{


static String machine,nom, adresse;
public Client() {
BufferInterface mailBox;
System.setSecurityManager(new RMISecurityManager());
try{mailBox=(BufferInterface) Naming.lookup(adresse);
Producteur producteurThread1=new Producteur(mailBox);
Producteur producteurThread2=new Producteur(mailBox);
Consommateur consommateurThread1= new Consommateur (mailBox);
Consommateur consommateurThread2= new Consommateur (mailBox);
producteurThread1.start();
producteurThread2.start();
consommateurThread1.start();
consommateurThread2.start();}
catch (Exception e){System.err.println(e);
System.exit(1); 99
}}
RMI et le Multithread
Exemple du Producteur / Consommateur
/*Fichier Client.java*/

public static void dormir(){


private static final int TEMPS_SOMMEIL=5;
int tempsSommeil= (int) (TEMPS_SOMMEIL *Math.random());
try { Thread.sleep(tempsSommeil*1000);} catch (InterrruptedException e){ }}

public static void main(String args[ ]) {


if(args.length==0){ System.err.println(‘’au moins un paramètre’’); return; }
nom= args[0]; //ServeurBuffer
if(args.length!=1) machine=args[1];System.out.println(machine+ ‘’ ‘’+nom);
if (machine !=null) adresse=‘’rmi://’’+machine+’’/’’+nom;
else adresse=‘’rmi://localhost/’’+nom;
System.out.println(adresse);
Client client=new Client();
}

100
Déploiement Producteur / Consommateur

Chez le serveur:
• BufferInterface
• BufferInterfaceImpl
• ServeurBuffer

Chez le client:
• BufferInterface
• Client
• Consommateur
• Producteur

101
Exécution du Producteur /
Consommateur chez le serveur
C:\RMIExemples\start rmiregistry
C:\RMIExemples\ProdCons\Serveur>java ServeurBuffer

102
Exécution du Producteur /
Consommateur chez le client
C:\RMIExemples\ProdCons\Client>java Client ServeurBuffer localhost

103
Deadlock distribué

public synchronized foo() {


serveur.bar()
} public bar() {
client.toto()
public synchronized toto() { }
……
}

• 2 objets distants communiquent


• Cycle dans le graph d’appels
• Le thread ne peut pas entrer dans la méthode toto() tant que l’autre thread n’a pas fini d’exécuter foo
 Deadlock!
• Très difficile à identifier
– Pas de vue globale de l’application
– Pas d’information de la JVM
104
Les Callbacks
• Un Callback (appel en retour) est le fait qu’un serveur exécute une
méthode sur un objet distant proposé par le client.

• Le client, lors de son appel de méthode distante passe en paramètre une


référence sur l’objet qu’il propose au serveur. Ce dernier peut alors
invoquer une méthode sur cet objet.

• Il faut indiquer à la JVM que cet objet est bien un objet RMI, sinon il sera
sérialisé! D’où l’utilisation des méthodes statiques exportObject et
unexportObjet de UnicastRemoteObject.

• Un Callback peut être utilisé pour des retours d’information, pour des
applications de chat où les clients sont des objets distants.
105
Définition de l’interface
Exemple Hello World en utilisant un Callback

/*Fichier HelloInterface.java*/
import java.rmi.*;
public interface HelloInterface extends Remote{
/*Méthode imprimant un message prédéfini dans l’objet appelé */
public String sayHello(lclient oclient) throws RemoteException;
}

106
Implémentation de l’interface
Exemple Hello World en utilisant un Callback

/*Fichier HelloImpl.java*/
import java.rmi.*;
import java.rmi.server.*;
public class HelloImpl extends UnicastRemoteObject implements
HelloInterface {
private String message;
public HelloImpl(String s) throws RemoteException{
message=s; }
public String sayHello(lclient oclient) throws RemoteException {
return message+« »+oclient.getName() ; } 107
Définition de l’interface lclient
Exemple Hello World en utilisant un Callback

/*Fichier lclient.java*/
import java.rmi.*;
public interface lclient extends Remote{
public String getName( ) throws RemoteException;
}

108
Implémentation de l’interface lclient
Exemple Hello World en utilisant un Callback

/*fichier lclientImpl.java*/
import java.rmi.*;
public class lclientImpl implements lclient{
private String name;
public lclientImpl(String name) throws RemoteException{
this.name=name; }
public String getName( ) throws RemoteException {
return this.name;} }

109
Programme du serveur
Exemple Hello World en utilisant un Callback
/* fichier HelloServer.java*/
Import java.rmi.*;
public class HelloServer {
public static void main(String args[]) {
try {
Naming.rebind(" Hello1", new HelloImpl("Hello World"));
System.out.println("Sereveur prêt:");
} catch (Exception e) { e.printStackTrace(); }
}}

110
Programme du client
Exemple Hello World en utilisant un Callback
/*Fichier HelloClient.java*/
import java.rmi.*;
import java.rmi.server.*;

public class HelloClient {


public static void main(String args[]) {
lclient oclient;
try {
oclient=new oclient(args[0]);
HelloInterface hello= (HelloInterface) Naming.lookup("rmi://localhost/Hello1");
UnicastRemoteObject.exportObject(oclient);
System.out.println(hello.sayHello(oclient));
UnicastRemoteObject.unexportObject(oclient,true);
} catch (Exception e) {System.out.println(‘’Erreur client:’’+ e);}
}}
111
Hello World en utilisant un Callback
Déploiement après compilation avec JDK 1.5

Chez le client:
• HelloInterface
• HelloClient
• lclient
• lclientImpl
• lclientImpl_Stub

Chez le serveur:
• HelloInterface
• HelloImpl
• HelloServer
• lclien
• lclientImpl_Stub

112
Hello World en utilisant un Callback
Exécution

C:\RMIExemples\Callback\Server>start rmiregistry
C:\RMIExemples\Callback\Server>java HelloServer

Serveur prêt:

C:\RMIExemples\Callback\Client>java HelloClient Salima

Hello World Salima


C:\RMIExemples\Callback\Client>

113
Fabrique d’objets (Usines d’objets, classes Factory)

• Pour rendre un objet distant visible pour les clients à travers rmirestry, l’objet
doit être créer explicitement à l’intérieur d’une machine virtuelle java sur le
serveur, puis enregistrer en utilisant le bind() ou rebind() sur le registre.

• Lorsque le nombre et le genre d’objets distants ne peuvent pas être prévus


à l’avance, l’utilisation des fabriques d’objets s’avère utile.

• Permet à l’application cliente de demander dynamiquement la création de


nouveaux objets à distance, sans que les objets ne soient enregistrés
individuellement dans rmiregistry.

• C’est un objet distribué qui créé, à la demande, d’autres objets


distribués.
114
Fabrique d’objets (Usines d’objets, classes Factory)

RMI Restry

JVM Client 2-Recherche 1- Publie JVM Serveur

ObjectFactoryC

réf new()

Client

Objets de
la classe C

115
Fabrique d’objets (Usines d’objets, classes Factory)

116
Fabrique d’objets - Exemples

• Gestion de comptes bancaires,


• Gestion d’annuaires (répertoires téléphonique).

117
Fabrique d’objets
Exemples: Annuaire
public interface Annuaire extends Remote{
public String titre;
public boolean inserer(String nom, Info info) throws RemoteException, ExisteDeja;
public boolean supprimer(String nom)throws RemoteException, PasTrouve;
public Info rechercher(String nom) throws RemoteException, PasTrouve;
}

import java.rmi.*
public interface FabAnnuaire extends Remote{
public Annuaire newAnnuaire(String titre) throws RemoteException ;
}

public class Info implements Serializable {


public String adresse;
public int num_tel ;
}
public class ExisteDeja extends Exception{} ;
public class PasTrouve extends Exception{} ;
118
Fabrique d’objets
Implémentation de la classe annuaire
La classe annuaire

public class AnnuaireImpl implements Annuaire extends UnicastRemoteObject{


private String letitre;

public AnnuaireImpl(String titre) {this.letitre=titre};

public String titre {return letitre};

public boolean inserer(String nom, Info info) throws RemoteException, ExisteDeja{...};

public boolean supprimer(String nom) throws RemoteException, PasTrouve{...};

public Info rechercher(String nom) throws RemoteException, PasTrouve{...};

119
Fabrique d’objets
Implémentation de la fabrique

public class FabAnnuaireImpl implements FabAnnuaire extends UnicastRemoteObject{

public FabAnnuaireImpl{};

public Annuaire newAnnuaire(String titre) throws RemoteException {

return new AnnuaireImpl(titre)};

120
Fabrique d’objets
Mise en œuvre de la fabrique
Le serveur
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) ;
}}}

121
Fabrique d’objets
Utilisation de la fabrique
Programme client
import java.rmi.*;
public class Client {
public static void main (String [ ] argv) {
/* lancer SecurityManager */
System.setSecurityManager (new RMISecurityManager ()) ;
try {/* trouver une référence vers la fabrique */
FabAnnuaire fabrique =(FabAnnuaire ) 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) ;
}}}
122
Fonctionnement d’ensemble de la fabrique

123
Chargement dynamique de classes

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.
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.
124
Chargement dynamique de classes

Java charge les .class à la demande à partir du disque (CLASSPATH).


RMI introduit en plus un mécanisme de chargement de classes à distance par
HTTP ou FTP.

Il y a chargement dynamique quand:

• Un client a besoin d’une souche dont la classe n’est pas dans CLASSPATH,
chargement dynamique du Stub.

• Le serveur obtient une référence à un objet dont la classe est inconnue


(passage de paramètre, callback).

• Le serveur envoie à distance un sous-type du type attendu, réalisation du


125
polymorphisme.
Chargement dynamique de classes

Exemple de polymorphisme:

• Le client appelle une routine du type:


Result1 res=serveur.foo(…)

• Le serveur retourne un objet de type Result2 qui dérive de


Result1.

• Le client devra charger le code de Result2, ainsi que toutes


les classes (ou interfaces) utilisées par Result2.
126
Chargement dynamique de classes
• L’endroit contenant les classes à partir duquel il est possible de charger du code
est désigné par un codebase.

• Une propriété est utilisée: java.rmi.server.codebase spécifiant l’URL (un


répertoire ://, http://) où il est possible de trouver les classes.

• Dans le cas où le protocole http est utilisé, les classes doivent être chargées dans
un serveur Web.

• Deux classes doivent être utilisées: java.rmi.RMISecurityManager et


java.rmi.server.RMIClassLoader qui possède la méthode:
Public class loadClass (String codebase, String name)

• Le CLASSPATH est prioritaire sur codebase.


127
Principe du chargement dynamique

 A l’enregistrement (dans rmiregistry) de l ’objet distant, le codebase est


spécifié par java.rmi.server.codebase.

 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.

 Une fois que toutes les définitions de classe sont disponibles, la méthode
proxy du stub appelle les objets sur le serveur.

128
Les différentes étapes d’un chargement dynamique

1. Ecrire les classes correspondantes respectivement à l’interface et à l’objet.

2. Les compiler.

3. Générer le Stub correspondant à l’objet.

4. Installer tous les fichiers de classe (.class) sur un serveur Web.

5. Ecrire le serveur dynamique.

6. Installer rmiregistry au niveau de la machine du serveur.

129
Les différentes étapes d’un chargement dynamique

7. Lancer le serveur dynamique en lui précisant l’URL des fichiers de classe.

8. Sur la machine du client, écrire le code du client statique.

9. Compiler le client (statique) et installer éventuellement sur le serveur Web.


Il n’est pas nécessaire d’installer le security manager à ce niveau.

10. Compiler le client dynamique et le lancer en précisant l’URL des fichiers


de classe.

130
Exemple Hello Wolrd
Fichiers nécessaires si chargement dynamique

131
Exemple Hello World avec chargement dynamique:
Le serveur dynamique
Le serveur dynamique :
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RMISecurityManager;
import java.rmi.server.RMIClassLoader;
import java.util.Properties;
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, "Reverse");
Naming.rebind("rmi://sinus.cnam.fr:1099/MyReverse",(Remote) ClasseServeur.newInstance());
System.out.println("Objet Reverse lié dans le RMIregistry");
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());}}}
132
Exemple Hello World avec chargement dynamique:
Le client dynamique
Le client dynamique :
import java.rmi.RMISecurityManager;
import java.rmi.server.RMIClassLoader;
import java.util.Properties;
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() ;
catch (Exception e){ System.out.println (e.toString());}}}
133
Exemple Hello World avec chargement dynamique:
Exécution

Lancement du serveur Web:

Lancement du serveur dynamique:


java –Djava.security.policy=java.policy
-Djava.rmi.server.codebase=http://chemin du serveur Web DynamicServer

Lancement du client dynamique:


java –Djava.security.policy=java.policy
-Djava.rmi.server.codebase=http://chemin du serveur Web DynamicClient

134
Activation d’objets

Problématique
• Le nombre d’objets distribués par JVM est limité
• Dans des conditions réelles d’utilisation, plusieurs milliers d’objets peuvent être distribués
simultanément
• À un instant donné, seule une partie des objets distribués est utilisée
 lancer des JVM (proposant des objets distribués) à la demande
C’est le rôle des objets activables

Outil/Package
Un package java.rmi.activation
Un démon rmid qui active les objets à la demande

135
Activation d’objets
• Permet de n’activer des objets que quand ils sont utilisés.

• Evite d’avoir des objets serveurs actifs en permanence (Trop couteux si


beaucoup d’objets dans une JVM).

• L’activation peut être repoussée jusqu’à la première utilisation par le client, donc
première invocation de méthode.

• Rend les objets persistants (Enregistrés dans le système de fichier lorsqu’ils sont
désactivés).

• Un démon rmid s’occupe d’activer les objets quand ils reçoivent des requêtes.

• Les références distantes restent constantes d’une activation sur l’autre


(Transparent pour le client). 136
Activation d’objets: Quelques principes

• Les objets activables sont regroupés en un « GROUPE D’ACTIVATION »:


tous les objets qui appartiennent à un groupe d’activation sont activés
dans la même Machine Virtuelle Java.

• Deux « DESCRIPTEURS » sont utilisés:


 Descripteur d’un groupe d’activation.
 Descripteur d’activation, pour chaque objet activable.
Ils contiennent toutes les informations nécessaires au système pour
activer un objet.

137
Activation d’objets: cycle de développement
1. Créer l’interface
2. Créer une implémentation de l’objet, qui:
Étend Activable et non java.rmi.server.UnicastRemoteObject et possède le
constructeur obligatoire XX(ActivationID id, MarshalledObject data)
3. Créer le serveur, qui:
Crée les groupes d’activation
Crée les objets et les associe au groupe d’activation
Enregistre l’objet auprès du registre
4. Créer un client « standard »
5. Lancer le registre :rmirestry
6. Lancer le système d’activation :rmid
7.Lancer le serveur.
8.Lancer le client.
138
Etapes principales du serveur

1. Création de la description du groupe (ActivationGroupDesc):


Propriétés+ Paramètres de la JVM.

2. Obtention d’un identifiant de groupe: (ActivationGroupID).

3. Création d la description de l’objet activable (ActivationDesc):


Nom de la classe + Position + Paramètre(s) aux constructeurs

4. Enregistrement auprès du rmid

5. Enregistrement auprès du rmiregistry (pas de création d’une


instance de l’objet. 139
Exemple: HelloImpl.java
avec activation d’objets

import java.rmi .*;


import java.rmi . activation .*;
import java.io.Serializable ;
public class HelloImpl extends Activatable implements Hello {

public HelloImpl ( ActivationID id , MarshalledObject data ) throws


RemoteException {super (id, 0);}

public String sayHello ( String nom ) {


return "Hello World";}}

140
Exemple: HelloServer.java
avec activation d’objets

import java.rmi.registry.LocateRegistry ;
import java.rmi.registry.Registry ;
import java.rmi.RMISecurityManager ;
import java.rmi.activation .*;
import java.util.Properties ;
import java.util.Arrays ;

141
Exemple: HelloServer.java
public class Server {
avec activation d’objets
public static void main ( String args []) throws Exception {
int port = 1099;
if ( args . length ==1)
port = Integer.parseInt ( args [0]) ;
try { System.setSecurityManager (new RMISecurityManager ());
Properties props = new Properties ();
ActivationGroupDesc my_group_desc = new ActivationGroupDesc (props , null);
ActivationGroupID my_group_id = ActivationGroup . getSystem (). registerGroup (my_group_desc );
ActivationGroup.createGroup ( my_group_id , my_group_desc , 0);
ActivationDesc objectdesc = new ActivationDesc ( my_group_id , " HelloImpl ", "", null );
Hello stub = (Hello) Activatable.register ( objectdesc );
Registry registry = LocateRegistry . getRegistry ( port );
if (! Arrays.asList ( registry . list ()). contains (" HelloActivatable "))
registry.bind (" HelloActivatable ", stub );
Else registry . rebind (" HelloActivatable ", stub );
System.out.println (" HelloActivatable active et lie au registre "); }
catch ( Exception e){ System . out. println (" Exception "+ e);}}}
142
Exemple: Client.java
avec activation d’objets
import java.rmi.registry.LocateRegistry ;
import java.rmi.registry.Registry ;
public class Client {
public static void main ( String args []) {
String machine = " localhost ";
int port = 1099;
if ( args.length ==3) {
machine = args [0];
port = Integer . parseInt ( args [1]) ;
} else if ( args.length ==2)
machine = args [0];
try {
Registry registry = LocateRegistry . getRegistry ( machine , port );
Hello stub = ( Hello ) registry . lookup (" HelloActivatable ");
System .out. println ( stub . sayHello ( args [ args . length -1]) );
} catch ( Exception e) {
System .out. println (" Client exception : " +e);
}}} 143
Exemple: helloworld.policy
avec activation d’objets

grant {
// permission java.net.SocketPermission "*:1024 -65535" ," listen , connect
,accept , resolve";
// permission java.net.SocketPermission "*:80" , " connect ";
permission java.security . AllPermission ; /* on autorise tout */
};

144
Exemple: l’exécution
avec activation d’objets
• Lancement de rmiregistry

• Lancement de rmid:
rmid -J- Djava.security.policy=java.policy

• Lancement du serveur activable:


Java –Djava.security.policy=java.policy
- Djava.rmi.server.codebase=… HelloServer

• Lancement du client:
java Client 145
Autres aspects avancés de RMI

• Sockets personnalisés et SSL: pour crypter/compresser la


communication.

• RMI pare-feux et HTTP:utilisation HTTP tunneling pour faire


des invocations de méthodes rmi à travers des pare-feu.

146

Vous aimerez peut-être aussi