Exercice 2 ========= Il s'agit de reproduire le RMIRegistry, qui est une application RMI comme une autre. Elle a donc un CLASSPATH éventuellement initialisé, ce qui permet de télécharger des classes. Notamment, les .class des interfaces distantes dont on lui demande de stocker des objets qui sont en fait des stubs exposant ces interfaces. AUCUN DES ELEVES n'a réussi à me donner la conviction que les principes détaillés ci dessous étaient globalement compris; sauf un qui n'était pas trop loin puisque il a fait mention de problématiques de classpath ou téléchargement, et de sérialisation du stub. Il a donc eu le maximum des points pour l'exo. à quelques autres détails près (4,5 / 5) Ainsi, ce que j'attendais pour la question 2 et l'ensemble de l'exercice 2 au sens large : lorsque un objet RMI, implémentant interfaceDistante, et étendant UnicastRemoteObject veut enregistrer un stub de lui - même auprès du rmi registry - il sérialise un objet stub, qui implémente l'interface distante, et dont le .class a été généré à la volée par la JVM où s'exécute l'objet RMI (on précisait dans le sujet que la version du JDK est récente, pas besoin d'utiliser rmic pour générer la classe d'un stub). L'OBJET stub est donc sérialisé, et dans ce flux, il y a indiqué que le .class correspondant est celui de la classe stub. Le nom d'une classe stub est normalisé et dans notre exemple c'est interfaceDistanteStub.class - Rmi registry reçoit ce flux, il a donc besoin de connaitre le .class qui correspond à l'objet mis sous forme de flux. Il a donc besoin de trouver le fichier interfaceDistanteStub.class. Comme on est sur un JDK récent, il suffit de trouver la classe interfaceDistante.class. Puis de générer , grâce à elle, à la volée, la classe interfaceDistanteStub.class Comment le rmiRegistry peut trouver le fichier interfaceDistante.class ? Grâce à son CLASSPATH ! Il télécharge donc dans sa JVM ce fichier. Si le rmiReg a été démarré sans CLASSPATH, souvenez vous qu'on peut utiliser le téléchargement dynamique de classe. Si le serveur a sérialisé l'objet stub en indiquant une URL de codebase, alors, le rmiRegistry va ouvrir une connection HTTP vers le serveur web, et , ce dernier va pouvoir lui donner le fichier interfaceDistante.class, que nous aurons pris soin de placer évidemment auprès du serveur web. - donc RMI registry sait comment désérialiser le flux reçu afin de reconstruire l'objet stub. Il le caste en fait, non pas en interfaceDistante (puisque ce serait un code rmi registry dépendant d'un client précis, ce que nous ne voulons pas), mais en le nom de l'interface donné dans le sujet, à savoir quelque chose comme RemoteReference, qui plus est, Serializable. C'est pour celà qu'on a pu désérialiser l'objet reçu. - le rmiregistry met donc dans une hashmap, avec comme clé le nom (la string) passée au moment du bind, une référence locale vers l'objet stub entièrement reconstruit grâce au processus de désérialisation. - quand un client fait un lookup, il suffit, grâce à la string passée en paramètre, d'aller chercher dans la hashmap. Ce qu'on y trouve est l'objet stub. Il suffit donc de sérialiser cet objet dans un nouveau flux, d'indiquer le nom de la classe correspondant, dans ce flux, et d'envoyer celà vers le client - coté client, en recevant les données du flux, on sait désérialiser. En effet, coté client, pour pouvoir programmer le code métier, on connait déjà le fichier interfaceDistante.class. Il suffit donc juste à la jvm cliente de générer à la volée le bytecode de interfaceDistanteStub.class, par réflexion, et de s"en servir pour reconstruire (désérialiser) le flux reçu afin de récupérer un objet stub. Si du téléchargement de classe est actif, alors, il y a aussi dans ce flux fabriqué par le rmiregistry, l'information du codebase récupérée par le registry quand il avait désérialisé l'objet stub reçu du serveur RMI. Cette info est repassée dans le flux sérialisé qui est envoyé au client. Ainsi, le client pourrait ne pas disposer de interfaceDistante.class, et vous pouvez vérifier ceci en remarquant que le serveur web répond à une requête du client correspondant à la ligne de code Object stub = Naming.lookup("serveur"); Par contre, là où le client devient embété, c'est à l'instruction suivante (qu'on met d'habitude sur la même ligne de code; bien que ce ne soit pas une obligation) interfaceDistante id = (interfaceDistante) stub; En effet, même si interfaceDistante.class a été téléchargé par http, il y a une erreur de la part de la JVM, car, elle a besoin pour faire un cast Java, semble-t-il, de trouver un fichier sur le disque. Elle n'est pas capable de réaliser qu'il existe dans le class loader de la JVM, un bytecode déjà chargé, correspondant à interfaceDistante.class. ======= Exercice 3 ========== Globalement bien réussi par les élèves ! Attention, il y avait un petit piège, dans lequel nombreux sont tombés. L'info quant à savoir qu'une thread pouvait s'appeler main , et non RMI TCP xxx.xxx.xxx.xxx n'etait pas utile, sauf si vous aviez décidé d'ajouter un System.out.println(" je suis la thread ... "+getName()) dans l'implémentation de m2(). Q1) Quand le client récupère, via getAccess(), un stub vers service métier qui implante interfaceDistante, tous les appels vers le service métier sont donc des appels RMI, et les traces coté serveur, sont toutes RMI TCP xxx.xxx.xxx.xxx (puisque déclenchés par des appels RMI), et dans l'ordre d'appel du client m1,m1,m2. Q2) En effet, le principe du smart proxy, concrétisé par les traces, est le suivant. Au lieu de renvoyer simplement et uniquement un stub vers service métier, le serveur, dans getAccess, renvoie un objet SERIALISE qui est un objet de type SmartProxyServiceMetier. Dans un attribut, il y a 1 objet stub (lui même sérialisé par copie profonde), vers l'objet service metier. Ce sera utile pour appeler m1() toujours en RMI, depuis le client. En effet m1 doit tjs être appelé vraiment, par le client (ou les clients), puisque la logique métier fait que chaque appel renvoie une réponse différente. Il y a un second attribut String, qui est la valeur immuable , de la chaine déginie dans l'implémentation du service métier. Pour initialiser cet attribut, avvant la sérialisation du smart proxy, renvoyé au client: il faut invoquer, une seule fois, la méthode m2() du serveur RMI. C'est ce qui est fait dans l'implémentation de getAccess() EN conséquence, les traces coté serveur sont d'abord: l'appel de m2(), initiée depuis getAccess(), et comme getAccess() est invoquée depuis le client en RMI, la thread qui exécute m2 est une thread RMI TCP (et pas une thread main). Ensuite, le client invoque m1 deux fois, qui sont des appels réseau, donc, pas de changement par rapport à Q1. Si dans l'implémentation de m2(), dans le smart proxy, vous aviez mis un SOP, alors, lorsque le client invoque m2(), vous auriez effectivement vu un message sur la console client. Et ce message montrerait que c'est la thread main (du client) qui exécute m2, renforçant la preuve que l'appel à m2() est bien un appel purement local, coté client. Pas besoin que cet appel traverse le réseau, puisque la réponse de m2() est déjà en cache dans l'objet smart proxy reçu en retour du getAccess()