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