1 / 46

RMI

RMI. Introduction. En deuxième vous avez appris à créer des applications distribuées en utilisant les sockets. Cette manière de faire n’est pas orientée objets

reuben
Download Presentation

RMI

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. RMI

  2. Introduction • En deuxième vous avez appris à créer des applications distribuées en utilisant les sockets. • Cette manière de faire n’est pas orientée objets • Les classes de l’application sont réparties sur plusieurs machines (virtuelles) et on aimerait pouvoir appeler directement les méthodes d’objets distants. • RMI est une solution « pur Java » • CORBA est une solution multi - langages et multi - plateformes • COM, DCOM, COM+, ADO sont des solutions purement Microsoft

  3. Architecture de RMI

  4. Architecture RMI (2) • Le client utilise des interfaces d’objets implémentées coté serveur. • Quand un client appelle une méthode sur une telle interface, il le fait en réalité sur un Stub (proxy) • Ce Stub transmet l’appel à la couche RRL qui assure la connexion avec le serveur en point à point (unicast). Le rmiregistry travaille à ce niveau. • Cette couche transmet la requête à la couche transport qui utilise l’un des protocoles JRMP ou IIOP au dessus de TCP/IP et transfère la requête en remontant vers le skeleton qui appelle la méthode sur l’objet distant

  5. Exemple : Hello World (1) • L’interface : import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String sayHello() throws RemoteException; } • L’interface doit étendre java.rmi.Remote. • Toutes les méthodes doivent lancer une RemoteException.

  6. Exemple : Hello World (2) • L’implémentation : import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class HelloImpl extends UnicastRemoteObject implements Hello { public HelloImpl() throws RemoteException { } public String sayHello() throws RemoteException { return "Hello World !"; } }

  7. Exemple : Hello World (3) • Elle doit implémenter l’interface Hello. • Il faut que les constructeurs lancent une RemoteException. Donc il faut toujours en écrire un. • Elle doit étendre UnicastRemoteObject ou utiliser exportObject() : Si elle ne l’étend pas, le constructeur s’écrira : public HelloImpl() throws RemoteException { UnicastRemoteObject.exportObject(this); } • Si ce n’est pas fait, il faudra le faire à chaque construction. • Elle peut implémenter d’autres méthodes que celle dans l’interface mais celles-ci ne seront pas accessibles à distance.

  8. Passage des paramètres • Les paramètres que l’on passe à une méthode appelable à distance doivent être • des types primitifs : int, short, double, boolean … • des objets Serializable • des objets Remote • Les types primitifs sont transmis par valeur (copie). • Les objets Serializable sont sérialisés, transmis puis désérialisés. • Les objets Remote sont transmis sous forme de références distantes. • Ce sont les stubs qui emballent (marshal) les paramètres et les désemballent (unmarshal). • Il en va de même de la valeur de retour.

  9. Le rmiregistry • Afin que les clients trouvent les services distants, ceux-ci doivent au préalable êtreenregistrés dans un annuaire. • RMI peut utiliser plusieurs types d’annuaires. • RMI/JRMP en fournit un : rmiregistry • Le serveur pourra enregistrer un objet distant en appelant la méthode bind() (ou rebind()) de la classe java.rmi.Naming. • Le client le retrouvera grâce à la méthode lookup() de cette même classe.

  10. Exemple : Hello World (4) • Le Serveur : import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.RemoteException; public class HelloServer { public static void main(String[] args) {

  11. Exemple : Hello World (5) Hello hello; try { hello = new HelloImpl(); Naming.rebind("rmi://localhost:1099/Hello", hello); } catch (RemoteException e) { System.err.println( "Avez-vous lancé rmiregistry ?"); System.exit(1); } catch (MalformedURLException e) { throw new InternalError( "l'URL est incorrecte"); } } }

  12. Exemple : Hello World (6) • Le client : import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; public class HelloClient { public static void main(String[] args) { String url = null; Hello hello = null; try { url = "rmi://" + args[0] + ":1099/Hello"; hello = (Hello) Naming.lookup(url);

  13. Exemple : Hello World (7) } catch (MalformedURLException e) { System.err.println("l'URL " + url + "est incorrecte"); System.exit(1); } catch (RemoteException e) { System.err.println( "Avez-vous lancé le rmiregistry ?"); System.exit(2); } catch (NotBoundException e) { System.err.println( "Avez-vous lancé le serveur ?"); System.exit(2); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "Usage : HelloClient host"); System.exit(3); }

  14. Exemple : Hello World (8) try { System.out.println(hello.sayHello()); } catch (RemoteException e) { System.err.println("le serveur semble down"); System.exit(4); } } }

  15. Implémentation • Pour implémenter une application RMI, il faut d’abord • écrire l(es) interface(s) Remote (Hello) • écrire leur(s) implémentation(s) (HelloImpl) • écrire le serveur • écrire le client

  16. Compilation (1) • Vous devez ensuite compiler vos classes avec javac (En Eclipse, ceci est automatique). • Puis vous devez compiler les implémentationscompilées des interfaces Remote avec rmic :rmic HelloImpl • rmicadmet entre autre les options : -keep garde les sources des stubs et skeletons -g pour debugger -d chemin pour placer dans chemin les fichiers générés -classpath cheminpour dire où sont les classes -v 1.1 génère les stubs et skeletons v1.1 -v 1.2 génère les stubs version 1.2 (défaut) -v compat génère les deux (défaut avant Java 5)

  17. Compilation (2) • En Eclipse, pour exécuter rmic, vous devez créer un external tool : External Tools Program New Name : rmic Location : Browse File System : C:\jdk1.5.0_08\bin\rmic.exe Working Directory : Browse Workspace : ${workspace_loc:/rmi} Arguments : HelloImpl Apply Run

  18. Exécution (1) • Lancer rmiregistry : • En Eclipse, vous devez créer un external tool : External Tools Program New Name : rmiregistry Location : Browse File System : C:\jdk1.5.0_08\bin\rmiregistry.exe Working Directory : Browse Workspace : ${workspace_loc:/rmi} Apply Run

  19. Exécution (2) • Lancer le serveur • En Eclipse, c’est un Run normal • Lancer le client • En Eclipse, c’est aussi un Run normal • Dans la Console du client vous devriez voir Hello World !

  20. Variante • Le serveur peut lancer lui-même le rmiregistry par programme : try { LocateRegistry.createRegistry(1099); } catch (RemoteException e1) { System.err.println( "rmiregistry est déjà lancé sur ce port"); System.exit(1); } • Dans ce cas, il ne faut pas exécuter rmiregistry via l’external tool

  21. Déploiement • La machine client disposera des classes : • HelloClient.class • Hello.class • HelloImpl_Stub.class • La machine serveur disposera de : • HelloServer.class • Hello.class • HelloImpl.class • HelloImpl_Stub.class (en version 1.2) • HelloImpl_Skel.class (en version 1.1)

  22. URL • Le format général d’une URL RMI est rmi://host:port/Nom • Seul le Nom est obligatoire : • le protocole par défaut est rmi: • le port par défaut est 1099 • le host par défaut est localhost • exemples d’URL valides • //172.16.2.125:2000/ObjetDistant • //172.16.2.125/ObjetDistant (port = 1099) • ObjetDistant (port 1099 sur localhost)

  23. Distributed Garbage Collector • Un objet distant est mis à disposition du DGC si plus aucun stub n’existe. • Ainsi si un client accède à un objet distant, il possède un stub de cet objet distant. Un message indiquant ce fait est transmis à la JVM du serveur qui garde un compteur de références distantes. • Ce stub cessera d’exister s’il est remis à null ou, plus généralement à la fin d’une période de lease. A ce moment la JVM client prévient la JVM serveur qui décrémente son compteur de références. • Quand celui-ci tombe à 0, il est mis à disposition du DGC qui appelle la méthode unreferenced() de l’objet (si elle existe) et passe l’objet au GC (qui lui appelle la méthode finalize() de l’objet si elle existe).

  24. Callbacks (1) • Si le client instancie un objet d’une classe qui implémente une interface Remote, le serveur pourra appeler des méthodes de cet objet. • Ces appels sont nommés callbacks. • Comme le serveur ne connaît pas la manière dont est implémentée une telle méthode et que le temps d’exécution de celle-ci peut être long, les callbacks seront généralement implémentés comme des opérations asynchrones. • En java les méthodes sont synchrones à l’exception de la méthode start() d’un thread !

  25. Callbacks (2) • Considérons un jeu style « Questions pour un champion® ». • A un moment, le Jeu enverra la question à tous les Joueurs. • Il n’est évidemment pas possible d’attendre que le premier joueur contacté ait répondu avant d’envoyer la question au deuxième, puisque c’est une question de rapidité. • Le Jeu enverra donc la question de manière asynchrone à chaque joueur. • Une méthode asynchrone est toujours void.

  26. jeu :Jeu joueur : Joueur questionner(Stringquestion) Callbacks (3)

  27. jeu : Jeu joueur : Joueur new  : Thread questionner(String question) Callbacks (4) • Implémentation en Java : start

  28. Callback (5) • Autre exemple : au début du jeu se déroule une phase d’inscription. • Quand le dernier joueur s’inscrit, la méthode d’inscription le détermine (timeout ou maximum atteint). • Elle doit alors lancer le Jeu. • Ceci devra se faire de manière asynchrone afin que le joueur puisse terminer l’appel à la méthode d’inscription. • Comme la méthode d’inscription est appelable par plusieurs joueurs, elle devra être synchronized. • Il en ira de même de toutes les méthodes du serveur appelable par plusieurs clients.

  29. Terminaison du client (1) • Si le client détecte qu’il a terminé lors de l’exécution d’un callback, il devra prendre des précautions pour ne pas interrompre la communication avec le serveur. • Pour cela, il créera un thread qui exécutera le System.exit(0) plutôt que de le faire lui-même. • Encore faut-il que ce thread ne s’exécute que quand le callback est terminé. • Ceci se fera en synchronisant l’exécution du callback et celle du thread sur un même lock.

  30. Terminaison du client (2) public void callback() { ... if (fini()) { synchronized (this) { new Thread() { public void run() { synchronized (Client.this) { System.exit(0) ; } } }.start(); } } }

  31. RMI – IIOP (1) • Utilise le protocole IIOP de CORBA au lieu de JRMP. • On utilisera javax.rmi.PortableRemoteObject au lieu de java.rmi.server.UnicastRemoteObject. • On accédera à un service de noms via l’API JNDI et non plus via la classe Naming. • rmiregistry sera remplacé par un annuaire compatible avec CORBA, tnameserv ou mieux orbd. • Il faudra signaler au serveur et au client où se trouve cet annuaire. • Les conversions de types pour des objets distants ne pourront plus se faire par casting mais en utilisant PortableRemoteObject.narrow().

  32. RMI – IIOP (2) • Reprenons l’exemple de HelloWorld. • l’interface Hello.java ne change pas. • l’implémentation HelloImpl.java est modifiée comme suit, afin de tenir compte des remarques précédentes.

  33. RMI – IIOP (3) import java.rmi.RemoteException; import javax.rmi.PortableRemoteObject; public class HelloImpl extends PortableRemoteObject implements Hello { public HelloImpl() throws RemoteException { // ou PortableRemoteObject.exportObject(this); } public String sayHello() throws RemoteException{ return "Hello World !"; } }

  34. RMI – IIOP (4) • Le serveur commencera par localiser son annuaire : import java.rmi.RemoteException; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.Context; import javax.naming.NamingException; public class HelloServer { public static void main(String[] args) { // étape 0 : dire où se trouve le service de nom Properties props = new Properties(); props.put("java.naming.factory.initial", "com.sun.jndi.cosnaming.CNCtxFactory"); props.put("java.naming.provider.url", "iiop://localhost:1050");

  35. RMI – IIOP (5) • Ensuite, il instanciera le servant : Hello hello = null; try { hello = new HelloImpl(); } catch (RemoteException e) { System.err.println( "avez-vous exécuté rmic -iiop ?"); System.exit(1); }

  36. RMI – IIOP (6) • Enfin il publie la référence du servant dans l’annuaire : Context initialNamingContext = null; try { initialNamingContext = new InitialContext(props); } catch (NamingException e) { System.err.println( "le service de nom n‘est pas lancé"); System.exit(2); }

  37. RMI – IIOP (7) try { initialNamingContext.rebind( "HelloService", hello ); } catch (NamingException e) { System.err.println( "le service de nom est down"); System.exit(3); } } }

  38. RMI – IIOP (8) • Le client devra également préciser la localisation de l’annuaire. Ici nous allons le faire en utilisant un fichier de properties : • jndi.properties contient : java.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory java.naming.provider.url=iiop://localhost:1050

  39. RMI – IIOP (9) import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.ConnectException; import java.rmi.RemoteException; import java.util.Properties; import javax.naming.CommunicationException; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; public class HelloClient {

  40. RMI – IIOP (10) public static void main( String args[] ) { Context ic = null; Object objref; Hello hello; // étape 0: localiser le service de nom Properties props = new Properties(); try { props.load( new FileInputStream("jndi.properties"));

  41. RMI – IIOP (11) } catch (FileNotFoundException e) { System.err.println( "le fichier jndi.properties n'a pas été trouvé"); System.exit(1); } catch (IOException e) { System.err.println( "une erreur d'entrée sortie s'est produite :" + e); e.printStackTrace(); System.exit(2); }

  42. RMI – IIOP (12) try { ic = new InitialContext(props); } catch (Exception e) { System.err.println( "Avez-vous lancé le serveur de nom (orbd) ?"); System.exit(3); } try {

  43. RMI – IIOP (13) // étape 1 : obtenir la référence au servant, // par un appel JNDI objref = ic.lookup("HelloService"); // étape 2 : narrow la référence à l'objet // vers son type réel et invoquer la méthode. hello = (Hello) PortableRemoteObject.narrow( objref, Hello.class); System.out.println(hello.sayHello()); } catch(Exception e ) { System.err.println("lancez le serveur !"); System.exit(3); }

  44. RMI – IIOP (14) • Compilation : on utilisera rmic avec l'option –iiop : rmic –iiop HelloImpl • Ceci aura pour effet de créer 2 classes • _Hello_Stub.class (nom de l'interface) • _HelloImpl_Tie.class (nom de l'implémentation) • Exécution : on commencera par lancer l'annuaire orbd : orbd –ORBInitialPort 1050 • Le serveur et le client s'exécute ensuite normalement.

  45. RMI – IIOP (14) • Limitations de IIOP : Comme c’est un protocole multi – langages, certaines particularités propres à Java ne sont plus utilisables, d'où certaines contraintes : • les constantes dans les interfaces Remote doivent être d’un type primitif ou String. • la surcharge des méthodes n’est pas permise : donc, pas deux méthodes de même nom ! • une méthode, une constante ou une interface Remote ne peuvent pas avoir le même nom à la casse près : donc pas de méthode hello() dans l’interface Hello par exemple. • le DGC n’est pas supporté, donc unrefenced() ne serait pas appelée.

  46. A suivre • On pourrait encore parler de : • RMISecurityManager • RMIClassLoader • Remote Object Activation • l’emploi de HTTP POST pour passer les firewalls • … • Références : http://java.sun.com/j2se/1.5/pdf/rmi-spec-1.5.0.pdf (la spécification de RMI) http://www2.lifl.fr/~seinturi/middleware/rmi.pdf (sur le site de Lionel Seinturier) http://www.labo-sun.com/data/resources/642/files/Labo%20Sun%20-%20Cours%20-%20RMI%20-%202.0.pdf(chez labo-sun)

More Related