Classe BanqueImpl

Transcription

Classe BanqueImpl
RMI : Remote Method Invocation
Appel de méthodes à distance
TD/TP
Patrice Torguet
[email protected]
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 :
void creer_compte(String id, double somme_initiale);
l void ajouter(String id, double somme);
l void retirer(String id, double somme);
l
l
–
!3
Position position(String id);
Position est la classe suivante (à compléter) :
public class Position {
private double solde;
private Date derniereOperation;
public Position(double solde) {
this.solde = solde;
this.derniereOperation = new Date();
}
}
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
!4
à Position qui doit être sérialisable
Interface Banque
public interface Banque extends java.rmi.Remote {
public void creerCompte(String id, double somme)
throws java.rmi.RemoteException;
!
public void ajouter(String id, double somme)
throws java.rmi.RemoteException;
!
public void retirer(String id, double somme)
throws java.rmi.RemoteException;
!
public Position position(String id) throws java.rmi.RemoteException;
!
public void enregistrerNotification(String id, BanqueNotification b,
double minimum) throws java.rmi.RemoteException;
!
public void enleverNotification(String id) throws java.rmi.RemoteException;
}
!5
Classe Position
public class Position implements Serializable {
private double solde;
private Date derniereOperation;
!
public Position(double solde) {
this.solde = solde; this.derniereOperation = new Date();
}
public Date getDerniereOperation() {
return derniereOperation;
}
public void setDerniereOperation(Date derniereOperation) {
this.derniereOperation = derniereOperation;
}
public double getSolde() {
return solde;
}
}
public void setSolde(double solde) {
this.solde = 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
!7
É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
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) {
this.position = new Position(somme);
}
!
public Position getPosition() {
return position;
}
!
!9
}
public void ajouter(double somme) {
position.setSolde(position.getSolde()+somme);
position.setDerniereOperation(new Date());
}
public void retirer(double somme) {
position.setSolde(position.getSolde()-somme);
position.setDerniereOperation(new Date());
}
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 {
comptes.put(id, new Compte(somme));
}
public synchronized void ajouter(String id, double somme) throws RemoteException {
Compte cpt = comptes.get(id);
cpt.ajouter(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 = comptes.get(id);
cpt.retirer(somme);
}
public synchronized Position position(String id) throws RemoteException {
Compte cpt = comptes.get(id);
return cpt.getPosition();
}
}
public static void main(String[] args) throws Exception {
LocateRegistry.createRegistry(1099);
Naming.rebind("MaBanque", new BanqueImpl());
System.out.println("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.
–
–
!14
Quel mécanisme, vu en cours, peut être utilisé ?
Modifiez l’interface et les classes pour gérer cette situation.
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) Naming.lookup("rmi://localhost:1099/MaBanque");
!
proxy.creerCompte("Bob", 100);
proxy.ajouter("Bob", 100);
System.out.println("Position du compte de Bob : "+proxy.position("Bob"));
proxy.retirer("Bob", 300);
proxy.retirer("Bob", 100);
System.out.println("Position du compte de Bob : "+proxy.position("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 java.rmi.Remote {
public void creerCompte(String id, double somme)
throws java.rmi.RemoteException;
!
...
!
public void enregistrerNotification(String id, BanqueNotification b,
double minimum) throws java.rmi.RemoteException;
!
}
public void enleverNotification(String id) throws java.rmi.RemoteException;
public interface BanqueNotification extends Remote {
public void notification(double valeur, double mini) throws RemoteException;
}
!18
Ajout des notifications
l Il
!19
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
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) {
position.setSolde(position.getSolde()-somme);
position.setDerniereOperation(new Date());
if (notif != null && position.getSolde() < notifMinimum) {
try {
notif.notification(position.getSolde(), notifMinimum);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void setNotification(BanqueNotification notif, double mini) {
this.notif = notif;
notifMinimum = mini;
}
!
!
!20
!
}
Ajout des notifications
l Il
faut 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 = comptes.get(id);
cpt.setNotification(b, mini);
}
!
!22
public synchronized void enleverNotification(String id) throws RemoteException {
Compte cpt = comptes.get(id);
cpt.setNotification(null, 0);
}
...
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 System.out.println)
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;
!
}
!24
public BanqueNotificationImpl(String id) throws RemoteException {
super() ;
this.id = id;
}
public void notification(double valeur, double mini) throws RemoteException {
System.out.println("Votre compte "+id+" est inferieur au mini : "+
mini+" solde : "+valeur);
}
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) Naming.lookup("MaBanque");
!
proxy.creerCompte("Bob", 100);
BanqueNotificationImpl bni = new BanqueNotificationImpl("Bob");
proxy.enregistrerNotification("Bob", bni, 0);
proxy.ajouter("Bob", 100);
System.out.println("Position du compte de Bob : "+
proxy.position("Bob"));
proxy.retirer("Bob", 300);
proxy.retirer("Bob", 100);
System.out.println("Position du compte de Bob : "+
proxy.position("Bob"));
bni = null;
proxy.enleverNotification("Bob");
!26
}