630 likes | 740 Views
8. Notion d’adaptateur d’objet. Principe. Rendre plusieurs objets (Servant) disponibles dans un serveur Chaque objet est associé à une clé unique dans le serveur Une référence inclut désormais la clé de l’objet Adaptateur d’objet Aiguillage et lien entre le réseau et les objets
E N D
Principe • Rendre plusieurs objets (Servant) disponibles dans un serveur • Chaque objet est associé à une clé unique dans le serveur • Une référence inclut désormais la clé de l’objet • Adaptateur d’objet • Aiguillage et lien entre le réseau et les objets • Gère les clés associées aux objets et leurs références • Raffinement du Manager • Adaptateur : lien référence / objet d’implémentation • Squelette : décodeur de requête pour un objet
Architecture version 8 Servant Servant Client Squelette Squelette Contrat Proxy ObjectAdaptateur Réseau
Côté serveur • Adaptateur d’objet • Gestion du accept et du routage des requêtes • Connecte les objets et crée leurs références • Squelette de service • Défini par rapport au contrat de Servant • Décodeur de messages réseau pour un Servant particulier • Définition de deux squelettes • Programme serveur • Crée un adaptateur d’objet • Crée deux squelettes et servants, les enregistre • Active l’adaptateur d’objet
Contrats interface ServiceA { public String hello (String msg) throws Exception ; public String lower (String msg) throws Exception ; public String upper (String msg) throws Exception ; } interface ServiceB { public boolean isprime (int a) throws Exception ; public int sqr (int a) throws Exception ; }
Code Server public class Server { public void run () throws Exception { ObjectAdapter oa = ObjectAdapter.init () ; SkeletonA skel_a = new SkeletonA (new ServantA ()) ; oa.register (skel_a) ; System.out.println (oa.obj2ref (skel_a)) ; SkeletonB skel_b = new SkeletonB (new ServantB ()) ; oa.register (skel_b) ; System.out.println (oa.obj2ref (skel_b)) ; oa.start () ; } // rest is unchanged
Code ObjectAdapter (i) public class ObjectAdapter extends Thread { private static ObjectAdapter ref ; private ServerSocket asock ; private Vector skeletons ; protected ObjectAdapter () throws Exception { this.skeletons = new Vector () ; this.asock = new ServerSocket (0) ; } public static ObjectAdapter init () throws Exception { if (ObjectAdapter.ref == null) ObjectAdapter.ref = new ObjectAdapter () ; return ObjectAdapter.ref ; }
Code ObjectAdapter (ii) public String obj2ref (Skeleton obj) { int key = this.skeletons.indexOf (obj) ; if (key == -1) return null ; String host = null ; try { host = InetAddress.getLocalHost () .getHostAddress () ; } catch (UnknownHostException uhe) { return null ; } int port = this.asock.getLocalPort () ; return host + ":" + port + ":" + key ; }
Code ObjectAdapter (iii) public void run () { try { while (true) { Socket sock = this.asock.accept () ; BufferedReader in = ... String msg = in.readLine () ; if (msg == null) { // no more to be read sock.close () ; continue ;} int key = Integer.parseInt (msg.substring (0,1)) ;
Code ObjectAdapter (iv) try { Skeleton skel = (Skeleton)this.skeletons.elementAt (key) ; skel.init (sock) ; new Thread (skel) .start () ; } catch (ArrayIndexOutOfBoundsException e) { sock.close () ; } } } catch (Exception e) { return ; } } }
Interface Skeleton interface Skeleton extends Runnable { void init (Socket sock) throws Exception ; }
Code SkeletonA (i) public class SkeletonA implements Skeleton { private ServantA ref ; public SkeletonA (ServantA s) throws Exception { this.ref = s ; } public void init (Socket sock) throws Exception { this.in = ... this.out = ... }
Code SkeletonA (ii) public void run () { try { while (true) { String msg = this.in.readLine () ; if (msg == null) // no more to be read break ; String res ; switch (msg.charAt (0)) { case ’0’: res = this.ref.hello (msg.substring (1)) + "\n" ; break ; // etc. }
Côté client • Modification du proxy • Prise en compte des clés d’objets • Connexion en deux temps au squelette • Définition d’un Proxy pour chacun des services • Deux chaînes Proxy / Skeleton / Servant • Fourniture de la référence comme une propriété • Définition de deux programmes clients • Chacun utilise un proxy pour un service particulier
Code ProxyA (changes) public class ProxyA implements ServiceA { private Socket sock ; private BufferedReader in ; private DataOutputStream out ; public ProxyA (String host, int port, String key) throws Exception { this.sock = new Socket (host, port) ; this.in = ... this.out = ... this.out.writeBytes (key + "\n") ; } }
Code ClientA (changes i) public class ClientA { private ServiceA ref ; public ClientA () throws Exception { this.ref = (ServiceA) this.ref2proxy () ; } public void run (String msg) throws Exception { System.out.println (this.ref.hello (msg)) ; System.out.println (this.ref.lower (msg)) ; System.out.println (this.ref.upper (msg)) ; }
Code ClientA (changes ii) public ProxyA ref2proxy () throws Exception { Properties props = System.getProperties () ; String ref = props.getProperty ("service.reference") ; if (ref == null) throw new Exception ("no server reference given") ; String parts[] = ref.split (":") ; if (parts.length < 3) throw new Exception ("malformed reference") ; String host = parts [0] ; int port = Integer.parseInt (parts [1]) ; String key = parts [2] ; return new ProxyA (host, port, key) ; } }
Bénéfices et limitations • Bénéfices • Serveur multi-services et multi-threadé (Skeleton) • Identification complète d’un service – adresse:port:clé • Les références sont créées au niveau du serveur • Limitations • Comment définir et utiliser des exceptions applicatives ? • Comment simplifier la traduction de référence en Proxy ?
Principe • Exceptions • Ajout d’un code dans les messages de retour • 0 : OK; sinon un numéro d’exception... • Traduction automatique de références en Proxy • Extension des références pour contenir le type des références • Contrôle du typage lors de la création du proxy • Instanciation et initialisation automatique du Proxy
Architecture version 9 Servant Servant Client Squelette Squelette Contrat Proxy ObjectAdaptateur Réseau
Chargement automatique de proxy • Motivations • Transformation d’une référence de service distant en proxy • Contrôler la conformité entre le type du proxy et du service • Modifications des implémentations • Extension des références • Ajout du type du service dans la référence : adresse:port:clé:type • Règles de nommage des classes • NomDuServiceProxy, NomDuServiceSkeleton, NomDuServiceServant • Les squelettes fournissent le type du service (pour construire la référence) • Interface de base Proxy implémentée par tous les proxy (pour init )
Opération ref2proxy (i) public class Util { public static Proxy ref2proxy (String ref) throws Exception { String parts[] = ref.split (":") ; if (parts.length < 4) throw new Exception ("malformed reference") ; String host = parts [0] ; int port = Integer.parseInt (parts [1]) ; String key = parts [2] ; String type = parts [3] ;
Opération ref2proxy (ii) // instanciation du bon proxy (présent dans classpath : convention de nommage) Class pclass = Class.forName (type + "Proxy") ; Proxy p = (Proxy) pclass.newInstance () ; // initialisation p.init (host, port, key) ; return p ; } }
Interface Proxy interface Proxy { publicvoid init (String host, int port, String key) throws Exception ; }
Côté serveur • Gestion des exceptions • Définition des exceptions dans le contrat du service • Modification des squelettes pour transférer les exceptions • Ajout de try catch pour les exceptions applicatives • Ajout d’un code en début de message réponse réseau • Typage des références • Modification des squelettes : fourniture du type du servant • Modification de l’opération obj2ref dans l’adaptateur d’objet
Contrat ServiceB public class DivByZero extends Exception {} interface ServiceB { public boolean isprime (int a) throws Exception; public int sqr (int a) throws Exception ; public int div (int a, int b) throws Exception, DivByZero; }
Interface Skeleton interface Skeleton extends Runnable { void init (Socket sock) throws Exception ; String type () ; }
Code ServiceBSkeleton (modifs) public String type () { return "step9.ServiceB" ; } public void run () { … // managing isprime method from ServiceB case ’0’: int val0 = Integer.parseInt (msg.substring (1)) ; res = "0" + this.ref.isprime (val0) + "\n" ; break ;
Code ServiceBSkeleton (changes ii) // managing div method from ServiceB case ’2’: String args [] = msg.substring (1) .split (" ") ; int arg0 = Integer.parseInt (args [0]) ; int arg1 = Integer.parseInt (args [1]) ; try { res = "0" + this.ref.div (arg0,arg1) + "\n" ; } catch (DivByZero e) { res = "1\n" ; // first exception raised } break ; // etc. }
Code ObjectAdapter public String obj2ref (Skeleton obj) { int key = this.skeletons.indexOf (obj) ; if (key == -1) return null ; String host = null ; try { host = InetAddress.getLocalHost () .getHostAddress () ; } catch (UnknownHostException uhe) { return null ; } int port = this.asock.getLocalPort () ; return host + ":" + port + ":" + key + ":" + obj.type () ; }
Côté client • Modification des proxy • Gestion des exceptions • Si code interne supérieur à 0 alors lever l’exception équivalente • Modification des clients • Utilisation de l’opération ref2proxy • Plus besoin de préciser le nom du Proxy • Utiliser le proxy avec le type de son interface
Code ServiceBProxy (changes i) public boolean isprime (int val) throws Exception { // automatic conversion of int to string out.writeBytes ("0" + val + "\n") ; String res = in.readLine () ; return Boolean.valueOf( res.substring (1)) .booleanValue () ; }
Code ServiceBProxy (changes ii) public int div (int a, int b) throws Exception, DivByZero { out.writeBytes ("2" + a + " " + b + "\n") ; String res = in.readLine () ; if (res.charAt (0) == ’1’) throw new DivByZero () ; return Integer.parseInt (res.substring (1)) ; }
Code ClientB (changes) public ClientB () throws Exception { Properties props = System.getProperties() ; String ref = props.getProperty("service.reference") ; this.refB = (ServiceB) Util.ref2proxy (ref) ; } Exécution avec -D"service.reference=localhost:12345:1:step9.ServiceB"
Bénéfices et limitations • Bénéfices • Bénéfices des exceptions sur le code produit • Contrôle du typage à l’utilisation des références • Limitations • Partage des références compliqué et réduit au fichier • Utiliser facilement plusieurs services dans un même client ?
Principe • Désignation des objets par des noms symboliques • Association : nom symbolique / références • Les serveurs enregistrent une référence avec un nom • Les clients recherchent une référence avec un nom • Service notoire : l’annuaire
Architecture version 10 Nommage Servant Service Servant Client Squelette Squelette Contrat Proxy OAdap… OAdap… Réseau
Annuaire • Service au même titre qu’un autre • Défini par une interface • Hébergé par un serveur • Accessible au travers d’un Proxy • Particularités • Serveur dédié initialisé sur un port prédéfini • Connaître la machine = point d’entrée du système • Proxy particulier pour simplifier l’utilisation • Patron de conception Singleton
Contrat NameService interface NameService { public void export (String name, String ref) throws Exception ; public String lookup (String name) throws Exception ; }
Proxy Naming (extrait) public class Naming implements NameService { private static Naming ref ; private Naming () throws Exception { Properties props = System.getProperties () ; String host = props.getProperty ("naming.host") ; // etc. } public static Naming init () throws Exception { if (Naming.ref == null) Naming.ref = new Naming () ; return Naming.ref ; // etc. } // etc. }
Côté serveur • Diffusion des références • Plus d’affichage sur la ligne de commande • Export des références de services • Vers une application répartie • Le serveur est client du service de nommage • Utilisation du proxy de ce dernier • Unique point d’entrée : référence du service de nommage $ javac -Dnaming.host=localhost step9.Server
Code Server (changes) public void run () throws Exception { Naming ns = Naming.init () ; ObjectAdapter oa = ObjectAdapter.init () ; ServiceASkeleton skel_a = new ServiceASkeleton (new ServiceAServant ()) ; oa.register (skel_a) ; ns.export ("ServiceA", oa.obj2ref (skel_a)) ; ServiceBSkeleton skel_b = new ServiceBSkeleton (new ServiceBServant ()) ; oa.register (skel_b) ; ns.export ("ServiceB", oa.obj2ref (skel_b)) ; oa.run () ; }
Côté client • Récupération des références • Plus de lecture de fichiers texte • Lookup des références de service • Vers une application répartie • Client est client des services A, B et Nommage • Utilisation des proxy de ces trois services • Unique point d’entrée : référence du service de nommage $ javac -Dnaming.host=localhost step9.Client
Code Client (changes) public class Client { public Client () throws Exception { Naming ns = Naming.init () ; String ref_a = ns.lookup ("ServiceA") ; this.refA = (ServiceA) Util.ref2proxy (ref_a) ; String ref_b = ns.lookup ("ServiceB") ; this.refB = (ServiceB) Util.ref2proxy (ref_b) ; } // merge of previous clients’ code }
Bénéfices et limitations • Bénéfices • Unique point d’entrée dans le système • Partage simplifié des références de services • Limitations • On écrit un peu toujours la même chose... • On risque à chaque fois de faire des erreurs (skel/proxy)