Cette section décrit l'architecture client-serveur de la bibliothèque.
L'architecture client-serveur de Cogitant permet de rendre disponibles les objets manipulés par la bibliothèque (support, graphes, règles) et opérations (entrées / sorties, recherche de projections, applications de règles, vérifications, etc.) à d'autres applications, éventuellement exécutées sur d'autres postes. Les utilisations de cette architecture client/serveur peuvent se diviser en deux catégories :
Dans les deux cas, le serveur peut être utilisé comme serveur de support(s) et de base(s) de graphes (stockage de graphes et exécution d'opérations). Dans cette partie, nous détaillons dans un premier temps la procédure de compilation et d'installation. Dans un deuxième temps nous décrivons plus précisément les classes de l'architecture client serveur. Enfin, dans la dernier point de cette partie, le protocole d'échange est décrit, sous la forme de la DTD du format.
Un serveur Cogitant (appelé serveur TCP par la suite) peut être interrogé par des clients à travers une liaison TCP (mode connecté), ou un échange de messages suivant le protocole HTTP (chaque requête effectue une connexion qui est coupée après réception de la réponse), comme cela est illustré en figure 1. Dans le premier cas, les clients communiquent directement avec le serveur TCP. Dans le second cas, les clients communiquent avec un serveur HTTP (Apache http://www.apache.org par exemple), ce serveur HTTP transmet ses ordres au module d'interfaçage HTTP/TCP de Cogitant, qui, lui même est en communication avec un serveur TCP. Le module d'interfaçage communique avec le serveur TCP par le biais de TCP, et il est donc possible d'installer le serveur TCP sur une autre machine (A) que celle utilisée pour le serveur HTTP (B). De cette façon, il est possible d'installer le serveur TCP sur une machine visible uniquement sur un réseau local (et invisible depuis Internet), et de rendre ce serveur (indirectement) disponible (à C, D, E et F) par le biais d'un serveur HTTP, à condition que la machine serveur HTTP, sur laquelle est exécuté le module d'interfaçage HTTP/TCP, puisse avoir accès par TCP à la machine sur laquelle le serveur TCP est exécuté. L'interfaçage HTTP/TCP se fait par le biais d'un CGI, qui reçoit (par le protocole POST) les données en provenance du client. Si vous n'avez pas compris les phrases précédentes, prenez un aspirine, et relisez-les, sinon, passez à la suite.
La bibliothèque doit être compilée avec des options particulières afin que les fonctionnalités client/serveur soit disponibles. Les échanges entre clients et serveur (échanges de messages XML, voir Protocole d'échange) peuvent se faire de différentes façons, mais pour l'instant, les seules possibilités d'échange sont l'utilisation du protocole TCP ou du protocole HTTP. Les échanges de message au niveau physique (sockets) peuvent être effectués de différentes façon, mais pour l'instant, les échanges TCP et HTTP ont été développées en utilisant la bibliothèque GNU Common C++ (http://www.gnu.org/software/commoncpp). Cette bibliothèque, libre et gratuite, offre un certain nombre de fonctionnalités qui ne sont pas présentes dans la bibliothèque standard C++. Elle n'est toutefois pas fournie en standard avec les compilateurs du marché. Il est donc nécessaire de télécharger les sources (http://www.gnutelephony.org/dist/tarballs/) et de la compiler avant de pouvoir compiler les fonctions client/serveur de Cogitant. A noter que dans la plupart des distributions GNU/Linux, cette bibliothèque est disponible sous forme de package. Si vous êtes dans ce cas, il n'est pas nécessaire de compiler Common C++ à partir des sources, il suffit d'installer le package correspondant ainsi que le package de développement (libcommonc++
et libcommonc++-dev
sous Debian). Common C++ a été testé avec Cogitant sous Linux et sous Windows avec la version 1.7.3.
Nous supposerons que la bibliothèque Common C++ est préalablement installée. Afin de le vérifier, tapez tout simplement ccgnu-config –version
dans un shell, la commande doit être reconnue, et quelque chose comme 1.9.7
doit être affiché. Si vous utilisez la version 2 de Common C++, il vous faut utiliser ccgnu2-config –version
. Pour compiler Cogitant avec les fonctions client/serveur, il est nécessaire de passer l'option –enable-cs
au script configure
. Les fonctions client/serveur pouvant être développées avec une bibliothèque autre que Common C++ (ce n'est pas le cas actuellement), il faut préciser que la compilation devra se faire en utilisant cette bibliothèque, pour cela, il faut rajouter l'option –with-ccxx
. Au final, l'appel au script de configuration doit avoir cette forme :
Après le processus de configuration, lancer la compilation de la façon habituelle.
Notez que l'utilisation de Borland C++ qui était la méthode conseillée dans les versions 5.1.x de Cogitant ne peut plus être utilisée, il est maintenant nécessaire d'utiliser Visual C++ afin de profiter de l'architecture client-serveur de Cogitant.
La première chose à faire est la compilation de Common C++ avec Visual C++. Pour cela, télécharger les sources de la biblithèque, et extraire le contenu de l'archive. Par la suite, on supposera que le contenu de l'archive de Common C++ a été copié dans D:\commoncpp2-1.x.x
. Ouvrir le projet D:\commoncpp2-1.x.x\w32\vs2008\ccgnu2.vcproj
. S'assurer que Visual C++ génèrera des fichiers en mode Release (et non Debug) et lancer la compilation. Une fois que la compilation est finie le répertoire D:\commoncpp2-1.x.x\w32\vs2008\Release
contiendra les deux bibliothèques dynamiques composant Common C++ (CapeCommon17.dll
et CapeExtras17.dll
, mais cela peut varier selon la version de Common C++).
La configuration de Cogitant pour utiliser Common C++ se fait ensuite de la façon habituelle avec CMake : Lancer l'outil CMake sur le répertoire de Cogitant et, après avoir choisi la génération de fichiers pour Visual C++ 2008, cocher la case WithClientServer_CommonCPP. Cliquer sur Configure. Une erreur est détectée, ce qui est tout à fait normal : il faut choisir dans CcppBaseDir le répertoire qui contient Common C++, c'est à dire, ici, D:\commoncpp2-1.x.x
. CcppLibraryDir (répertoire contenant les dll de Common C++) devrait alors être fixé automatiquement après un Configure ainsi que CcppLibraries (noms des dll, sans l'extension). Si ce n'est pas le cas, modifier les valeurs de ces variables. Cliquer sur Generate et ouvrir le projet dans Visual C++ afin de lancer la compilation.
Afin de vérifier que la bibliothèque a été correctement compilée avec le support des fonctionnalités client/serveur, le plus simple est d'exécuter l'exemple de serveur et de client. Le serveur est samples/doc/server_1[.exe]
et le client dans test/client
. Ces deux programmes sont compilés par défaut (lors du make
) sous Unix, et doivent être compilés (de la façon habituelle) sous Windows. Exécuter le serveur, puis lancer le client. Ce client teste le serveur, en lui envoyant un grand nombre de requêtes par TCP. Sous Windows, assurez vous que les bibliothèques dynamiques (dll) indispensables à l'exécution des programmes sont bien présentes dans les chemins de recherche : cogitant.dll
(si Cogitant a été compilée en dll), les dll de Common C++, et les bibliothèques runtime du compilateur. Vous pouvez pour cela copier ces dll dans les répertoires contenant les exécutables, ou modifier la valeur du PATH
afin qu'il contienne les répertoires contenant les dll en question.
Il est possible de se connecter au serveur en utilisant l'application telnet. Pour cela, exécuter la commande telnet 127.0.0.1 4246
(4246 est le port utilisé par le serveur par défaut). Vous pouvez alors saisir des requêtes en respectant le Protocole d'échange. La réponse du serveur est affichée en retour.
Aucune installation particulière n'est nécessaire. Il suffit de compiler le serveur. Le code du serveur (server_1.cpp
) est très simple, il ne fait qu'instancier des classes de la bibliothèque. Le seul point important réside en le choix des opérations qui sont offertes aux clients. Il est ainsi possible de construire un serveur offrant exactement les opérations que vous voulez offrir (et en interdire d'autres, par exemple, vous interdisez aux clients la possibilité de charger un nouveau support, ou charger des graphes, etc). En vous inspirant du source du serveur par défaut, vous pouvez aussi construire un serveur offrant de nouvelles opérations, il suffit pour cela de définir de nouvelles sous-classes de cogitantcs::OpeServer et de rendre ces opérations disponibles pour le serveur.
L'utilisation du protocole HTTP est préférable quand le serveur Cogitant doit être disponible à travers Internet. En effet, dans le cas de l'utilisation de HTTP, il suffit d'installer le module d'interface HTTP/TCP afin qu'il soit connu par le serveur HTTP (Apache, etc.), et de cette façon, le serveur Cogitant est accessible à travers le serveur HTTP, comme n'importe quelle page Web. Ainsi, les messages peuvent passer à travers un firewall (car le port 80 utilisé par le service HTTP n'est habituellement pas filtré), et il est inutile d'"ouvrir" un nouveau port pour laisser passer les messages comme le demanderait le serveur TCP.
Pour rendre le serveur Cogitant accessible par HTTP, il est donc nécessaire d'intaller le CGI d'interfaçage TCP/HTTP sur la machine exécutant le serveur HTTP. Les CGI sont souvent rédigés dans des langages de script (il serait possible d'utiliser un tel script ici), mais le module d'interfaçage est écrit en C++, même si son code est très simple: il ne fait que "rediriger" la requête (reçue par POST) au serveur TCP (par défaut, il redirige la requête sur le serveur TCP tournant sur la même machine, mais il suffit de modifier le source (cgtserver_cgi.cpp
) pour rediriger la requête vers un serveur TCP d'une autre machine). Le source de ce CGI réside dans le répertoire samples/cgiserver
.
Dans ce répertoire, il y a aussi un fichier query.html
qui peut être utilisé pour transmettre une requête à un serveur Cogitant à partir d'un navigateur. Pour utiliser cette page html, le cgi doit être exécutable par le serveur HTTP dans le même répertoire (voir ci-dessous).
cogitant/samples/cgiserver
soit accessible par le serveur Apache, et vous pouvez alors visionner la page. /var/www
(ou /var/www/html
) est le répertoire qui doit contenir les fichiers et répertoires servis par Apache, vous pouvez rendre le répertoire cogitant/samples/cgiserver
visible à partir du serveur comme étant /cogitant
, en tapant l'ordre ln -s /monrepertoire/sources/cogitant-5.x.x/samples/cgiserver /var/www/cogitant
(ou /var/www/html/cogitant
). Vérifiez dans la configuration d'Apache (/etc/apache/httpd.conf
ou /etc/httpd/conf/httpd.conf
) que le serveur Apache est autorisé à suivre les liens symboliques (section Directory "var/www/[html/]"
option FollowSymLinks
) et que les cgi sont exécutables dans les répertoires contenant les pages (option ExecCGI
). La méthode ci-dessus ne fonctionne que sous Unix. La méthode décrite ici fonctionne sous Unix et sous Windows. Cette méthode demande de créer un nouvel alias. Éditer le fichier de configuration d'Apache httpd.conf
, situé dans /etc/apache
ou /etc/httpd/conf
sous Unix et dans Apache/conf
sous Windows, et rajouter dans la section IfModule mod_alias.c
un nouvel alias (changez le chemin donné par le chemin dans lequel réside cgtserver.cgi
chez vous). Cette méthode a l'avantage de ne pas demander de rajouter l'option ExecCGI
aux répertoires contenant les fichiers HTML (mais uniquement au répertoire contenant le CGI du module d'interfaçage). Sous Unix :
Sous Windows : Under Windows:
Si ce n'est pas déjà fait, configurez Apache pour qu'il reconnaisse l'extension cgi
pour les CGI : Dans la section IfModule mod_mime.c
, rajoutez (ou dé-commentez) la ligne
Si vous ne désirez pas autoriser les fichiers .cgi
a être exécutés par le serveur, il vous suffit de renommer cgtserver.cgi
en un nom étant reconnu comme cgi par le serveur (exe
par exemple). Dans ce cas, il faut évidemment modifier la page html. Enfin, pour que le cgi puisse être exécuté correctement, il est nécessaire, sous Windows, de copier dans le répertoire cgiserver
les dll indispensables à l'exécution : cogitant.dll
, ccgnu2.dll
, cc3250mt.dll
. Si une de ces dll est absente, l'accès au cgi provoque l'affichage de la page Internal server error (sans gravité, mais la requête ne peut être transmise au serveur TCP, car le CGI ne peut être exécuté).
Lancez le serveur TCP (dans samples/doc
), en tapant server_1
. Il est alors possible de charger la page de test dans un navigateur (http://127.0.0.1/cogitant/query.html
). Si vous ne voyez pas la page, vous avez mal configuré l'alias (avez-vous pensé à redémarrer apache ?) ou le lien.
L'exécution d'une requête doit provoquer une réponse (ou une erreur si la requête ne respecte pas la dtd cogitantcs.dtd
, mais pas une erreur de type unknownserver
, qui signale que le module d'interfaçage HTTP/TCP n'a pas trouvé le serveur TCP). Notez que le serveur est prévu pour communiquer avec des clients qui respectent la DTD, et que donc, si des requêtes incorrectes sont transmises au serveur, ceci peut provoquer des comportements étranges voire un blocage du serveur. Notez que la plupart des navigateurs (Mozilla, IE, etc.) n'affichent pas les documents XML dans la zone habituelle, et qu'il faut choisir l'affichage du source pour voir la réponse du serveur. Vous pouvez aussi utiliser l'application client de test, située dans test/client
et appelée client
, qui communique avec le serveur par TCP ou HTTP. Vous pouvez utiliser les paramètres passés à ce programme pour indiquer un nom de machine, un numéro de port, l'utilisation du protocole HTTP et l'URL (syntaxe : client [http] [http://<host>[:<port>]/<dir>/cgtserver.cgi] [tcp [<host_tcp>[<port_tcp>]]]
).
Les classes permettant l'échange entre serveur et client sont toutes dans l'espace de nom cogitantcs. Elles permettent d'utiliser des supports, graphes et règles distants (sur le serveur) comme s'ils étaient locaux (sur le client). Dès qu'un objet distant est nécessaire sur le client, une requête est envoyée par le client, et le serveur lui retourne les caractéristiques de cet objet. Se reporter aux exemples (samples/doc/server_*.cpp
) et à la documentation des classes de l'espace de nom cogitantcs pour plus d'informations.
La gestion du serveur est fournie par la classe cogitantcs::Server. Cette classe gère la réception des messages des clients, l'exécution des requêtes et l'envoi des réponses. À chaque serveur est associé un ensemble de cogitant::Environment, et c'est le contenu de ces environnements (support, graphes et règles ainsi que les opérations correspondantes) qui est offert aux clients. Afin de pouvoir adapter un serveur à différents usages, il est possible de personnaliser certaines aspects du serveur, comme les opérations disponibles et offertes au serveur et le mode de réception et d'émission.
De façon générale, l'exécution du serveur est lancée par l'instanciation de cogitantcs::Server (ou une sous-classe, voir plus loin), l'appel à la méthode cogitantcs::Server::addStdOperations() ou cogitantcs::Server::addMinOperations(), et enfin l'appel à cogitantcs::Server::mainLoop().
La méthode addStdOperations() permet de lancer un serveur qui offre toutes les opérations standards fournies avec la bibliothèque, alors que la méthode addMinOperations() crée un serveur qui n'offre que les opérations minimales, c'est-à-dire celles d'accès (en lecture seule) aux supports et graphes du serveur. Dans ce dernier cas, les clients ne peuvent modifier les données offertes, il est donc nécessaire, avant d'appeler mainLoop(), de construire un (des) environnements. Pour cela, il suffit d'initialiser un environnement de la façon habituelle, puis d'ajouter cet environnement au serveur en appelant cogitantcs::Server::addEnvironment() (voir l'exemple ci-dessous).
Très souvent, il est nécessaire d'ajouter de nouvelles opérations aux serveurs, par exemple, des opérations particulières d'interrogation d'une base de graphes, ou des opérations particulières d'entrées sorties. De telles opérations ne font pas partie du protocole "standard" de Cogitant mais peuvent être utilisées par un client "particulier" pour s'adresser à un serveur "particulier". Comme toutes les opérations de Cogitant, de telles opérations doivent être des sous-classes de cogitant::OperationBase. En fait, les opérations du serveur doivent être des sous-classes de cogitantcs::OpeServer. En utilisant ce mécanisme, il n'est pas nécessaire d'écrire une sous-classe de Server pour rajouter de nouvelles opérations, il suffit d'écrire une sous-classe de OpeServer correspondant à l'opération désirée, d'instancier cette classe, et de rajouter cette instance au serveur à l'aide de la méthode cogitantcs::Server::addOperation(). De cette façon, l'ajout de nouvelles opérations, ou la modification d'opérations existantes est simplifié, et peut même être fait dynamiquement, au cours de l'exécution du serveur. Pour définir une nouvelle opération il est donc nécessaire de définir une sous-classe de cogitantcs::OpeServer. Plus précisément, il est nécessaire de redéfinir deux méthodes dans la sous-classe à créer :
cogitantquery
de niveau 0, qui contient des balises d'appels à des opérations, chacune de ces balises ayant pour nom le nom de l'opération devant être exécutée (préfixé par q
comme query, voir Protocole d'échange). Ainsi, pour une requête qloadgraphs
, le serveur exécutera automatiquement l'opération pour laquelle la méthode name() retourne loadgraphs
. S'il est possible de personnaliser le fonctionnement d'un serveur en rajoutant de nouvelles opérations, il est aussi possible de le personnaliser en utilisant des fonctions particulières de transport des messages XML. En effet, selon l'application, la communication entre clients et serveur peut se faire de différentes façons. Cogitant est fourni avec un serveur pouvant être interrogé par le biais d'une liaison TCP (avec Common C++) et un client pouvant se connecter au serveur par le biais d'une liaison TCP ou HTTP (toujours avec Common C++). Il est possible de rajouter de nouvelles méthodes de transport, par exemple pour utiliser des bibliothèques autres que Common C++ ou en "attaquant" directement la couche sockets du système d'exploitation, ou pour utiliser d'autres méthodes de communications qui permettent de transporter des documents XML (par exemple des tubes, ou des tubes nommés). L'ajout de nouvelles méthodes de transport est très simple, car les opérations de transport ne font pas partie du serveur ou du client, mais elles sont déportées dans la classe abstraite cogitantcs::OperationCSIO (client server input output). À tout serveur et à tout client est associée une instance d'une sous-classe de cogitantcs::OperationCSIO. Et à chaque fois que le client ou le serveur doit envoyer des données, recevoir des données, ou se mettre en attente de connexions, il appelle des méthodes sur cette instance de OperationCSIO). Pour définir de nouvelles méthodes de transport d'information, il suffit donc de définir une sous-classe de cogitantcs::OperationCSIO et plus précisément de redéfinir les méthodes suivantes :
À titre d'exemple, cogitantcs::OperationCSIO_Simple fournit les opérations d'entrées sorties à partir de flux C++. Le constructeur de cette classe prend deux paramètres : un ostream et un istream. En utilisant cette opération d'entrées sorties, il est par exemple possible d'utiliser un serveur sur cout
et cin
, de lui passer des requêtes sur l'entrée standard, et de récupérer les résultats sur la sortie standard. Cette opération n'est toutefois pas utilisable pour la communication réelle avec un client. Pour de véritables connexions, on utilisera la classe cogitantcs::OperationCSIO_TCP qui se base sur Common C++ pour fournir des entrées sorties sur TCP. Cette classe est munie de deux constructeurs cogitantcs::OperationCSIO_TCP::OperationCSIO_TCP(), le premier ne prend qu'un paramètre entier, et doit être utilisé pour instancier l'opération devant être utilisée dans un serveur. L'entier est alors le numéro de port d'attente du serveur. Le second constructeur prend deux paramètres : une chaîne de caractères et un entier. Ce deuxième constructeur doit être utilisé dans un client. Le premier paramètre passé au constructeur sera alors le nom du serveur auquel devra se connecter le client, et le second paramètre le numéro de port. Pour utiliser une classe particulière de transport, il suffit d'instancier la classe choisie, et de passer un pointeur sur cette instance au constructeur de cogitantcs::Server ou cogitantcs::Client. Il est possible de faire la même chose plus simplement, en utilisant la classe cogitantcs::Server_TCP dont le constructeur ne prend qu'un paramètre, le numéro de port d'attente (voir l'exemple ci-dessus). Notez toutefois que l'utilisation de Server_TCP n'est qu'un raccourci et est exactement équivalente à l'instanciation de cogitantcs::OperationCSIO_TCP et le passage de l'instance créée au constructeur cogitantcs::Server::Server().
Le client (cogitantcs::Client) peut être utilisé de différentes façons, plus ou moins simples et permettant d'accéder à plus ou moins de fonctions.
Évidemment, ces trois modes d'utilisation correspondent à une seule et même méthode de communication avec le serveur, l'échange de messages XML en utilisant la couche de transport.
Comme dans le cas d'un serveur, le transport de messages XML est effectué par une sous-classe de cogitantcs::OperationCSIO. Le constructeur de Client prend donc comme paramètre un pointeur sur une instance d'une sous-classe de OperationCSIO. Toutefois, afin d'utiliser plus simplement un client TCP, la classe cogitantcs::Client_TCP est définie, et son constructeur prend deux paramètres : le nom de l'hôte sur lequel est exécuté le serveur, et le numéro de port d'attente du serveur. De même, la classe cogitantcs::Client_HTTP permet de se connecter simplement à un serveur par HTTP. Le constructeur de cette classe ne prend qu'un seul paramètre, une chaîne de caractères qui doit contenir l'URL du CGI d'interfaçage.
Pour utiliser un client, il ne faut pas instancier la classe cogitant::Environment, car le client requiert l'utilisation de structures de données spécifiques. Ces classes particulières (cogitantcs::SetClient et ses sous-classes, cogitantcs::EnvironmentAllocatorClient) fournissent des ensembles (de types, de graphes) qui interrogent le serveur dès qu'un élément requis n'est pas connu du client. Ainsi, quand un client se connecte à un serveur, il ne transfère pas la totalité du support et des graphes, mais les objets sont transférés dès que nécessaire. Il ne faut donc pas instancier Environment, mais utiliser la méthode cogitantcs::Client::environment() qui retourne (un pointeur sur) l'environnement géré par le client. Mais cet environnement ne peut être utilisé dès la connexion au serveur. En effet, un serveur peut gérer plusieurs environnements, il est donc nécessaire de sélectionner lequel de ses environnements est choisi par le client. Ceci se fait par la méthode cogitantcs::Client::setServerEnvironment(). Cette méthode est surchargée : elle peut prendre comme paramètre un entier (numéro de l'environnement côté serveur, c'est-à-dire le résultat de cogitantcs::Server::addEnvironment()) ou une chaîne de caractères (nom de l'environnement, c'est-à-dire la chaîne passée en second paramètre à cogitantcs::Server::addEnvironment()). Ainsi l'exemple ci-dessous se connecte à un serveur et sélectionne l'environnement de nom env_bucolic
.
Comme on le voit sur cet exemple, une fois que le client est initialisé, il est possible d'accéder aux objets de l'environnement de la façon habituelle. Ainsi, l'exécution de ce programme (dont le résultat est donné ci-dessous) affiche le support et le premier graphe (au format linéaire), de la même façon que si ce support et ce graphe étaient chargés "localement".
cogitant::Support ConceptTypes : 0:Universal, 1:Action, 2:Attribute, 3:Boat, 4:Bucolic, 5:Couple, 6:Entity, 7:Fish, 8:Lake, 9:Living being, 10:Painting, 11:Person, 12:Place, 13: Scene, 14:Sleep, 15:Think PartialOrder : (0 (1, 2, 6)) (1 (15, 14, 7)) (2 (4)) (3 ()) (4 ()) (5 ()) (6 (1 3, 5, 3, 12, 10, 9)) (7 ()) (8 ()) (9 (11, 7)) (10 ()) (11 ()) (12 (8)) (13 ()) (14 ()) (15 ()) RelationTypes : 0:agent, 1:attr, 2:in, 3:object, 4:on PartialOrder : (0 ()) (1 ()) (2 ()) (3 ()) (4 ()) NestingTypes : 0:Description, 1:Component, 2:Representation PartialOrder : (0 (1, 2)) (1 ()) (2 ()) Individuals : 0:Peter(11), 1:A(10) sleepandfish: [Person:Peter]<-(agent)<-[Sleep] [Person]<-(agent)<-[Fish]->(in)->[Lake].
Dès qu'un accès sera effectué sur un objet de l'environnement qui n'est pas présent sur le client, une requête sera automatiquement générée en direction du serveur, qui retournera au client la description de cet objet. L'environnement client est donc une image de l'environnement serveur, cette image étant éventuellement incomplète car certains objets n'ont pas (encore) été transférés. Ceci a toutefois une conséquence : il est interdit de rajouter ou supprimer des objets dans l'environnement client, car dans ce cas, l'environnement client ne serait plus une image de l'environnement serveur. De plus, dans le cas où plusieurs clients sont connectés à un même serveur, il serait très difficile et coûteux de maintenir une cohérence entre tous les environnements. C'est pourquoi les seuls transferts automatiques se font dans le sens serveur vers client, et c'est l'environnement serveur qui fait foi.
Cependant, dans certains cas, il peut être nécessaire, à partir d'un client, de modifier les objets du serveur (modification d'un graphe existant, ajout ou suppression d'un graphe, chargement d'un support, etc.). Pour de tels usages, la classe cogitantcs::Client contient des méthodes telles cogitantcs::Client::newEnvironmentObject() qui permettent de modifier l'environnement serveur (et donc, indirectement, l'environnement client). Il est alors possible d'accéder à des fonctions qui ne sont pas disponibles à partir de l'environnement. Par exemple, si côté client un graphe est modifié, ces modifications ne sont pas répercutées sur le serveur car effectuées sur la copie locale. En utilisant la méthode cogitantcs::Client::commitEnvironmentObject(), il est possible de transférer au serveur les modifications. Le graphe correspondant côté serveur est alors modifié, à condition que le serveur accepte les modifications venant des clients. Une telle modification de l'environnement serveur ne se fait pas de façon automatique : il est nécessaire d'appeler explicitement cette méthode. Ainsi, les problèmes de concurrence (mise à jour d'un même objet par deux clients connectés à un même serveur par exemple) sont à la charge du programmeur. Se reporter à la documentation de cogitantcs::Client pour connaître toutes les méthodes permettant d'interagir avec le serveur.
Dans certains cas, il est préférable de ne disposer d'aucun mécanisme de mise à jour automatique, et d'effectuer explicitement toutes les mises à jour, que ce soit dans un sens ou dans l'autre. Entre autres inconvénients de la mise à jour automatique, les graphes portent le même identifiant (iSet) dans le serveur et dans le client, ce qui empêche d'en créer de nouveaux ou d'en détruire certains dans le client (sauf en passant par des méthodes d'accès au serveur, ce qui peut dans certains cas devenir lourd). Dans le cas où le client n'a besoin que de quelques graphes, il peut être plus simple de désactiver la mise à jour automatique, de récupérer certains graphes du serveur (qui ont dans le client un identifiant qui est éventuellement différent de leur identifiant dans le serveur), d'en créer localement ou d'en charger à partir d'un fichier local, puis envoyer certains de ces graphes au serveur afin de lui faire effectuer certains traitements. Il est possible de désactiver les mises à jour automatiques séparément pour le support et pour les objets de l'environnement. Il faut pour cela utiliser les deux derniers paramètres du constructeur de cogitantcs::Client (et ses sous-classes, comme cogitantcs::Client_TCP::Client_TCP()), comme cela est décrit dans les deux exemples ci-dessous.
Exemple. L'exemple ci-dessous crée un client dans lequel support et graphs sont différents de ceux manipulés par le serveur. De cette façon, le client peut modifier "son" support (en chargeant un fichier à l'aide de cogitant::Environment::readSupport()) et envoyer le support obtenu au serveur (cogitantcs::Client::commitSupport()). Côté serveur, l'environnement qui a été créé pour l'occasion (par cogitantcs::Client::newEnvironment()) reçoit le serveur envoyé par le client. Pour le vérifier, un deuxième client est créé et sélectionne l'environnement créé précédemment sur le serveur. En affichant alors le support associé à ce client, on obtient le support qui a été envoyé par le premier client.
Exemple. Ce deuxième exemple illustre l'utilisation d'un support obtenu à partir du serveur, et de graphes (et règles) locaux au client. Ici, le client charge (en local) un graphe et une règle, et envoie ces deux objets au serveur (cogitantcs::Client::commitEnvironmentObject()), après avoir obtenu du serveur deux identifiants pour stocker ces deux objets (cogitantcs::Client::newEnvironmentObject()). Le graphe chargé à partir du client et envoyé au serveur est ensuite sauvegardé côté serveur (cogitantcs::Client::saveEnvironmentObject()). Le deuxième client permet de vérifier le résultat des opérations du premier : le graphe et la règle sont disponibles sur le serveur.
Enfin, le troisième mode d'échange avec le serveur consiste à générer explicitement une requête sous la forme d'un document XML. En effet, les méthodes de cogitantcs::Client ne concernent que les messages "standards" de Cogitant. Si de nouvelles opérations sont définies dans le serveur, il est nécessaire d'envoyer des messages particuliers. Pour cela, on peut utiliser cogitantcs::Client::addPendingQuery() qui rajoute au document XML requête "en attente" une balise d'exécution d'opération et cogitantcs::Client::answer() qui retourne le document XML réponse reçu du serveur.
Il est possible aussi d'envoyer des messages XML à partir d'une application qui n'est pas basée sur Cogitant, pour utiliser par exemple les fonctions offertes par un serveur à partir d'une application Java. L'exemple ci-dessous, qui doit être utilisé conjointement avec le serveur server_1
, permet de créer un environnement, charger un graphe et l'envoyer vers le client à partir d'un client java qui se contente d'envoyer et recevoir des messages XML.
L'exemple ci-dessus est très simplifié, car les réponses reçues du serveur ne sont pas analysées, et sont affichées telles quelles à l'écran. Il existe toutefois un grand nombre de bibliothèques Java qui permettent d'analyser du XML, et en les utilisant, on peut alors facilement interpréter les réponses du serveur, pour déterminer si le graphe a été correctement chargé, quels sont les sommets qui composent le graphe et construire une représentation "locale au client" du graphe, etc.
Les échanges entre serveur et client se font à l'aide de messages XML. La DTD suivante documente les messages pouvant être envoyés par le client et par le serveur. Le client ne peut envoyer que des documents ayant pour racine une balise cogitantquery
, et le serveur des documents ayant pour racine cogitantanswer
.
<?xml version="1.0" encoding="UTF-8"?> <!-- DTD CogitantCS 1.3 This DTD describes the exchange protocol between a Cogitant server and its clients. To apply this DTD, use the following syntax: <!DOCTYPE cogitantquery PUBLIC "-//COGITANT//Cogitant Client-server Specification 1.3//EN" "http://cogitant.sourceforge.net/cogitantcs.dtd"> or <!DOCTYPE cogitantanswer PUBLIC "-//COGITANT//Cogitant Client-server Specification 1.3//EN" "http://cogitant.sourceforge.net/cogitantcs.dtd"> This file is part of Cogitant which is a library facilitating construction of applications using conceptual graphs. It is under GPL licence. http://cogitant.sourceforge.net Cogitant version 5.2.0 - Last change of the DTD : 21/05/2009 --> <!-- Extensions des attributs des balises standard. --> <!ENTITY % cogitantQueryExtensions ""> <!ENTITY % cogitantAnswerExtensions ""> <!-- Document. --> <!ELEMENT cogitantquery (qserver | qenvironment | qsetcontent | qconcepttype | qrelationtype | qnestingtype | qindividual | qenvironmentobject | qgraph | qrule | qcommitenvironmentobject | qcommitimmediateless | qcomparison | qimmediateless | qimmediategreater | qbannedobjects | qgraphobject qnewenvironment | qnewenvironmentobject | qloadsupport | qsavesupport | qloadgraphs | qsaveenvironmentobjects | qdeleteenvironment | qdeleteenvironmentobject | qnewsupportobject | qcommitsupportobject | qprojections | qruleapplications | qruleapply | qrulesclosure | qconstraintsatisfaction | qcopyenvironmentobject | qoperationconfig | qaddconcept | qaddrelation | qaddnesting | qdelgraphobject | qaddedge | qdeledge | %cogitantQueryExtensions; )*> <!-- reguiresheader : si égal à 0, le serveur n'envoie pas une entête destinée au serveur http (Content-type: ... Cet attribut doit donc être utilisé (avec la valeur 0) quand le serveur est utilisé en accès direct (TCP) pour ne pas recevoir ce header. --> <!ATTLIST cogitantquery requiresheader CDATA '1' > <!ELEMENT cogitantanswer (server | environment | setcontent | concepttype | relationtype | nestingtype | individual | environmentobject | graph | rule | deletedobject | commitenvironmentobject | commitimmediateless | comparison | immediateless | immediategreater | bannedobjects | graphobject newenvironment | newenvironmentobject | loadsupport | loadgraphs | saveenvironmentobjects | deleteenvironment | deleteenvironmentobject | newsupportobject | commitsupportobject | projections | ruleapplications | ruleapply | rulesclosure | constraintsatisfaction | copyenvironmentobject | operationconfig | addconcept | addrelation | addnesting | delgraphobject | addedge | deledge | error %cogitantAnswerExtensions;)*> <!ATTLIST cogitantanswer > <!ELEMENT error EMPTY> <!ATTLIST error <!-- syntax : syntaxe xml non respectée dans le message requête. environmentid : identificateur d'environnement inconnu (attribut env). setid : identificateur d'ensemble inconnu (attribut set). objectid : identificateur d'objet inconnu (par ex, dans un graphe, une arête a pour extrémité un objet qui n'existe pas). objecttype : l'identificateur d'objet se réfère à un objet du mauvais type (par ex, vérification d'une contrainte avec l'id d'un objet de l' environnement qui n'est pas une contrainte. unknownserver : retourné par cgiserver quand il n'arrive pas à établir une connexion avec le serveur TCP. arg : contient un descriptif plus détailé de l'erreur (parfois). --> type (syntax | environmentid | setid | objectid | objecttype | unknownserver) #REQUIRED arg CDATA #IMPLIED > <!-- Propriétés d'un objet --> <!ELEMENT properties (property*)> <!-- subid : Identificateur de sous-ensemble de propriétés. Si absent, l'élément prop désigne l'ensemble principal. --> <!ATTLIST properties subid CDATA #IMPLIED > <!ELEMENT property EMPTY> <!ATTLIST property type CDATA #IMPLIED value CDATA #IMPLIED > <!-- A partir de la DTD 1.3, certains attributs d'objets ne sont plus transférés comme des propriétés mais des attributs. Les attributs correspondent à des informations faisant partie du modèle (conformité d'un type de relation, par exemple), alors que les propriétés représentent des informations externes au modèle (position d'un sommet, par exemple). --> <!ELEMENT attribute EMPTY> <!ATTLIST attribute type CDATA #IMPLIED value CDATA #IMPLIED > <!-- Identificateur d'un objet de l'environnement. --> <!ELEMENT eoid EMPTY> <!ATTLIST eoid id CDATA #REQUIRED > <!-- Serveur. --> <!ELEMENT qserver EMPTY> <!ATTLIST qserver> <!ELEMENT server (properties*)> <!-- environments : Nombre d'environnements disponibles sur le serveur --> <!ATTLIST server environments CDATA #REQUIRED > <!-- Environnement. --> <!ELEMENT qenvironment EMPTY> <!-- env (numéro de l'environnement) ou name doivent être donnés. --> <!ATTLIST qenvironment env CDATA #IMPLIED name CDATA #IMPLIED > <!ELEMENT environment (properties*, support)> <!-- objects : Taille de l'ensemble d'EnvironmentObjects --> <!ATTLIST environment env CDATA #REQUIRED objects CDATA #REQUIRED name CDATA #IMPLIED > <!ELEMENT support (properties*)> <!-- xxxtypes : Tailles des ensembles du support --> <!ATTLIST support concepttypes CDATA #REQUIRED relationtypes CDATA #REQUIRED nestingtypes CDATA #REQUIRED individuals CDATA #REQUIRED bannedtypes CDATA #REQUIRED > <!-- Contenu d'un ensemble (de TC, de TR, de TN, de MI, de EnvironmentObject). --> <!ELEMENT qsetcontent EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN) +4 (MI) +5 (EnvironmentObjects). id : Identificateur dans l'ensemble de l'élément demandé ou "all" : tout le contenu de l'ensemble. --> <!ATTLIST qsetcontent set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Type de concept. --> <!ELEMENT qconcepttype EMPTY> <!-- env : Numéro de l'environnement. (idem relationtype, nestingtype, individual, environmentobject) id : Numéro dans l'ensemble de l'objet demandé ou "all" (tout le contenu de l'ensemble). (idem relationtype, nestingtype, individual, environmentobject) --> <!ATTLIST qconcepttype env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT concepttype (attribute*, properties*)> <!ATTLIST concepttype set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Type de relation. --> <!ELEMENT qrelationtype EMPTY> <!ATTLIST qrelationtype env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT relationtype (attribute*, properties*)> <!ATTLIST relationtype set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Type d'emboîtement. --> <!ELEMENT qnestingtype EMPTY> <!ATTLIST qnestingtype env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT nestingtype (attribute*, properties*)> <!ATTLIST nestingtype set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Marqueur individuel. --> <!ELEMENT qindividual EMPTY> <!ATTLIST qindividual env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT individual (attribute*, properties*)> <!ATTLIST individual set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Élément de l'environnement. --> <!ELEMENT qenvironmentobject EMPTY> <!-- env : Numéro de l'environnement côté serveur. id : Numéro de l'objet dans l'environnement ou "all". iddest : Numéro d'identifiant chez le client. N'est précisé que dans le cas où il est différent d'id. La valeur de cet attribut est simplement recopiée telle quelle par le serveur dans la réponse, et peut être utilisée par le client. Cet attribut ne doit pas être utilisé dans le cas ou id vaut "all". --> <!ATTLIST qenvironmentobject env CDATA #REQUIRED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT deletedobject EMPTY> <!ATTLIST deletedobject set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Graphe. --> <!ELEMENT qgraph EMPTY> <!-- cf. environmentobject --> <!ATTLIST qgraph env CDATA #REQUIRED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT graph (properties*, (internalgraph | concept | relation | nesting | coreferenceclass)*)> <!-- size : Taille de l'ensemble de noeuds. set, id : sont obligatoires quand le graphe est transféré en tant qu'objet de l'environment, sont interdits quand le graphe est transféré à l'intérieur d'une règle. --> <!ATTLIST graph set CDATA #IMPLIED id CDATA #IMPLIED iddest CDATA #IMPLIED size CDATA #REQUIRED > <!ELEMENT internalgraph (properties*, edge*)> <!ATTLIST internalgraph id CDATA #REQUIRED > <!ELEMENT concept (properties*, edge*)> <!ATTLIST concept id CDATA #REQUIRED > <!ELEMENT relation (properties*, edge*)> <!ATTLIST relation id CDATA #REQUIRED > <!ELEMENT nesting (properties*, edge*)> <!ATTLIST nesting id CDATA #REQUIRED > <!ELEMENT coreferenceclass (properties*, edge*)> <!ATTLIST coreferenceclass id CDATA #REQUIRED > <!-- Attention, l'ordre des arêtes liées à un noeud d'un graphe est important, et doit être transféré "tel quel". --> <!ELEMENT edge EMPTY> <!-- env : Extrémité de l'arête (identificateur de sommet, ou * pour ISET_NULL) label : Étiquette de l'arête (entier (étiquette de l'arête) ou 'P' (lien parent) 'C' (lien enfant) ou '=' (lien de coreference). --> <!ATTLIST edge end CDATA #REQUIRED label CDATA #REQUIRED > <!-- Règle. --> <!ELEMENT qrule EMPTY> <!-- cf. environmentobject --> <!ATTLIST qrule env CDATA #REQUIRED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT rule (properties*, hypothesis, conclusion, conpt*)> <!ATTLIST rule set CDATA #IMPLIED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT hypothesis (graph)> <!ATTLIST hypothesis> <!ELEMENT conclusion (graph)> <!ATTLIST conclusion> <!ELEMENT conpt EMPTY> <!ATTLIST conpt idc1 CDATA #REQUIRED idc2 CDATA #REQUIRED > <!-- Transfert d'un objet de l'environnement du client vers le serveur. --> <!ELEMENT qcommitenvironmentobject (graph | rule)> <!ATTLIST qcommitenvironmentobject env CDATA #REQUIRED > <!ELEMENT commitenvironmentobject EMPTY> <!ATTLIST commitenvironmentobject error CDATA #IMPLIED> <!-- Accès aux plus petits immédiats d'un type. --> <!ELEMENT qcommitimmediateless (qilid*)> <!-- tp= "c": type de concept, "r": type de relation, "n": type d'emboîtement --> <!ATTLIST qcommitimmediateless env CDATA #REQUIRED tp CDATA #REQUIRED > <!ELEMENT qilid EMPTY> <!-- id1 > id2 setunset= "s" fixer id1 > id2, "u" supprimer id1 > id2 --> <!ATTLIST qilid id1 CDATA #REQUIRED id2 CDATA #REQUIRED setunset CDATA 's' > <!ELEMENT qcommitimmediateless> <!ATTLIST qcommitimmediateless error CDATA #IMPLIED> <!-- Comparaison de deux types. --> <!ELEMENT qcomparison EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN). --> <!ATTLIST qcomparison set CDATA #REQUIRED id1 CDATA #REQUIRED id2 CDATA #REQUIRED > <!ELEMENT comparison EMPTY> <!ATTLIST comparison set CDATA #REQUIRED id1 CDATA #REQUIRED id2 CDATA #REQUIRED result CDATA #REQUIRED > <!-- Accès aux plus petits immédiats d'un type. --> <!ELEMENT qimmediateless EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN). id : identificateur du type ou "all". --> <!ATTLIST qimmediateless set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT immediateless (ilid*)> <!ATTLIST immediateless set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT ilid EMPTY> <!ATTLIST ilid id CDATA #REQUIRED > <!-- Accès aux plus grands immédiats d'un type. --> <!ELEMENT qimmediategreater EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN). id : identificateur du type ou "all". --> <!ATTLIST qimmediategrater set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT immediategreater (igid*)> <!ATTLIST immediategreater set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT igid EMPTY> <!ATTLIST igid id CDATA #REQUIRED > <!-- Accès aux types interdits. --> <!ELEMENT qbannedtypes EMPTY> <!-- env : identificateur de l'environnement à interroger. --> <!ATTLIST qbannedtypes env CDATA #REQUIRED> <!ELEMENT bannedtypes (bannedtype*)> <!ATTLIST bannedtypes env CDATA #REQUIRED> <!-- L'élément bannedtypes contient autant d'éléments bannedtype qu'il y a de types interdits. --> <!ELEMENT bannedtype (btid*)> <!-- Chaque élément bannedtype contient des éléments btid pour chaque identificateur de type primitif composant le type conjonctif interdit. --> <!ELEMENT btid EMPTY> <!ATTLIST btid id CDATA #REQUIRED > <!-- Accès à un objet (concept, relation, graphe interne, emboîtement, classe de coref) composant un graphe. --> <!ELEMENT qgraphobject EMPTY> <!-- env : Numéro de l'environnement côté serveur. idgraph : Identificateur du graphe. idobject : Identificateur de l'objet l'intérieur du graphe. --> <!ATTLIST qgraphobject env CDATA #REQUIRED idgraph CDATA #REQUIRED idobject CDATA #IMPLIED > <!ELEMENT graphobject (internalgraph | concept | relation | nesting | coreferenceclass)> <!ATTLIST graphobject env CDATA #REQUIRED idgraph CDATA #REQUIRED > <!-- Création d'un nouvel environnement. --> <!ELEMENT qnewenvironment EMPTY> <!ATTLIST qnewenvironment <!-- Optimisation automatique de l'ordre partiel des types de concepts. Par défaut, oui. Attention, s'il est optimisé, il ne doit plus être modifié. --> optorderc CDATA "true" <!-- Idem, sur les types de relations. --> optorderr CDATA "true" <!-- Idem, sur les types d'emboîtements. --> optordern CDATA "true" <!-- Optimisation automatique pour la recherche rapide de types de concepts par leur intitulé. --> optlabelc CDATA "true" <!-- Idem, sur les types de relations. --> optlabelr CDATA "true" <!-- Idem, sur les types d'emboîtements. --> optlabeln CDATA "true" <!-- Idem, sur les marqueurs individuels. --> optlabeli CDATA "true" > <!ELEMENT newenvironment EMPTY> <!ATTLIST newenvironment env CDATA #REQUIRED > <!-- Création d'un nouvel objet dans l'environnement. --> <!ELEMENT qnewenvironmentobject EMPTY> <!-- tp= "g": graphe (par défaut), "r": règle, "p": contrainte positive, "n": contrainte négative. --> <!ATTLIST qnewenvironmentobject env CDATA #REQUIRED tp (g | r | p | n) "g" > <!ELEMENT newenvironmentobject EMPTY> <!ATTLIST newenvironemntobject id CDATA #REQUIRED > <!-- Chargement d'un support. --> <!-- Le support peut être chargé à partir d'un fichier disponible sur l'hote serveur, en utilisant l'attribut file. Le support peut aussi être transféré à partir du client. Dans ce cas, l'attribut file ne doit pas être donné, et l'attribut embedded doit valoir "bcgct" ou "cogxml" ou "cgif". La balise qloadsupport doit alors avoir comme fils le texte bcgct ou cogxml du support. --> <!ELEMENT qloadsupport (#PCDATA)> <!ATTLIST qloadsupport env CDATA #REQUIRED file CDATA #IMPLIED embedded CDATA #IMPLIED > <!ELEMENT loadsupport EMPTY> <!ATTLIST loadsupport error CDATA #IMPLIED > <!-- Sauvegarde du support côté serveur. Si l'attribut embedded est passé, il doit valoir "bcgct", "cogxml" ou "cgif". Dans ce cas, l'attribut "file" ne doit pas être passé, et le resultat de l'écriture dans le format choisi n'est pas enregistré sur disque, mais transmis dans la réponse saveenvironmentobjects comme texte emboîté (dans un PCDATA). --> <!ELEMENT qsavesupport EMPTY> <!ATTLIST qsavesupport env CDATA #REQUIRED file CDATA #IMPLIED embedded CDATA #IMPLIED > <!ELEMENT savesupport (#PCDATA)> <!ATTLIST savesupport error CDATA #IMPLIED > <!-- Chargement de graphes (ou règles). Transfert d'un graphe en PCDATA par utilisation de l'attribut embedded. --> <!ELEMENT qloadgraphs (#PCDATA)> <!ATTLIST qloadgraphs env CDATA #REQUIRED file CDATA #IMPLIED embedded CDATA #IMPLIED > <!ELEMENT loadgraphs (loadedgraph*)> <!ATTLIST loadgraphs error CDATA #IMPLIED > <!ELEMENT loadedgraph EMPTY> <!ATTLIST loadedgraph id CDATA #REQUIRED > <!-- Sauvegarde d'un graphe (ou règle). Si l'attribut embedded est passé, il doit valoir "bcgct", "cogxml" ou "cgif". Dans ce cas, l'attribut "file" ne doit pas être passé, et le resultat de l'écriture dans le format choisi n'est pas enregistré sur disque, mais transmis dans la réponse saveenvironmentobjects comme texte emboîté (dans un PCDATA). --> <!ELEMENT qsaveenvironmentobjects (eoid*)> <!ATTLIST qsaveenvironmentobjects env CDATA #REQUIRED file CDATA #IMPLIED id CDATA #IMPLIED > <!ELEMENT saveenvironmentobjects (#PCDATA)> <!ATTLIST saveenvironmentobjects error CDATA #IMPLIED > <!-- Destruction d'un environnement. --> <!ELEMENT qdeleteenvironment EMPTY> <!ATTLIST qdeleteenvironment env CDATA #REQUIRED > <!ELEMENT deleteenvironment EMPTY> <!ATTLIST qdeleteenvironment > <!-- Destruction d'un objet de l'environnement --> <!ELEMENT qdeleteenvironmentobject EMPTY> <!ATTLIST qdeleteenvironmentobject env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT deleteenvironmentobject EMPTY> <!ATTLIST deleteenvironmentobject error CDATA #IMPLIED > <!-- Création d'un nouveau type ou marqueur --> <!ELEMENT qnewsupportobject EMPTY> <!-- tp= "c": type de concept, "r": type de relation, "n": type d'emboîtement, "i": marqueur individuel. Le label est l'intitulé (optionnel) de l'élément à créer. Si aucun intitulé n'est passé, utiliser qcommitsupportobject pour envoyer la propriété label. --> <!ATTLIST qnewsupportobject env CDATA #REQUIRED tp CDATA #REQUIRED label CDATA #IMPLIED > <!ELEMENT newsupportobject EMPTY> <!ATTLIST newsupportobject id CDATA #REQUIRED > <!-- Transfert d'un objet du support (TC, TR, TN, I) du client vers le serveur. --> <!ELEMENT qcommitsupportobject (properties*)> <!ATTLIST qcommitsupportobject env CDATA #REQUIRED tp CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT commitsupportobject EMPTY) <!ATTLIST commitenvironmentobject error CDATA #IMPLIED> > <!-- Projection. --> <!ELEMENT projection (couple*)> <!ELEMENT couple EMPTY> <!ATTLIST couple id1 CDATA #REQUIRED id2 CDATA #REQUIRED > <!-- Demande du calcul des projections d'un graphe dans un autre. --> <!ELEMENT qprojections (projectionconfig?)> <!ATTLIST qprojections env CDATA #REQUIRED <!-- id1: graphe projeté. --> id1 CDATA #REQUIRED <!-- id2: graphe dans lequel on projette. --> id2 CDATA #REQUIRED > <!ELEMENT projectionconfig EMPTY> <!ATTLIST projectionconfig <!-- Nombre max de projections trouvées. 0 pour ne pas limiter. --> maxsize CDATA #IMPLIED 0 <!-- Si true les projections (ensemble de couples) sont retournées, sinon, seul le nombre est retourné. --> memorize CDATA #IMPLIED "true" > <!ELEMENT projections (projection*)> <!ATTLIST projections <!-- Le nombre de projections trouvées. --> size CDATA #REQUIRED > <!-- Demande du calcul des applications d'une règle dans un graphe. --> <!ELEMENT qruleapplications (projectionconfig?)> <!ATTLIST qruleapplications env CDATA #REQUIRED <!-- idr: identificateur de la règle. --> idr CDATA #REQUIRED <!-- idg: identificateur du graphe. --> idg CDATA #REQUIRED > <!ELEMENT ruleapplications (projection*)> <!ATTLIST ruleapplications size CDATA #REQUIRED > <!-- Application d'une règle sur un graphe selon une projection. --> <!ELEMENT qruleapply (projection)> <!ATTLIST qruleapply env CDATA #REQUIRED idr CDATA #REQUIRED idg CDATA #REQUIRED > <!ELEMENT ruleapply EMPTY> <!ATTLIST ruleapply error CDATA #IMPLIED> > <!-- Fermeture d'un graphe par un ensemble de règles.--> <!-- Les éléments eoid fils de la balise doivent contenir les identificateurs des règles à appliquer. Si aucun eoid n'est précisé, toutes les règles de l'environnement sont utilisées. --> <!ELEMENT qrulesclosure (eoid*)> <!ATTLIST qrulesclosure env CDATA #REQUIRED <!-- Identificateur du graphe. L'application de l'opération modifie ce graphe. --> idg CDATA #REQUIRED <!-- Nombre maximum d'applications de règles. Par défaut 0, i.e. applications illimitées. --> maxa CDATA '0' > <!ELEMENT rulesclosure EMPTY> <!ATTLIST rulesclosure error CDATA #IMPLIED > <!-- Vérification de la validité d'un graphe par rapport à une contrainte. --> <!ELEMENT qconstraintsatisfaction> <!ATTLIST qconstraintsatisfaction env CDATA #REQUIRED <!-- idr: identificateur de la contrainte. --> idc CDATA #REQUIRED <!-- idg: identificateur du graphe. --> idg CDATA #REQUIRED > <!ELEMENT constraintsatisfaction> <!ATTLIST constraintsatisfaction <!-- result: "true" si le graphe vérifie la contrainte, "false" sinon. --> result CDATA #REQUIRED > <!-- Copie d'un objet de l'environnement (graphe ou règle). --> <!ELEMENT qcopyenvironmentobject EMPTY> <!ATTLIST qcopyenvironmentobject env CDATA #REQUIRED idsrc CDATA #REQUIRED iddst CDATA #REQUIRED > <!ELEMENT copyenvironmentobject EMPTY> <!ATTLIST copyenvironmentobject error CDATA #IMPLIED > <!-- Modification de la configuration d'une opération. --> <!ELEMENT qoperationconfig EMPTY> <!ATTLIST qoperationconfig env CDATA #REQUIRED <!-- ope: opération concernée, param: nom du paramètre à modifier, value: valeur. --> <!-- Les valeurs autorisées pour l'instant sont : - ope : iohandler param : graphobjectidentifiers value : true | false Si défini à true, l'opération de chargement de graphes conserve dans la propriété Property::IDENTIFIER l'identificateur lu dans le fichier. --> ope (iohandler) #REQUIRED param (graphobjectidentifiers) #REQUIRED value (true | false) #REQUIRED > <!ELEMENT operationconfig EMPTY> <!ATTLIST operationconfig error CDATA #IMPLIED > <!-- Ajout d'éléments dans un graphe. --> <!-- Ajout d'un sommet concept dans un graphe. --> <!ELEMENT qaddconcept> <!ATTLIST qaddconcept env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- idp : identificateur de l'InternalGraph dans lequel le sommet doit être créé, 0 par défaut. --> idp CDATA "0" <!-- idt : type de concept. Il s'agit de l'iSet du type à créer. --> idt CDATA #REQUIRED <!-- idm : marqueur individuel. Ne pas utiliser cet attribut pour créer un sommet concept générique. Si cet attribut est utilisé, il doit contenir l'iSet du marqueur. --> idm CDATA #IMPLIED > <!ELEMENT addconcept EMPTY> <!ATTLIST addconcept <!-- id : identificateur de l'objet créé dans le graphe. --> id CDATA #IMPLIED error CDATA #IMPLIED > <!-- Ajout d'un sommet relation, cf. qaddconcept. --> <!ELEMENT qaddrelation> <!ATTLIST qaddrelation env CDATA #REQUIRED idg CDATA #REQUIRED idp CDATA "0" idt CDATA #REQUIRED > <!ELEMENT addrelation EMPTY> <!ATTLIST addrelation id CDATA #IMPLIED error CDATA #IMPLIED > <!-- Ajout d'un emboîtement, cf. qaddconcept. --> <!ELEMENT qaddnesting> <!ATTLIST qaddnesting env CDATA #REQUIRED idg CDATA #REQUIRED idp CDATA #REQUIRED idt CDATA #REQUIRED > <!ELEMENT addnesting EMPTY> <!ATTLIST addnesting id CDATA #IMPLIED error CDATA #IMPLIED > <!-- Suppression d'un élément d'un graphe. --> <!ELEMENT qdelgraphobject> <!ATTLIST qdelgraphobject env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- id : identificateur de l'objet à détruire (iSet). --> id CDATA #REQUIRED > <!ATTLIST delgraphobject EMPTY> <!ATTLIST delgraphobject error CDATA #IMPLIED > <!-- Ajout d'une arête au graphe. --> <!ELEMENT qaddedge EMPTY> <!ATTLIST qaddedge env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- idr : identificateur du sommet relation dans le graphe (iSet). --> idr CDATA #REQUIRED <!-- idr : identificateur du sommet concept dans le graphe (iSet). --> idc CDATA #REQUIRED <!-- lab : étiquette de l'arête à créer. L'étiquette doit avoir une valeur inférieure ou égale à l'arité du type de relation du sommet relation. --> lab CDATA #REQUIRED > <!ELEMENT addedge EMPTY> <!ATTLIST addedge error CDATA #IMPLIED > <!-- Suppression d'une arête d'un graphe. --> <!ELEMENT qdeledge> <!ATTLIST qdeledge env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- idr : identificateur du sommet relation (iSet). --> idr CDATA #REQUIRED <!-- lab : étiquette de l'arête à supprimer. --> lab CDATA #REQUIRED > <!ATTLIST deledge EMPTY> <!ATTLIST deledge error CDATA #IMPLIED >