Modèle client
Transcription
Modèle client
Middleware à composants Daniel Hagimont IRIT/ENSEEIHT 2 rue Charles Camichel - BP 7122 31071 TOULOUSE CEDEX 7 [email protected] http://hagimont.perso.enseeiht.fr 1 Des objets aux composants Les objets : première approche pour la décomposition Encapsulation (séparation interface-réalisation) Mécanismes de réutilisation (héritage, délégation) Souvent : outils langage pour le développement Limitation des objets Pas d'expression explicite des ressources requises par un objet (autres objets, services fournis par l'environnement) Pas de vue globale de l'architecture d'une application Pas de possibilité d'expression de besoins non-fonctionnels (par ex. persistance, transactions, etc.) Peu d'outils pour le déploiement et l'administration Distinction entre support au développement et support à la construction 2 Middleware à composants Environnement de construction d'application À partir de brique logicielles (composants) Comme un assemblage (architecture logicielle) Environnement d'exécution de ces applications Déploiement d'une architecture Reconfiguration de cette architecture CCCBCLC CCC CCCBCLC CCC CCCBCLC CCC CBC BCLC LC CBC BCLC LC CLC C LC CLC C LC CCCBCLC CCC CLC C LC CBC BCLC LC CLC C LC 3 Que doit fournir un modèle de composants ? Encapsulation (composants) Interface = seule voie d'accès, séparation interface-réalisation Possibilité de définir des interface multiples Composition Dépendances explicites (expression des ressources fournies et requises), connecteurs Composition hiérarchique (un assemblage de composants est un composant) Capacité de description globale Si possible exprimée formellement (langage de description) Réutilisation et évolution Modèles génériques de composants (types de composants) Adaptation (interfaces de contrôle) Reconfiguration 4 Que doit fournir une infrastructure à composants ? Couverture du cycle de vie Non limitée aux phases de développement et d'exécution Administration • Déploiement : installation et activation des composants • Surveillance : collecte et intégration de données, tenue à jour de la configuration • Contrôle : réaction aux événements critiques (alarme, surcharge, détection d'erreur) Service communs Propriétés non-fonctionnelles (persistance, transactions, QoS) 5 Le modèle à composants Fractal Fractal Modèle général, peu de restrictions Fondé sur une base mathématique rigoureuse Plusieurs implémentations Très utilisé dans la recherche Source http://fractal.objectweb.org Auteurs Eric Bruneton (France Telecom R&D) Jean-Bernard Stefani (INRIA) 6 Aperçu : l'exemple Comanche Serveur HTTP minimal Écoute sur un ServerSocket Pour chaque connexion: • • • • Lance un nouveau thread Lit l'URL Envoie le fichier demandé Ou un message d'erreur public class Server implements Runnable { private Socket s; public Server (Socket s) { this.s = s; } public static void main (String[] args) throws IOException { ServerSocket s = new ServerSocket(8080); while (true) { new Thread(new Server(s.accept())).start(); } } public void run () { try { InputStreamReader in = new InputStreamReader(s.getInputStream()); PrintStream out = new PrintStream(s.getOutputStream()); String rq = new LineNumberReader(in).readLine(); System.out.println(rq); if (rq.startsWith("GET ")) { File f = new File(rq.substring(5, rq.indexOf(' ', 4))); if (f.exists() && !f.isDirectory()) { InputStream is = new FileInputStream(f); byte[] data = new byte[is.available()]; is.read(data); is.close(); out.print("HTTP/1.0 200 OK\n\n"); out.write(data); } else { out.print("HTTP/1.0 404 Not Found\n\n"); out.print("<html>Document not found.</html>"); } } out.close(); s.close(); } catch (IOException _) { } } 7 } Conception : trouver les composants Composants statiques Durée de vie • Celle de l'application Correspondent aux « services » Composants dynamiques Durée de vie plus courte Correspondent aux « données » Définition des composants Trouver • Les services • [ les structures de données ] Un service = un composant public class Server implements Runnable { private Socket s; public Server (Socket s) { this.s = s; } public static void main (String[] args) throws IOException { ServerSocket s = new ServerSocket(8080); while (true) { new Thread(new Server(s.accept())).start(); } } public void run () { try { InputStreamReader in = new InputStreamReader(s.getInputStream()); PrintStream out = new PrintStream(s.getOutputStream()); String rq = new LineNumberReader(in).readLine(); System.out.println(rq); if (rq.startsWith("GET ")) { File f = new File(rq.substring(5, rq.indexOf(' ', 4))); if (f.exists() && !f.isDirectory()) { InputStream is = new FileInputStream(f); byte[] data = new byte[is.available()]; is.read(data); is.close(); out.print("HTTP/1.0 200 OK\n\n"); out.write(data); } else { out.print("HTTP/1.0 404 Not Found\n\n"); out.print("<html>Document not found.</html>"); } } out.close(); s.close(); } catch (IOException _) { } } 8 } Conception : trouver les composants Receiver Scheduler Analyzer Logger FileHandler ErrorHandler public class Server implements Runnable { private Socket s; public Server (Socket s) { this.s = s; } public static void main (String[] args) throws IOException { ServerSocket s = new ServerSocket(8080); while (true) { new Thread(new Server(s.accept())).start(); } } public void run () { try { InputStreamReader in = new InputStreamReader(s.getInputStream()); PrintStream out = new PrintStream(s.getOutputStream()); String rq = new LineNumberReader(in).readLine(); System.out.println(rq); if (rq.startsWith("GET ")) { File f = new File(rq.substring(5, rq.indexOf(' ', 4))); if (f.exists() && !f.isDirectory()) { InputStream is = new FileInputStream(f); byte[] data = new byte[is.available()]; is.read(data); is.close(); out.print("HTTP/1.0 200 OK\n\n"); out.write(data); } else { out.print("HTTP/1.0 404 Not Found\n\n"); out.print("<html>Document not found.</html>"); } } out.close(); s.close(); } catch (IOException _) { } } 9 } Conception : définir l'architecture Structure hiérarchique : Correspond aux niveaux d'abstraction Dépendances : Liaisons entre les composants Receiver Frontend Analyzer Logger Scheduler File Handler Dispatcher Comanche Backend Handler Error Handler 10 Conception : définir les interfaces Interfaces entre composants : Doivent être définis avec attention • Pour être le plus stable possible ne doivent concerner que les aspects fonctionnels Pour Comanche public interface Logger { void log (String msg); } public interface Scheduler { void schedule (Runnable task); } public interface RequestHandler { void handleRequest (Request r) throws IOException; } • public class Request { Socket s; Reader in; PrintStream out; String url; } • Passage en paramètre de la requête 11 Implémentation : composants Composants sans dépendances : Implémentés comme en Java standard public class BasicLogger implements Logger { public void log (String msg) { System.out.println(msg); } } public class SequentialScheduler implements Scheduler { public synchronized void schedule (Runnable task) { task.run(); } } public class MultiThreadScheduler implements Scheduler { public void schedule (Runnable task) { new Thread(task).start(); } } 12 Implémentation : composants Composants avec dépendances : Première solution : ne permet pas la configuration statique public class RequestReceiver implements Runnable { private Scheduler s = new MultiThreadScheduler(); private RequestHandler rh = new RequestAnalyzer(); // rest of the code not shown } Deuxième solution: ne permet pas la reconfiguration dynamique public class RequestReceiver implements Runnable { private Scheduler s; private RequestHandler rh; public RequestReceiver (Scheduler s, RequestHandler rh) { this.s = s; this.rh = rh; } // rest of the code not shown } 13 Implémentation : composants Composants avec dépendances : public class RequestReceiver implements Runnable, BindingController { private Scheduler s; private RequestHandler rh; public String[] listFc () { return new String[] { "s", "rh" }; } public Object lookupFc (String n) { if (n.equals("s")) { return s; } else if (n.equals("rh")) { return rh; } else return null; } public void bindFc (String n, Object v) { if (n.equals("s")) { s = (Scheduler)v; } else if (n.equals("rh")) { rh = (RequestHandler)v; } } public void unbindFc (String n) { if (n.equals("s")) { s = null; } else if (n.equals("rh")) { rh = null; } } // … 14 Configuration : par programme public class Server { Avantages : public static void main (String[] args) { RequestReceiver rr = new RequestReceiver(); Méthode la plus directe Aucun outils requis RequestAnalyzer ra = new RequestAnalyzer(); RequestDispatcher rd = new RequestDispatcher(); Inconvénients : FileRequestHandler frh = new FileRequestHandler(); Propices aux erreurs Ne montre pas l'architecture ErrorRequestHandler erh = new ErrorRequestHandler(); Scheduler s = new MultiThreadScheduler(); Logger l = new BasicLogger(); rr.bindFc("rh", ra); rr.bindFc("s", s); ra.bindFc("rh", rd); ra.bindFc("l", l); rd.bindFc("h0", frh); rd.bindFc("h1", erh); rr.run(); } } 15 Configuration : avec un ADL Avantages : Bonne séparation des aspects architecture et déploiement Vérifications statiques possibles • Liaisons invalides, • Liaisons manquantes, • … Inconvénients : L'architecture n'est pas très visible <definition name="HandlerType"> <interface name="rh" role="server" signature="comanche.RequestHandler"/> </definition> <definition name="FileHandler" extends="HandlerType"> <content class="comanche.FileRequestHandler"/> </definition> <definition name="Comanche" extends="RunnableType"> <component name="fe" definition="Frontend"/> <component name="be" definition="Backend"/> <binding client="this.r" server="fe.r"/> <binding client="fe.rh" server="be.rh"/> </definition> … 16 Configuration : avec un outil graphique Outils graphique pour éditer des fichiers ADL La représentation en graphe montre clairement l'architecture 17 Reconfiguration Statique : stopper l'application, changer l'ADL, redémarrer Pas toujours possible, par exemple pour raisons de disponibilité Dynamique : reconfigurer l'application pendant son exécution Pose des problèmes de cohérence • Outil de base pour aider à les résoudre : gestion du cycle de vie Exemple : Remplacer le composant FileHandler dynamiquement : • RequestHandler rh = new FileAndDirRequestHandler(); • rd.unbindFc("h0"); • rd.bindFc("h0", rh); • Le RequestDispatcher (composant englobant) doit être suspendu pendant l'opération 18 Cycle de vie Interface LifeCycleController : exemple de solution public class RequestDispatcher implements RequestHandler, BindingController, LifeCycleController { private boolean started; private int counter; public String getFcState () { return started ? STARTED : STOPPED; } public synchronized void startFc () { started = true; notifyAll(); } public synchronized void stopFc () { while (counter > 0) { wait(); } started = false; } public void handleRequest (Request r) throws IOException { synchronized (this) { while (!started) { wait(); } ++counter; } try { // original code } finally { synchronized (this) { --counter; if (counter == 0) { notify(); } } } } // rest of the class unchanged 19 Reconfiguration : autres APIs Avant de stopper ou de reconfigurer un composant, il faut obtenir sa référence D'où les interfaces d'introspection (et de reconfiguration) suivantes : public interface Component { public interface ContentController { Object[] getFcInterfaces (); Object[] getFcInternalInterfaces (); Object getFcInterface (String itfName); Object getFcInternaInterface(String itfName); Type getFcType (); Component[] getFcSubComponents (); void addFcSubComponent (Component c); } void removeFcSubComponent(Component c); } Une interface Fractal implante à la fois l'interface Interface et l'interface métier, ce qui explique le type Object. 20 Quelques choix de déploiement C CC BC LC C CC BC LC C CC BC LC C BC LC C BC LC C LC C CC BC LC C LC C LC C BC LC C LC 21 Quelques choix de déploiement C CC BC LC C BC LC C BC LC C LC C LC C LC C BC LC C LC 22 En résumé Les composants Encapsulent du code fonctionnel Possèdent des interfaces (requises et fournies) Possèdent un ensemble de contrôleurs Les contrôleurs Fournissent une API de gestion des composants (non fonctionnelles) L'ADL est un langage de description • des composants • des assemblages de composants 23 Fractal : les composants De l’extérieur, un composant Fractal : Est une boite noire, Avec des points d’accès appelés interfaces (externes) • Chaque interface à un nom (unique dans le composant) • Une signature (ensemble d’opérations – i.e. méthodes en Java) • Un rôle (client, serveur ou contrôle) component, Component interfaces serveur -ou fournies -reçoivent des appels -à gauche par convention s, I u, I t, J v, I w, K interfaces de contrôle interfaces clientes -ou requises -émettent des appels -à droite par convention 24 Fractal : les composants A l’intérieur, un composant : À un contrôleur et un contenu Contenu • Composants primitifs • Composants composites • Composants partagés Le contrôleur peut (entre autres) : • Fournir des fonctions d’introspection et de reconfiguration • Intercepter les appels d’opération entrants et sortants • Pour les aspects non fonctionnels (cycle de vie, transactions, sécurité, …) • Modifier le comportement de ses sous contrôleurs 25 Fractal : les liaisons Liaisons Liaison primitive • Lie une interface cliente à une interface serveur • Dans le même espace d’adressage Liaison multiple • Lie une interface cliente • À un nombre arbitraire d’interface serveur Liaison composite • Est formée d'un ensemble de composants de liaisons Liaisons normales, d’import et d’export 26 Fractal : interfaces complémentaires Interfaces internes Interfaces du contrôleur Visibles seulement des sous composants Interfaces complémentaires Deux interfaces internes et externes De même nom et même signature, mais de rôle opposés interfaces complémentaires liaison d'export Exporte une interface d'un sous composant à l'extérieur du composant englobant liaison d'import Importe une interface externe à l'intérieur d'un composant composite 27 Fractal : typage Type de composant c, J ensemble de types d’interface non modifiable à l’exécution c : interface de type singleton Type d’interface nom signature contingence • obligatoire, optionnelle cardinalité • singleton, collection c1, J c2, J c3, J c : interface de type collection 28 Modèle : API Le modèle se définit principalement au travers de ses API de contrôle 29 Modèle : API Les types 30 Modèle : API Les factories 31 Modèle : API Les instances 32 Modèle : API Les contrôleurs 33 Déploiement avec l’API Component boot = Fractal.getBootstrapComponent(); class Fractal static getBootstrapComponent interface Component Object[] getFcInterfaces Object getFcInterface getFcType interface Interface Component getFcItfOwner getFcItfName m m rComp cComp s s sComp getFcItfType isFcInternalItf 34 Déploiement avec l’API TypeFactory tf = Fractal.getTypeFactory(boot); interface Component Interface[] getFcInterfaces Interface getFcInterface getFcType TypeFactory createFcInterfaceType createFcType On devrait écrire (TypeFactory)boot.getFcInterface("type-factory"); 35 Déploiement avec l’API ComponentType rType = tf.createFcType(new InterfaceType[] { tf.createFcItfType("m", "pkg.Main", false, false, false)}); TypeFactory InterfaceType extends Type createFcInterfaceType getFcItfName createFcType getFcItfSignature ComponentType extends Type m getFcInterfaceTypes isFcOptionalItf getFcInterfaceType isFcCollectionItf m rComp isFcClientItf cComp s s sComp 36 Déploiement avec l’API ComponentType cType = tf.createFcType(new InterfaceType[] { tf.createFcItfType("m", "pkg.Main", false, false, false), tf.createFcItfType("s", "pkg.Service", true, false, false)}); ComponentType sType = tf.createFcType(new InterfaceType[] { tf.createFcItfType("s", "pkg.Service", false, false, false), tf.createFcItfType("attribute-controller","pkg.ServiceAttributes",…)}); nom m m rComp cComp s signature isClient isOptional isCollection s sComp 37 Déploiement avec l’API GenericFactory gf = Fractal.getGenericFactory(boot); GenericFactory newFcInstance 38 Déploiement avec l’API Component rComp = gf.newFcInstance(rType, "composite", null); LifeCycleController, BindingController, ContentController contenu m rComp 39 Déploiement avec l’API Component cComp = gf.newFcInstance(cType, "primitive", "pkg.ClientImpl"); LifeCycleController, BindingController m m cComp s rComp 40 Déploiement avec l’API Component sComp = gf.newFcInstance(sType, "parametricPrimitive", "pkg.ServerImpl"); si attributs m m cComp s s sComp rComp 41 Déploiement avec l’API Fractal.getContentController(rComp).addFcSubComponent(cComp); ContentController getFcInternalInterfaces getFcInternalInterface getFcSubComponents addFcSubComponent removeFcSubComponent m m cComp s s sComp rComp 42 Déploiement avec l’API Fractal.getContentController(rComp).addFcSubComponent(sComp); m m cComp s s sComp rComp 43 Déploiement avec l’API Fractal.getBindingController(rComp).bindFc( "m", cComp.getFcInterface("m")); BindingController listFc lookupFc bindFc unbindFc m m cComp s s sComp rComp 44 Déploiement avec l’API Fractal.getBindingController(cComp).bindFc( "s", sComp.getFcInterface("s")); m m cComp s s sComp rComp 45 Déploiement avec l’API Fractal.getLifeCycleController(rComp).startFc(); ((pkg.Main)rComp.getFcInterface("m")).main(null); LifeCycleController getFcState startFc stopFc m m cComp s s sComp rComp 46 Déploiement avec Fractal ADL Définition des types de composants <definition name="RootType"> <interface name="m" role="server" signature="pkg.Main" contingency="mandatory" cardinality="singleton"/> </definition> optionnel <definition name="ClientType"> <interface name="m" role="server" signature="pkg.Main"/> <interface name="s" role="client" signature="pkg.Service"/> </definition> 47 Déploiement avec Fractal ADL Héritage entre définitions <definition name="ClientType" extends="RootType"> <interface name="s" role="client" signature="pkg.Service"/> </definition> Est équivalent à : <definition name="ClientType"> <interface name="m" role="server" signature="pkg.Main" contingency="mandatory" cardinality="singleton"/> <interface name="s" role="client" signature="pkg.Service"/> </definition> 48 Déploiement avec Fractal ADL Héritage entre définitions (suite) <definition name="ExtendedClientType" extends="ClientType"> <interface name="s" role="client" signature="pkg.ExtendedService"/> </definition> Est équivalent à : <definition name="ExtendedClientType"> <interface name="m" role="server" signature="pkg.Main" contingency="mandatory" cardinality="singleton"/> <interface name="s" role="client" signature="pkg.ExtendedService"/> </definition> 49 Déploiement avec Fractal ADL Définition des composants primitifs <definition name="BasicClient" extends="ClientType"> <content class="pkg.ClientImpl"/> </definition> <definition name="BasicServer" extends="ServerType"> <content class="pkg.ServerImpl"/> <attributes signature="pkg.ServiceAttributes"> <attribute name="Header" value="-> "/> <attribute name="Count" value="1"/> </attributes> </definition> 50 Déploiement avec Fractal ADL Définition des composants composites <definition name="BasicClientServer" extends="RootType"> <component name="client" definition="BasicClient"/> <component name="server" definition="BasicServer"/> <binding client="this.m" server="client.m"/> <binding client="client.s" server="server.s"/> </definition> m m client s s server BasicClientServer 51 Déploiement avec Fractal ADL Définition des composants composites (suite) <definition name="AbstractClientServer" extends="RootType"> <component name="client" definition="ClientType"/> <!-- pas d'implémentation --> <component name="server" definition="ServerType"/> <!-- pas d'implémentation --> <binding client="this.m" server="client.m"/> <binding client="client.s" server="server.s"/> </definition> <definition name="BasicClientServer" extends="AbstractClientServer"> <component name="client" definition="BasicClient"/> <component name="server" definition="BasicServer"/> </definition> 52 Déploiement avec Fractal ADL Création d'un composant avec l'ADL (en Java) en ligne de commande java … org.objectweb.fractal.adl.Launcher template par programme Factory f = FactoryFactory.getFactory(FRACTAL_BACKEND); Component c = (Component)f.newComponent("template", null); les définitions doivent être dans le CLASSPATH 53 Composants primitifs : utilisation d'annotation (Fraclet) @Interface : une interface fournie @Interface(name="service") public interface Service {…} @FractalComponent : un composant Fournit les interfaces qu'il implante @FractalComponent(controllerDesc="primitive") public class ServerA implements Service {…} @Provides : une interface fournie importée (pré-compilée) @Provides(interfaces={@Interface(name="r", signature=java.lang.Runnable.class, cardinality=Cardinality.SINGLETON, contingency=Contingeny.MANDATORY)} public class Client implements Runnable {…} 54 Composants primitifs : utilisation d'annotation (Fraclet) @Attribute: une propriété configurable d'un composant Génère un controller CompAttibuteController (pour un composant Comp) @Attribute(value="hello") private String message ; @Requires : une interface requise @Requires(name = "service", cardinality = Cardinality.COLLECTION) private Map<String,Service> services = new HashMap<String,Service>(); @LifeCycle : une méthode à exécuter lors d'un start/stop @LifeCycle(on=LifeCycleType.START) private void init() {... } 55 Reconfiguration dynamique ContentController rCompCC = Fractal.getContentController(rComp); Component cComp = rCompCC.getFcSubComponents()[0]; Component sComp = rCompCC.getFcSubComponents()[1]; Fractal.getLifeCycleController(rComp).stopFc(); Fractal.getBindingController(cComp).unbindFc("s"); m m cComp s s sComp rComp 56 Reconfiguration dynamique rCompCC.removeFcSubComponent(sComp); m m cComp s rComp 57 Reconfiguration dynamique sComp = gf.newFcInstance(sType, "primitive", "pkg.NewServerImpl"); rCompCC.addFcSubComponent(sComp); m m cComp s ss sComp rComp 58 Reconfiguration dynamique Fractal.getBindingController(cComp). bindFc("s", sComp.getFcInterface("s")); Fractal.getLifeCycleController(rComp).startFc(); m m cComp s s sComp rComp 59 Bilan de Fractal Principes architecturaux Construction par composition de briques logicielles : les composants Vision homogène de la topologie des systèmes logiciels applications, intergiciel, systèmes d’exploitation Couverture du cycle de vie complet : - développement, déploiement, supervision Réalisation d’un support d’exécution pour composants : Fractal Julia Le modèle peut être mis en oeuvre dans de nombreux environnements (Think, Plasma ...) http://fractal.ow2.org des tutoriaux en ligne ... 60