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 }