import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import javax.security.auth.login.LoginException; public class Client extends UnicastRemoteObject implements Facturation{ String name; double monCompte = 10000; // initialement le client est riche ! protected Client() throws RemoteException { super(); } public Client(String name) throws RemoteException{ super(); this.name=name; } public static void main(String[] args){ Client c=null; try { c = new Client(args[0]); } catch (RemoteException e1) { e1.printStackTrace(); } System.out.println("Recherche d'un objet serveur"); /* if (System.getSecurityManager() == null) { System.setSecurityManager(new java.rmi.RMISecurityManager()); } On peut remplacer ces lignes en écrivant -Djava.security.manager et pour debugger -Djava.security.debug=policy */ ConnexionServeur cs=null; // cs est un stub vers l objet remote, obtenu par le lookup try { RMISSLClientSocketFactory clientFactory = new RMISSLClientSocketFactory(); Registry reg= LocateRegistry.getRegistry("localhost", 2004, clientFactory); // Cette version de locate registry + lookup fait en sorte de munir le client d'une connexion SSL // car le rmi registry a été créé et ne parle plus que sur des sockets SSL // Note: le keystore contenant les certificats SSL est bien celui utilisé par les 2 cotes (serveur, client) : // la preuve: si on enlève les -Djavax.net.ssl.keyStore=test.ks -Djavax.net.ssl.keyStorePassword=changeitpass // au lancement du serveur, alors, le serveur se lance bien, mais le client // recoit une exception lui indiquant qu'il n'arrive pas à communiquer. // Symétriquement, si le serveur est bien démarré avec les properties nécessaires, mais le // client non (on enlève -Djavax.net.ssl.trustStore=testclient.ks à son démarrage), alors // c'est au moment de sa tentative de connexion avec le rmiregistry qu'il n'a pas les bonnes configs pour // communiquer sur un socket SSL //cs = (ConnexionServeur)Naming.lookup("rmi://localhost" + ":2004/Serveur"); cs = (ConnexionServeur)reg.lookup("Serveur"); // rechercher sur cette machine, localhost, un objet remote offrant un service de connexion //} catch (MalformedURLException e) { // e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } catch (NotBoundException e) { e.printStackTrace(); } Service s=null; try { System.out.println("Logon va débuter "); s=cs.logon(args[0], args[1]); } catch (LoginException e) { e.printStackTrace(); System.out.println("Dommage, mauvais login et mot de passe, Recommencez !"); System.exit(0); } catch (RemoteException e) { e.printStackTrace(); } System.out.println("On a recu une ref vers service distant, et on a été authentifié "+s ); c.travailler(s); // utiliser le service 's' afin de réaliser son propre travail System.out.println("Client a fini son boulot"); // mais après ce print, on est bloqué car client est unicastRemoteObject! } void travailler(Service s){ // precaution à prendre est de ne pas mettre cette méthode synchronized try{ for (int i=1;i<4;i++){ int quantite=2+i; int res=s.setVal(quantite,this); Thread.sleep(1000); System.out.println("Thread "+Thread.currentThread().getName() +" Valeur rendue par le service "+res); } }catch (Exception e) { e.printStackTrace(); } } // Cette méthode sera invoquee par l objet serveur pour savoir à quel nom établir la facture // Dans un deuxieme exercice, on enverra un smart proxy de Client afin que le serveur ait déjà // l information rendue par getName sans avoir besoin de faire un appel distant @Override public String getName() throws RemoteException { System.out.println("Thread="+Thread.currentThread().getName()+" is calling getName()"); return name; } // Cette méthode sera invoquee par l objet serveur pour présenter la facture au client @Override // facturer est synchronized car si le client a appelé plusieurs services, ceux ci // doivent pouvoir se faire payer sans etre en concurrence (modif de la valeur monCompte) // Cela ne provoque pas interblocage malgré que setVal soit aussi synchronized! // Un interblocage se produirait si travailler etait aussi synchronized, // car cela empecherait le serveur d'invoquer facturer tant que le client serait // dans le moniteur car encore dans sa méthode travailler public synchronized void facturer(Facture f) throws RemoteException { monCompte-=f.montantfact; System.out.println("On vient de payer " + f.montantfact + " pour un service de "+ f.v + ", adressee à "+getName()); } }