Objets répartis : principes
Un schéma "idéal" de gestion d'objets répartis est représenté sur la figure ci-dessous. Un objet unique est partagé par un ensemble de processus, répartis sur plusieurs sites. Chaque processus peut appeler les méthodes de l'objet, et toute modification de l'état de l'objet devient immédiatement visible à tous les processus (on n'aborde pas ici les problèmes de synchronisation liés au partage - on suppose pour simplifier que tout appel de méthode s'exécute en exclusion mutuelle).
Le schéma réel (figure ci-dessous) est une approximation de ce schéma idéal. En fait, un processus particulier se comporte comme le serveur de l'objet et les autres comme des clients. Néanmoins, après une phase initiale de mise en place, la situation est voisine de celle du schéma idéal, en ce sens que tout processus (client ou serveur) peut exécuter les méthodes de l'objet et tout processus voit immédiatement les changements de l'état de l'objet provoqués par les autres processus.
La phase initiale de mise en place comporte les étapes suivantes :
N. B. Bien que leur principe soit simple, les mécanismes de répartition des objets dans Tcl_DP peuvent paraître difficiles à comprendre au premier abord. Il est donc conseillé de lire la description qui suit, sans nécessairement essayer de comprendre tous les détails en première lecture, puis d'étudier soigneusement l'exemple fourni (et de le faire exécuter). On pourra ensuite revenir à la description des commandes.
Un objet est un exemplaire (ou instance) d'une classe, qui définit la structure de l'état des instances de cette classe et le programme des méthodes qui leur sont applicables. L'état est composé de champs ("slots") dont chacun a un nom et une valeur. Par exemple, pour une classe définissant des points (cf exemple plus loin), l'état d'un point est composé de 3 champs de noms x, y et c. Leur valeur est respectivement l'abscisse, l'ordonnée et la couleur du point.
Si class est le nom d'une classe, on définit ainsi une méthode de nom method dans cette classe :
proc class.method {object arg0 ... argn} {où class est le nom de la classe, object est l'identificateur de l'objet auquel s'applique la méthode et arg0 ... argn les paramètres de la méthode. On doit toujours définir au minimum trois méthodes : configure, slot-value, et destroy.<corps de la méthode>}
configure permet d'accéder à l'état de l'objet et de le manipuler.La définition des autres méthodes est spécifique de la classe considérée.
slot-value prend en paramètre le nom d'un champ et rend la valeur de ce champ.
destroy détruit l'objet sur lequel elle est appelée.
Attention : pour appeler une méthode method sur un objet object, la notation est :
object method ?args?Pour toute classe, il faut définir une procédure de création des instances de cette classe. Cette procédure sert également à définir la structure de l'état des objets de la classe (champs). Tcl_DP fournit pour cela les commandes suivantes.
dp_objectCreateProc class objectcréee un nouvel objet, instance d'une classe. Les paramètres sont le nom de la classe et l'identificateur de l'instance. Ce dernier nom va désormais désigner le nouvel objet créé.
Les commandes suivantes permettent de définir les champs d'un objet, et de consulter et de modifier leur valeur.
dp_objectSlotSet object slot valuedéfinit un champ. Les paramètres sont le nom d'un objet, le nom d'un champ et la valeur initiale du champ.
dp_objectSlots objectrenvoie la liste des champs de l'objet passé en paramètre.
dp_objectConfigure class object ?args?permet de configurer un objet, pour pouvoir y accéder. Paramètres : nom d'un objet, nom d'une classe, et suite de couples -<nom de champ> <valeur du champ> (noter le signe - devant le nom du champ ; construction analogue à la configuration des widgets en TK).
Répartition et partage des objets
Principe
Un objet destiné à être réparti est d'abord créé dans un processus serveur. Il est ensuite distribué aux processus clients qui en font la demande. La distribution est réalisée par la commande
dp_DistributeObject object processes makeObjectdont les paramètres sont : object l'objet à distribuer, processes la liste des processus auxquels il doit être distribué (désignés par leur descripteur, voir fiche sur l'appel de procédure à distance), et makeObject est le nom de la procédure de création associée à la classe dont l'objet est une instance. Comme le serveur ne connaît pas en général a priori la liste des clients potentiels de l'objet, il exécute en fait cette commande sur demande du client, transmise par le mécanisme de l'appel de procédure à distance.
Manipulation des objets répartis
Dans les méthodes applicables aux objets répartis, la manipulation des champs d'un objet se fait par des commandes spéciales qui assurent notamment que les modifications faites par un processus sont répercutées chez tous les processus qui ont accès à cet objet (ce que ne fait pas la commande dp_objectSlotSet) :
dp_setf object slot value affecte la valeur value au champ slot de l'objet object.Déclencheurs
dp_getf object slot renvoie la valeur du champ slot de l'objet object.
Tcl-DP permet d'associer l'exécution d'une commande à une modification d'un objet par dp_setf, au moyen d'un mécanisme appelé déclencheur (trigger). La commande qui réalise cette assoation est
dp_SetTrigger when object slot trigger_listobject et slot désignent respectivement l'objet réparti et le champ de cet objet dont la modification entraîne l'exécution des commandes données dans trigger_list. Le paramètre when indique quand l'évaluation de ces commandes doit avoir lieu : la valeur before indique que l'évaluation a lieu avant que la modification soit faite et la valeur after indique que l'évaluation a lieu après que la modification ait été faite.
On peut consulter les déclencheurs associés aux champs d'un objet (dp_GetTrigger), rajouter une commande à la liste des déclencheurs pour un champ d'un objet (dp_AppendTrigger), ou supprimer des déclencheurs (dp_ReleaseTrigger).
Exemple
Cet exemple simple illustre la gestion d'objets répartis de classe point. Les programmes sont dans quatre fichiers reproduits ci-après (également dans le répertoire : /h/thales/u8/enseigt/krakowis/tp-tcl/Point/)
graphique.tcl contient une procédure pour afficher les points dans une fenêtre (pour simplifier, l'ancienne image du point n'est pas effacée)
point-serveur.tcl contient le programme du serveur de points.
point-client.tcl contient le programme du client.
fichier point-class.tcl
# procedure de creation
# on cree un nouvel objet aPoint
# avec des parametres de creation
dans une liste args
package require dp
proc makePoint {aPoint args} {
# creation de l'objet avec le nom point passe' en parametre}
eval dp_objectCreateProc point $aPoint;
#Ces objets ont trois champs : coordonnees x et y, couleur c
dp_objectSlotSet $aPoint x 0;
dp_objectSlotSet $aPoint y 0;
dp_objectSlotSet $aPoint c black;
eval $aPoint configure $args; # "configuration" de l'objet (obligatoire)
# declaration des methodes de la classe point
proc point.configure {aPoint args} {
eval dp_objectConfigure point $aPoint $args}
proc point.slot-value {aPoint slot} {
dp_objectSlot $aPoint $slot; # renvoie la valeur du champ ?slot'}
proc point.destroy {aPoint} {
dp_objectFree $aPoint; # détruit l'objet ?aPoint'}
proc point.setX {aPoint newX} {
dp_setf $aPoint x $newX; # change la valeur du champ ?x' (abscisse)}
proc point.setY {aPoint newY} {
dp_setf $aPoint y $newY; # change la valeur du champ ?y' (ordonnée)}
proc point.setC {aPoint newC} {
dp_setf $aPoint c $newC; # change la valeur du champ ?c' (couleur)};
# dessine le point sur l'écran
proc point.draw {aPoint args} {
set xx [dp_getf $aPoint x]; # xx, yy, cc variables locales}
set yy [dp_getf $aPoint y];
set cc [dp_getf $aPoint c];
dot $xx $yy $cc; # procedure definie dans le fichier graphique.tcl
fichier point-serveur.tcl
package require dp
source point-class.tcl;
source graphique.tcl;
puts stdout "numero de porte : "
nonewline;
flush stdout; gets stdin porte;
makePoint p; # creer un point p
p configure -x 70 -y 30 -c red;
# definir position et couleur du point
p draw; # afficher le point
dp_MakeRPCServer $porte ; # creer
serveur pour les points
dp_SetTrigger after p x {p draw};
# si x change, réafficher point
dp_SetTrigger after p y {p draw};
# si y change, réafficher point
dp_SetTrigger after p c {p draw};
# si c change, réafficher point
fichier point-client.tcl
package require dp
source point-class.tcl;
source graphique.tcl;
puts stdout "machine serveur : "
nonewline;
flush stdout; gets stdin hote;
puts stdout "numero de porte : "
nonewline;
flush stdout; gets stdin porte;
# appel du serveur; server est l'identite'
sous laquelle le client
# connait le serveur pour les appels
futurs
set server [dp_MakeRPCClient $hote $porte];
# on recupere dans la variable my_Id
l'identification du canal de
# communication etablie cote' serveur
(valeur de dp_rpcFile)
# my_Id est donc l'identite' sous
laquelle le serveur connait le client
set my_Id [dp_RPC $server set dp_rpcFile];
# le client demande au serveur de
lui distribuer un objet
# de nom p construit par makePoint
dp_RPC $server eval dp_DistributeObject
p $my_Id makePoint;
p draw; # dessiner le point
dp_SetTrigger after p x {p draw};
# si x change, redessiner point
dp_SetTrigger after p y {p draw};
# si y change, redessiner point
dp_SetTrigger after p c {p draw};
# si c change, redessiner point
fichier graphique.tcl
# construction graphique tres simple
(visualiser un point)
package require dp
canvas .c;
pack .c; # cree un espace pour le
dessin graphique
# procedure qui dessine le point
de coordonnees x, y
# comme un petit carre' de couleur
" couleur "
proc dot {x y couleur} {
global .c}
set r 2
#calculer coordonnees point inf. gauche et pt sup droit
set xinfg [expr $x - $r] ; set yinfg [expr $y - $r]
set xsupd [expr $x + $r] ; set ysupd [expr $y + $r]
.c create rectangle $xinfg $yinfg $xsupd $ysupd -fill $couleur