RMI : Remote Method Invocation
Appel de méthodes à distance
TD/TP
Patrice Torguet
torguet@[Link]
Université Paul Sabatier
But
l But du TD/TP : application répartie permettant de gérer
des comptes bancaires.
l Un serveur gérera tous les comptes bancaires et
permettra à des clients de se connecter et d’effectuer les
opérations suivantes :
– void creer_compte(String id, double somme_initiale);
– void ajouter(String id, double somme);
– void retirer(String id, double somme);
– Position position(String id);
!2
Interface Banque
l Écrire l’interface Banque dérivant de Remote, avec les méthodes :
l void creer_compte(String id, double somme_initiale);
l void ajouter(String id, double somme);
l void retirer(String id, double somme);
l Position position(String id);
– Position est la classe suivante (à compléter) :
public class Position {
private double solde;
private Date derniereOperation;
public Position(double solde) {
[Link] = solde;
[Link] = new Date();
}
}
!3
Interface
l Elle doit hériter de Remote
l Toutes les méthodes doivent déclarer qu’elles peuvent
lancer l’exception RemoteException (lancée par RMI
s’il y a un pb)
l Les paramètres et résultats doivent être :
– de type de base (ici double) ;
– sérialisable (String et Date sont sérialisables) ;
– Remote.
l Attention à Position qui doit être sérialisable
!4
Interface Banque
public interface Banque extends [Link] {
public void creerCompte(String id, double somme)
throws [Link];
!
public void ajouter(String id, double somme)
throws [Link];
!
public void retirer(String id, double somme)
throws [Link];
!
public Position position(String id) throws [Link];
!
public void enregistrerNotification(String id, BanqueNotification b,
double minimum) throws [Link];
!
public void enleverNotification(String id) throws [Link];
}
!5
Classe Position
public class Position implements Serializable {
private double solde;
private Date derniereOperation;
!
public Position(double solde) {
[Link] = solde; [Link] = new Date();
}
public Date getDerniereOperation() {
return derniereOperation;
}
!
public void setDerniereOperation(Date derniereOperation) {
[Link] = derniereOperation;
}
!
public double getSolde() {
return solde;
}
!
public void setSolde(double solde) {
[Link] = solde;
}
public String toString() {
return "solde = "+solde+" date derniere op. "+derniereOperation;
!6 }
}
Implémentation
l Écrire la classe Compte qui permet de consulter la position
d’un compte, d’ajouter et de retirer une somme à un compte.
On pourra s’inspirer de la classe CompteEnBanque tu TD/TP
Sockets
!
l Écrire une classe BanqueImpl qui gère la partie serveur de
notre application répartie. Les comptes seront stockés dans
une HashMap qui permettra de retrouver un compte à partir de
son identification. Ici aussi on peut s’inspirer de
BanqueSimple
!7
Classe Compte
l On
reprends la classe CompteEnBanque du TD
précédent mais au lieu de déclarer le solde et la date on
déclare une Position en attribut
!8
Classe Compte
class Compte {
private Position position;
!
public Compte(double somme) {
[Link] = new Position(somme);
}
!
public Position getPosition() {
return position;
}
!
public void ajouter(double somme) {
[Link]([Link]()+somme);
[Link](new Date());
}
public void retirer(double somme) {
[Link]([Link]()-somme);
[Link](new Date());
}
!9 }
Classe BanqueImpl
l Elle doit hériter de UnicastRemoteObject
l Elle doit implanter l’interface Banque
l Le constructeur peut lancer une exception
RemoteException (en fait c’est celui de la classe
ancêtre : super(); )
l On doit protéger la HashMap contre des modifications
simultanées => synchronized
!10
Classe BanqueImpl
public class BanqueImpl extends UnicastRemoteObject implements Banque {
HashMap<String, Compte> comptes;
!
public BanqueImpl() throws RemoteException {
super();
comptes = new HashMap<String, Compte>();
}
!
public synchronized void creerCompte(String id, double somme) throws RemoteException {
[Link](id, new Compte(somme));
}
!
public synchronized void ajouter(String id, double somme) throws RemoteException {
Compte cpt = [Link](id);
[Link](somme);
}
!11
Classe BanqueImpl
l On doit placer un main
l Il va créer le rmiregistry (annuaire RMI)
l Il va créer un objet de type BanqueImpl
l Puis l’enregistrer dans le rmiregistry avec un nom en
utilisant la classe Naming
!12
Classe BanqueImpl
public synchronized void retirer(String id, double somme) throws RemoteException {
Compte cpt = [Link](id);
[Link](somme);
}
!
public synchronized Position position(String id) throws RemoteException {
Compte cpt = [Link](id);
return [Link]();
}
public static void main(String[] args) throws Exception {
[Link](1099);
[Link]("MaBanque", new BanqueImpl());
[Link]("MaBanque est enregistrée");
}
}
!13
Client
l Écrire une classe BanqueClient qui gère la partie client de
notre application répartie. L’application présentera un petit
menu (sous forme textuelle) permettant d’accéder aux
diverses méthodes.
!
l On veut maintenant que le serveur, prévienne le client quand
le solde de son compte devient négatif ou inférieur à une
valeur choisie par le client.
– Quel mécanisme, vu en cours, peut être utilisé ?
– Modifiez l’interface et les classes pour gérer cette situation.
!14
Client
l On va utiliser la classe Naming pour récupérer un
proxy (stub, souche cliente) qui va être envoyée par le
rmiregistry.
l Une fois qu’on a le proxy on appelle les méthodes
normalement et elles s’exécutent à distance
!15
Classe BanqueClient
public class BanqueClient {
public static void main(String[] args) throws Exception {
Banque proxy = (Banque) [Link]("rmi://localhost:1099/MaBanque");
!
[Link]("Bob", 100);
[Link]("Bob", 100);
[Link]("Position du compte de Bob : "+[Link]("Bob"));
[Link]("Bob", 300);
[Link]("Bob", 100);
[Link]("Position du compte de Bob : "+[Link]("Bob"));
}
}
!16
Ajout des notifications
l Le serveur doit appeler une méthode du client
l Donc il nous faut une interface dérivant de Remote
pour le client => BanqueNotification
l On va ajouter des méthodes dans Banque qui
permettent de donner la référence distante de
BanqueNotification au serveur
l C’est un Remote donc pas de pb de sérialisation
!17
Banque avec notif
public interface Banque extends [Link] {
public void creerCompte(String id, double somme)
throws [Link];
!
...
!
public void enregistrerNotification(String id, BanqueNotification b,
double minimum) throws [Link];
!
public void enleverNotification(String id) throws [Link];
}
public interface BanqueNotification extends Remote {
public void notification(double valeur, double mini) throws RemoteException;
}
!18
Ajout des notifications
l Il faut modifier la classe Compte pour qu’elle stocke la
souche cliente permettant d’envoyer les notifications
l On ajoute donc un attribut de type BanqueNotification
et une méthode setNotification
l On oublie pas d’ajouter en plus le seuil minimum en
dessous duquel on doit faire la notification
l Lors d’un retrait, si on passe en dessous du seuil
minimum on notifie le client
l Attention : l’appel de la méthode notification peut
!19 générer une exception si RMI a un problème.
Compte avec notif
class Compte {
private Position position;
private BanqueNotification notif;
private double notifMinimum;
!
...
!
public void retirer(double somme) {
[Link]([Link]()-somme);
[Link](new Date());
if (notif != null && [Link]() < notifMinimum) {
try {
[Link]([Link](), notifMinimum);
} catch (RemoteException e) {
[Link]();
}
}
}
!
public void setNotification(BanqueNotification notif, double mini) {
[Link] = notif;
notifMinimum = mini;
}
!
!20 }
Ajout des notifications
l Ilfaut modifier la classe BanqueImpl pour implanter les
deux nouvelles méthodes de l’interface Banque
l Elles permettent d’ajouter une souche de
BanqueNotification à un compte ou d’enlever une
souche déjà présente
!21
BanqueImpl avec notif
...
public synchronized void enregistrerNotification(String id, BanqueNotification b,
double mini) throws RemoteException {
Compte cpt = [Link](id);
[Link](b, mini);
}
!
public synchronized void enleverNotification(String id) throws RemoteException {
Compte cpt = [Link](id);
[Link](null, 0);
}
...
!22
Ajout des notifications
l On code la classe BanqueNotificationImpl
l Le but de cette classe est d’implémenter l’interface
BanqueNotification et donc d’afficher un message en
cas de notification (ici un simple [Link])
l Cette classe doit (comme BanqueImpl) dériver de
UnicastRemoteObject
l On n’oublie pas le constructeur pouvant générer
l’exception RemoteException
!23
NotificationImpl
public class BanqueNotificationImpl extends UnicastRemoteObject
implements BanqueNotification {
private String id;
!
public BanqueNotificationImpl(String id) throws RemoteException {
super() ;
[Link] = id;
}
public void notification(double valeur, double mini)
throws RemoteException {
[Link]("Votre compte "+id+" est inferieur au mini : "+
mini+" solde : "+valeur);
}
}
!24
Ajout des notifications
l Dans le main du client on va créer un objet de type
BanqueNotificationImpl
l On va l’enregistrer sur un compte
l Dès qu’on fera trop de retraits, la notification sera
appelée par le serveur et un message s’affichera sur la
console
!25
Client avec notif
public static void main(String[] args) throws Exception {
Banque proxy = (Banque) [Link]("MaBanque");
!
[Link]("Bob", 100);
BanqueNotificationImpl bni = new BanqueNotificationImpl("Bob");
[Link]("Bob", bni, 0);
[Link]("Bob", 100);
[Link]("Position du compte de Bob : "+
[Link]("Bob"));
[Link]("Bob", 300);
[Link]("Bob", 100);
[Link]("Position du compte de Bob : "+
[Link]("Bob"));
bni = null;
[Link]("Bob");
}
!26