Table of Contents

Le système d'exécution de Greenstone

Le présent chapitre décrit le système d'exécution de Greenstone pour que vous puissiez comprendre, augmenter et étendre ses fonctionnalités. Le logiciel est écrit en C++ est utilise intensivement l'héritage virtuel. Si vous ne connaissez pas bien ce langage il est préférable que vous l'appreniez avant de continuer la lecture. Deitel et Deitel (1994) proposent un didacticiel complet, mais la référence incontournable est le Stroustrup (1997).

Nous commençons par expliquer la philosophie de la conception qui a sous-tendu le système d'exécution car elle a des conséquences importantes sur l'implémentation. Nous fournissons ensuite les détails d'implémentation, qui forment la partie principale de ce chapitre. La version de Greenstone décrite ici est la version CGI (ou version Bibliothèque Web pour les utilisateurs de Windows). La version Bibliothèque locale utilise le même code source mais dispose en frontal d'un serveur web intégré. C'est également un processus persistent.

Structure des processus

<imgcaption figure_overview_of_a_general_greenstone_system|%!– id:760 –%Vue d'ensemble d'un système Greenstone générique ></imgcaption>

La figure <imgref figure_overview_of_a_general_greenstone_system> montre plusieurs utilisateurs, représentés par des terminaux informatiques en haut du diagramme, en train d'accéder à trois collections Greenstone. Avant d'être mises en ligne, ces collections subissent les processus d'import et de construction décrits dans les chapitres précédents. Tout d'abord, les documents, représentés en bas de la figure, sont importés dans le format d'archivage de Greenstone, qui est compatible XML. Les fichiers d'archives servent ensuite à la construction de plusieurs index de recherche et d'une base de données d'informations de la collection comprenant les structures hiérarchiques permettant la navigation. La collection est alors prête à être mise en ligne et à répondre à des requêtes d'informations.

Deux composants sont centraux dans la conception du système d'exécution: les «réceptionnistes» et les «serveurs de collection». Du point de vue de l'utilisateur, un réceptionniste est le point de contact avec la bibliothèque numérique. Il accepte les données fournies par l'utilisateur, généralement sous la forme de saisies au clavier et de clics de souris; il les analyse; et il émet ensuite une requête au(x) serveur(s) de collection appropriés. Ce dernier localise l'information recherchée et la renvoie au réceptionniste pour que ce dernier la présente à l'utilisateur. Les serveurs de collection se comportent comme un mécanisme abstrait qui gère le contenu de la collection, alors que les réceptionnistes gèrent l'interface utilisateur.

<imgcaption figure_greenstone_system_using_the_null_protocol|%!– id:763 –%Système Greenstone utilisant le «protocole vide» ></imgcaption>

Comme illustré dans la figure <imgref figure_overview_of_a_general_greenstone_system>, les réceptionnistes communiquent avec les serveurs de collection en utilisant un protocole défini. L'implémentation de ce protocole dépend de l'ordinateur et de la configuration du système de bibliothèque numérique. Le cas le plus fréquent et le plus simple est celui d'un réceptionniste et d'un serveur de collection, fonctionnant tous deux sur le même ordinateur. C'est ce qui est produit par l'installation par défaut de Greenstone. Les deux processus sont alors combinés dans un unique exécutable (appelé library), et le protocole se réduit par conséquent à des appels de fonctions. C'est ce que nous appelons leprotocole vide(null protocol). C'est la base des systèmes de bibliothèque numérique Greenstone clef en main. Cette configuration simplifiée est illustrée figure <imgref figure_greenstone_system_using_the_null_protocol>, où le réceptionniste, le protocole et le serveur de collection sont regroupés sous la forme d'une unique entité, le programme library. Le présent chapitre a pour but d'expliquer son fonctionnement.

Un «serveur» est habituellement un processus persistent qui, une fois démarré, fonctionne indéfiniment, en répondant aux requêtes entrantes. Malgré son nom, cependant, le serveur de collection dans la configuration du protocole vide n'est pas un serveur au sens habituel. En fait, à chaque requête d'une page web de Greenstone, le programme library est démarré (par le mécanisme CGI), répond à la requête, et quitte. Nous n'appelons le serveur de collection «serveur» que parce qu'il est également conçu pour fonctionner dans la configuration plus générale de la figure <imgref figure_overview_of_a_general_greenstone_system>.

De manière surprenante, ce cycle de démarrage, traitement, et fin d'exécution n'est pas aussi lent qu'on pourrait le craindre, et le service fourni est parfaitement utilisable. Il est clair cependant que ce système est inefficace. Un mécanisme appelé Fast-CGI (www.fastcgi.com) fournit un compromis. Si on l'utilise, le programme library demeure en mémoire après la première exécution, et les lots suivants d'arguments CGI lui sont communiqués, ce qui évite les surcharges d'initialisation, et ressemble beaucoup à un serveur au sens classique. Fast-CGI est une option dans Greenstone, que l'on active en recompilant le code source de Greenstone avec les bibliothèques appropriées.

Pour fournir un autre choix que le protocole vide, nous avons également développé le protocole Greenstone en utilisant le célèbre système CORBA (Slamaet al., 1999). Il utilise un paradigme orienté objet unifié pour activer différents processus, fonctionnant sur différentes plates-formes, et implémentés en différents langages de programmation, pour accéder au même ensemble d'objets distribués sur l'Internet (ou tout autre réseau). Des scénarios comme celui de la figure <imgref figure_overview_of_a_general_greenstone_system> peuvent ensuite être entièrement implémentés, avec des réceptionnistes et des serveurs de collection fonctionnant sur des ordinateurs différents.

<imgcaption figure_graphical_query_interface_to_greenstone|%!– id:768 –%Interface graphique de requêtes de Greenstone ></imgcaption>

Ceci permet la mise en place d'interfaces bien plus sophistiquées pour mettre en place exactement les mêmes collections de bibliothèques numériques. Un exemple parmi tant d'autres, la figure <imgref figure_graphical_query_interface_to_greenstone> représente une interface graphique de requêtes, fondée sur des diagrammes de Venn, qui permet aux utilisateurs de manipuler directement des requêtes booléennes. Écrite en Java, cette interface fonctionne localement, sur l'ordinateur de l'utilisateur. Utilisant CORBA, elle accède à un serveur de collection Greenstone distant, écrit en C++.

Ce protocole distribué faisant toujours l'objet de recherches en raffinement et utilisabilité, le présent manuel ne le détaillera pas davantage (voir Bainbridgeet al., 2001, pour des informations complémentaires à ce sujet).

Cadre conceptuel

<imgcaption figure_generating_the_about_this_collection_page|%!– id:772 –%Génération de la page «à propos» ></imgcaption>

La figure <imgref figure_generating_the_about_this_collection_page> représente la page «à propos» d'une collection Greenstone particulière (la collection du projet Gutenberg). Examinez l'URL au sommet de la figure: cette page est générée par l'exécution du programme CGI library, qui est l'exécutable mentionné plus haut, comprenant à la fois le réceptionniste et le serveur de collection, reliés entre eux par le protocole vide. Les arguments passés au programme library sont c=gberg, a=p, et p=about. On peut les interpréter comme suit:

Pour la collection du projet Gutenberg (c=gberg), l'action est de générer une page (a=p), et la page à générer s'appelle about – à propos de … – (p=about).

<imgcaption figure_greenstone_runtime_system|%!– id:775 –%Système d'exécution de Greenstone ></imgcaption>

La figure <imgref figure_greenstone_runtime_system> illustre les portions principales du système d'exécution de Greenstone. Au sommet, le réceptionniste initialise d'abord ses composants, puis analyse les arguments CGI pour savoir quelle action appeler. Lors de l'exécution de l'action (ce qui comprend d'autres traitements des arguments CGI), le logiciel utilise le protocole pour accéder au contenu de la collection. La réponse est utilisée pour générer une page web, avec l'aide du composant de mise en forme et du langage de macros.

Le langage de macros, que nous avons rencontré section controlling_the_greenstone_user_interface, fournit un système de bibliothèque numérique Greenstone d'un style consistant, et permet de créer des interfaces dans différentes langues. L'interaction avec la bibliothèque fournit les squelettes des pages web; les macros du répertoire GSDLHOME/macros leur donnent chair.

L'objet de langage de macros représenté figure <imgref figure_greenstone_runtime_system> a pour rôle de lire ces fichiers et de stocker le résultat de l'analyse en mémoire. Toute action peut utiliser cet objet pour développer une macro. On peut même créer de nouvelles définitions de macros pour en écraser d'anciennes, ce qui ajoute une dimension dynamique à l'utilisation des macros.

La représentation de la page «à propos» (figure <imgref figure_generating_the_about_this_collection_page>) est connue avant l'exécution, et consignée dans le fichier de macros about.dm. Les en-têtes, les pieds, et l'image de fond n'y sont même pas mentionnés car ils se trouvent dans le paquetage de macros global. Cependant, le texte spécifique d'«à propos» d'une collection particulière n'est pas connu d'avance; il est stocké dans la base de données d'informations de la collection lors du processus de construction. Cette information, rapatriée à l'aide du protocole, est stockée sous _collectionextra_ dans le paquetage de macros global. Vous remarquerez que ce nom de macro ressemble presque exactement au nom utilisé pour exprimer cette information dans le fichier de configuration de collection décrit section configuration_file. Pour générer le contenu de la page, la macro _content_ (contenu) du paquetage about (présenté figure <imgref figure_part_of_the_aboutdm_macro_file>) est développée, ce qui à son tour développe _textabout_ (texte d'à propos), qui accède à la macro _collectionextra_, qui venait d'être placée là de façon dynamique.

Un autre ingrédient important est l'objet de mise en forme, ou formatage. Les instructions de mise en forme du fichier de configuration de collection affectent la présentation de portions particulières de la collection, comme nous l'avons expliqué section formatting_greenstone_output. Elles sont gérées par l'objet de mise en forme représenté figure <imgref figure_greenstone_runtime_system>. La tâche principale de cet objet est d'analyser et d'évaluer des instructions telles que les chaînes de formatage de la figure <imgref figure_excerpt_from_the_demo_collection_collect>. Comme nous l'avons vu en section formatting_greenstone_output, de telles chaînes peuvent comprendre des références à des méta-données entre crochets (comme par exemple [Title] pour le titre), qu'il faut ensuite rapatrier du serveur de collection. Les objets de mise en forme et de langage de macros interagissent, car les instructions de formatage peuvent comprendre des macros qui, une fois développées, incluent des méta-données, qui une fois développées incluent des macros, etc.

Le serveur de collection, au bas de la figure <imgref figure_greenstone_runtime_system>, connaît lui aussi une phase d'initialisation, préparant les objets filtre et source pour répondre à des requêtes entrantes fournies selon le protocole, et un objet de recherche pour les assister dans cette tâche. Les objets de recherche accèdent en fin de chaîne aux index et à la base de données d'informations de la collection, tous créés lors de la construction de la collection.

Si on ne compte pas les lignes vides, le réceptionniste contient 15 000 lignes de code. Le serveur de collection en compte 5 000, dont les trois quarts correspondent à des fichiers d'en-têtes. Le serveur de collection est plus compact car le rapatriement de données se fait à travers deux programmes pré-compilés. MG, un système de rapatriement dans le corps du texte, sert à la recherche, et GDBM, un système de gestion de bases de données, renferme la base de données d'informations de la collection.

Pour encourager extensibilité et souplesse, Greenstone utilise largement l'héritage – en particulier dans les objets d'action, de filtre, de source, et de recherche. Pour une simple bibliothèque numérique ne contenant que des collections textuelles, ceci signifie qu'il vous faudra en apprendre un peu plus pour programmer le système. Mais cela signifie aussi qu'on pourrait facilement remplacer MG et GDBM si le besoin devait un jour s'en faire sentir. De plus, l'architecture du logiciel est assez riche pour accepter toutes les fonctionnalités multimédia, telles que le contrôle de l'interface par des systèmes de reconnaissance vocale, ou la soumission de requêtes sous la forme d'images dessinées.

Agencement du cadre conceptuel

Les sections collection_server et receptionist détaillent le fonctionnement du serveur de collection et du réceptionniste, en passant plus de temps sur chacun des modules de la figure <imgref figure_greenstone_runtime_system> et en décrivant leur implémentation. Il est utile de commencer par travailler sur des exemples d'interaction utilisateur avec Greenstone et de décrire ce qui se passe en coulisses. Nous supposerons pour le moment que tous les objects sont correctement initialisés. L'initialisation est une procédure assez compliquée, que nous revisiterons dans la section initialisation.

Effectuer une recherche

<imgcaption figure_searching_gutenberg_for_darcy|%!– id:787 –%Recherche de «Darcy» dans le projet Gutenberg ></imgcaption>

Quand un utilisateur effectue une requête en pressant le boutton Démarrer la recherche sur la page de recherche, une nouvelle action de Greenstone est invoquée, qui aboutit à la génération d'une nouvelle page HTML à l'aide du langage de macros. La figure <imgref figure_searching_gutenberg_for_darcy> montre le résultat de la recherche dans la collection du projet Gutenberg du nom Darcy. La page de recherche comprenait originellement l'instruction a=q cachée dans le HTML. Cette instruction est activée lorsque le bouton de recherche est enfoncé, et appelle l'action queryaction. L'exécution de queryaction effectue un appel à l'objet de filtre de la collection (c=gberg) à travers le protocole.

Les filtres sont une fonction de base importante des serveurs de collection. Adaptés aux activités de recherche et de navigation, ils fournissent une manière de sélectionner un sous-ensemble d'informations dans la collection. Dans le cas présent, queryaction met en place une requête de filtre en:

Les appels du protocole sont synchrones: le réceptionniste est effectivement bloqué tant que la requête de filtre n'a pas été traitée par le serveur de collection et que les données générées n'ont pas été renvoyées.

Lors d'un appel de protocole de type QueryFilter, l'objet de filtre (de la figure <imgref figure_greenstone_runtime_system>) décode les options et effectue un appel à l'objet de recherche, qui utilise MG pour la recherche proprement dite. L'objet de recherche a pour rôle de fournir une interface abstraite de programme qui permet la recherche, indépendamment de l'outil de recherche utilisé de façon sous-jacente. Le format utilisé pour le renvoi des résultats utilise lui aussi l'abstraction, ce qui oblige l'objet de recherche à traduire les données générées par l'outil de recherche sous une forme standard.

Lorsque les résultats de la recherche ont été renvoyés au réceptionniste, la suite des événements consiste à mettre en forme les résultats pour les afficher, en utilisant les objets de mise en forme et de langage de macros. Comme le montre la figure <imgref figure_searching_gutenberg_for_darcy>, ceci suppose de générer l'en-tête, le pied de page, la barre de navigation et le fond de page standard de Greenstone; de répéter la partie principale de la page de requêtes juste en dessous de la barre de navigation; et d'afficher une icone de livre ainsi que le titre et l'auteur pour chacun des résultats de la recherche. La mise en forme de cette dernière partie est dictée par l'instruction format SearchVList du fichier de configuration de collection. Avant qu'on puisse afficher les méta-données de titre et d'auteur, il faut les rapatrier depuis le serveur de collection, ce qui suppose des appels supplémentaires à travers le protocole, en utilisant cette fois BrowseFilter (filtre de navigation).

Rapatrier un document

À la suite de la requête précédente pour Darcy, voyons ce qui se passe lors de l'affichage d'un document. La figure <imgref figure_the_golf_course_mystery> montre ce qui se passe quand on clique sur l'icone jouxtant le titre The Golf Course Mystery (le mystère du parcours de golf) dans la figure <imgref figure_searching_gutenberg_for_darcy>.

<imgcaption figure_the_golf_course_mystery|%!– id:798 –%The Golf Course Mystery (le mystère du parcours de golf) ></imgcaption>

Le texte source de la collection Gutenberg consiste en un long fichier par livre. Au moment de la construction, ces fichiers sont ventilés sous forme de pages différentes environ toutes les 200 lignes, et les informations relatives à chaque page sont stockées dans les index et la base de données d'informations de la collection. Au sommet de la figure <imgref figure_the_golf_course_mystery> on constate que ce livre comprend 104 pages engendrées automatiquement, et on remarque juste en dessous le début de la première page: le copiste, le titre, l'auteur, et le début d'une table des matières (qui fait partie du texte source de Gutenberg; elle n'a pas été engendrée par Greenstone). En haut à gauche on trouve des boutons qui contrôlent la mise en forme du document: une seule page ou tout le document; s'il faut mettre en valeur le terme de la requête ou non; et s'il faut que le livre soit ou non affiché dans sa propre fenêtre, détachée des activités principales de recherche et de navigation. En haut à droite on trouve une aide à la navigation qui propose un accès direct à n'importe quelle page du livre: il suffit de taper le numéro de la page et d'enfoncer le bouton «aller à la page». On peut aussi se rendre aux pages précédente ou suivante en cliquant sur les icones de flèches aux côtés de l'outil de sélection de page.

L'action de rapatriement des documents, documentaction, est spécifiée par a=d et elle accepte quelques arguments supplémentaires. Le plus important est le document à rapatrier: il est spécifié par la variable d. Dans la figure <imgref figure_the_golf_course_mystery>, il a la valeurd=HASH51e598821ed6cbbdf0942b.1pour rapatrier la première page du document d'identifiantHASH51e598821ed6cbbdf0942bqui s'appelle aussi, de manière plus agréable, The Golf Course Mystery (le mystère du parcours de golf). Il existe d'autres variables, spécifiant s'il faut ou non mettre en valeur le terme de la requête (hl, pour highlighting) et quelle page du livre afficher (gt). Ces variables permettent de proposer les fonctionnalités offertes par les boutons présentés sur la page web de la figure <imgref figure_the_golf_course_mystery>, et décrits ci-dessus. Si l'une de ces variables manque à l'appel, c'est le comportement par défaut correspondant qui sera utilisé.

Cette action suit une procédure similaire à celle de queryaction: évaluation des arguments CGI, accès au serveur de collection en utilisant le protocole, et utilisation du résultat pour engendrer une page web. Les options relatives au document sont décodées à partir des arguments CGI et stockées dans l'objet pour plus tard. Pour rapatrier le document depuis le serveur de collection, on n'a besoin que de l'identifiant de document pour effectuer l'appel de protocole à get_document() (obtenir le document). Après l'envoi du texte, un travail considérable de remise en forme est nécessaire. Pour ce faire, le code de documentaction accède aux arguments stockés dans l'objet et utilise les objets de mise en forme et de langage de macros.

La figure <imgref figure_browsing_titles_in_the_gutenberg_collection> montre un exemple de navigation, où l'utilisateur a choisi titres A-Z et choisi le lien hypertexte de la lettre K. C'est également documentaction qui gère cela, ce qu'on constate en observant l'argument CGI a=d, comme précédemment. Cependant, alors qu'auparavant la requête CGI comprenait une variable d, il n'y en a aucune cette fois-ci. À la place, le noeud à afficher pour navigation dans la hiérarchie de classification est spécifié dans la variable cl. Dans notre cas, ceci représente les titres groupés sous la lettre K. Cette liste a été formée au moment de la construction et fut stockée dans la base de données d'informations de la collection.

<imgcaption figure_browsing_titles_in_the_gutenberg_collection|%!– id:804 –%Navigation dans les titres de la collection Gutenberg ></imgcaption>

Les enregistrements représentant des noeuds de classificateur dans la base de données utilisent le préfixe CL, suivi de nombres séparés par des points (.) pour désigner où ils résident au sein de la structure récursive. Si on ignore le bouton de recherche, situé tout à gauche de la barre de navigation, les classificateurs sont numérotés séquentiellement et par ordre croissant, de gauche à droite, en commençant à 1. Ainsi, le noeud de classificateur de premier niveau pour les titres dans notre exemple est CL1 et la page recherchée est engendrée en positionnant cl=CL1.11. On peut observer cela dans l'URL au sommet de la figure <imgref figure_browsing_titles_in_the_gutenberg_collection>.

Lors du traitement d'une requête de document de type cl, l'objet de filtre est utilisé pour rapatrier le noeud à travers le protocole. Selon les données renvoyées, d'autres appels de protocole sont effectués pour rapatrier les méta-données des documents. Dans le cas présent, ce sont les titres des livres qui sont rapatriés. Mais si le noeud avait été un noeud interne dont les fils avaient eux-mêmes été des noeuds, les titres des noeuds fils auraient eux aussi été rapatriés. Du point de vue du code cela revient à la même chose, et c'est géré par le même mécanisme.

Enfin, toutes les informations rapatriées sont reliées les unes aux autres à l'aide du langage de macros, pour produire la page web représentée figure <imgref figure_browsing_titles_in_the_gutenberg_collection>.

Génération de la page d'accueil

<imgcaption figure_greenstone_home_page|%!– id:809 –%Page d'accueil de Greenstone ></imgcaption>

Comme dernier exemple, examinons le cas de la génération de la page d'accueil de Greenstone. La figure <imgref figure_greenstone_home_page> montre – dans le cas de l'installation par défaut de Greenstone – la page d'accueil après l'installation de certaines collections de test. Son URL, qu'on peut voir au sommet de la capture d'écran, comprend les arguments a=p et p=home. Ainsi, à l'instar de la page «à propos», elle a été engendrée par pageaction (a=p), mais dans le cas présent la page à produire est la page home – accueil – (p=home). Le langage de macros accède par conséquent au fichier home.dm. Nul besoin de spécifier une collection (à l'aide de la variable c) dans le cas présent.

La page d'accueil a pour but de présenter les collections disponibles. Cliquer sur une icone emmène l'utilisateur sur la page à propos de la collection choisie. Le menu des collections est généré automatiquement à chaque chargement de la page, en se fondant sur les collections présentes dans le système de fichiers à ce moment-là. Quand une nouvelle collection est mise en ligne, elle apparaît automatiquement sur la page d'accueil lorsque cette page est rechargée ou réactualisée, pour peu que cette collection ait été spécifiée comme «publique».

Pour ce faire, le réceptionniste utilise, évidemment, le protocole. Dans le cadre de l'évaluation des arguments CGI, pageaction est programmé pour détecter le cas particulier p=home. L'action utilise alors get_collection_list() (obtenir la liste des collections), appel de protocole servant à établir l'ensemble actuel de collections mises en ligne. Pour chacune de ces collections il appelle get_collectinfo() (obtenir les informations de collection) pour recevoir les informations la concernant. Ces informations précisent si la collection est publiquement disponible, quelle est l'URL menant à l'icone de la collection (si une telle icone existe), et le nom complet de la collection, et elles sont utilisées pour générer l'entrée appropriée sur la page d'accueil de la collection.

Code source

<tblcaption table_standalone_programs_included_in_greenstone|Programmes indépendants inclus dans Greenstone></tblcaption>

< - 132 397 >
setpasswd/ Support des mots de passe sous Windows.
getpw/ Support des mots de passe sous Unix.
txt2db/ Convertit un format de texte ASCII à la XML en le format de la base de données de GNU.
db2txt/ Convertit le format de la base de données de GNU en un format de texte ASCII à la XML.
phind/ Outil de navigation hiérarchique dans les groupes de mots.
hashfile/ Calcule un identifiant de document unique, fondé sur le contenu du fichier.
mgpp/ Version ré-écrite et mise à jour du paquetageManaging Gigabytes(gérer les giga-octets), en C++.
w32server/ Serveur de bibliothèque locale pour Windows.
checkis/ Support spécifique pour l'installation de Greenstone sous Windows.

Le code source du système d'exécution se trouve dans GSDLHOME/src. Il occupe deux sous-répertoires, recpt pour le code du réceptionniste, et colservr pour celui du serveur de collection. Greenstone fonctionne sur les systèmes Windows à partir de la version 3.1, qui impose malheureusement une limite de huit caractères sur les noms de fichiers et de répertoires; c'est ce qui explique l'utilisation d'abréviations cryptiques telles que recpt et colservr. Les sous-répertoires restants comprennent des outils indépendants, principalement utilisés en renfort du processus de construction. Ils sont listés dans la table <tblref table_standalone_programs_included_in_greenstone>.

Un autre répertoire, GSDLHOME/lib, inclut des objets de bas niveau utilisés à la fois par le réceptionniste et par le serveur de collection. Ce code est décrit dans la section common_greenstone_types.

Greenstone utilise énormément la bibliothèque standard de patrons (STL), une bibliothèque bibliothèque très répandue de code C++, développée par Silicon Graphics (www.sgi.com), et qui est l'aboutissement de nombreuses années de conception et de développement. À l'instar de toutes les bibliothèques de programmation, il faut un certain temps pour l'apprendre. L'annexe AppendixA donne un bref aperçu de ses principales portions utilisées tout au long du code de Greenstone. Pour une description plus détaillée, consultez le manuel de référence officiel de STL, disponible en ligne sur le site webwww.sgi.com, ou l'un des nombreux manuels traitant de STL, tel que celui de Josuttis (1999).

Types de base de Greenstone

Les objets définis dans le répertoire GSDLHOME/lib sont des objets de bas niveau de Greenstone, construits par-dessus STL, et qui sont utilisés tout au long du code source. Nous détaillons tout d'abord text_t, un objet utilisé pour représenter du texte Unicode. Nous résumerons ensuite le but de chacun des fichiers de bibliothèque.

L'objet text_t

Greenstone fonctionne avec de nombreuses langues, tant pour le contenu d'une collection que pour l'interface utilisateur. Pour gérer cela, nous avons utilisé Unicode tout au long du code source, et l'objet sous-jacent qui réalise une chaîne Unicode s'appelle text_t.

<imgcaption figure_the_text_t_api|%!– id:840 –%API de text_t (abrégée) %!– withLineNumber –%></imgcaption>

typedef vector<unsigned short> usvector;
 
class text_t {
protected:
usvector text;
unsigned short encoding; // 0 = unicode, 1 = other
 
public:
   // constructors
   text_t ();
   text_t (int i);
   text_t (char *s); // assumed to be a normal c string
 
   void setencoding (unsigned short theencoding);
   unsigned short getencoding ();
 
   // STL container support
   iterator begin ();
   iterator end ();
 
   void erase(iterator pos);
   void push_back(unsigned short c);
   void pop_back();
 
   void reserve (size_type n);
 
   bool empty () const {return text.empty();}
   size_type size() const {return text.size();}
 
   // added functionality
   void clear ();
   void append (const text_t &t);
 
   // support for integers
   void appendint (int i);
   void setint (int i);
   int getint () const;
 
   // support for arrays of chars
   void appendcarr (char *s, size_type len);
   void setcarr (char *s, size_type len);
};

Unicode stocke chaque caractère sur deux octets. La figure <imgref figure_the_text_t_api> montre les fonctionnalités principales de l'interface de programmation (API, pour Application Program Interface) de text_t. Les deux octets sont assurés par l'utilisation du type standard C++ short, défini comme étant un entier sur deux octets. Le type de données central de l'objet text_t est un tableau dynamique de short non signés, construit par la déclaration STL vector<unsigned short>, et baptisé usvector (pour unsigned short vector).

Les fonctions de construction (lignes 10-12) gèrent explicitement trois formes d'initialisation: construction sans paramètres, qui engendre une chaîne Unicode vide; construction avec un paramètre entier, qui engendre une version texte Unicode de la valeur numérique fournie; et construction avec un paramètre char*, qui traite l'argument comme une chaîne C++ terminée par un caractère nul et en génère une version Unicode.

Ensuite, la plus grande partie du code (lignes 17-28) s'occupe de maintenir un conteneur de type vecteur STL: begin() (début), end() (fin), push_back() (remettre), empty() (vide), etc. On peut aussi effacer des chaînes, concaténer des chaînes, ainsi que convertir une valeur entière dans une chaîne de texte Unicode et renvoyer la valeur entière correspondant à un texte qui représente un nombre.

<imgcaption figure_overloaded_operators_to_text_t|%!– id:844 –%Opérateurs surchargés pour text_t %!– withLineNumber –%></imgcaption>

class text_t {
   // ...
   public:
   text_t &operator=(const text_t &x);
   text_t &operator+= (const text_t &t);
   reference operator[](size_type n);
 
   text_t &operator=(int i);
   text_t &operator+= (int i);^ \\
   text_t &operator= (char *s);
   text_t &operator+= (char *s);
 
   friend inline bool operator!=(const text_t& x, const text_t& y);
   friend inline bool operator==(const text_t& x, const text_t& y);
   friend inline bool operator< (const text_t& x, const text_t& y);
   friend inline bool operator> (const text_t& x, const text_t& y);
   friend inline bool operator>=(const text_t& x, const text_t& y);
   friend inline bool operator<=(const text_t& x, const text_t& y);
   // ...
};

De nombreux opérateurs surchargés n'apparaissent pas dans la figure <imgref figure_the_text_t_api>. Ils sont montrés dans la figure <imgref figure_overloaded_operators_to_text_t> pour donner un avant-goût des opérations possibles. La ligne 4 permet l'affectation d'un objet text_t à un autre, et la ligne 5 surcharge l'opérateur +=, pour fournir une manière plus naturelle de concaténer un objet text_t à la fin d'un autre. Il est également possible, à travers la ligne 6, d'accéder à un caractère Unicode particulier (représenté sous la forme d'un short) en utilisant des indices de tableaux [ ]. On fournit aussi des opérateurs d'affectation et de concaténation pour les entiers et les chaînes C++. Les lignes 12-18 fournissent des opérateurs booléens de comparaison de deux objets text_t: égale, différent de, précède alphanumériquement, etc.

Il existe aussi des fonctions membre qui prennent des arguments de type const au lieu d'arguments de type non-const, mais elles ne sont pas représentées ici. Une telle répétition est habituelle dans des objets C++, ce qui augmente le code de l'API mais ne la rend pas plus grosse conceptuellement. En fait, de nombreuses fonctions sont implémentées sous la forme d'instructions tenant sur une ligne. Pour plus de détails, reportez-vous au fichier source GSDLHOME/lib/text_t.h.

Le code des bibliothèques Greenstone

Les fichiers d'en-têtes du répertoire GSDLHOME/lib comprennent un mélange de fonctions et d'objets qui fournissent un support utile au système d'exécution de Greenstone. Là où l'efficacité prime, les fonctions et les fonctions membre sont déclarées inline. Dans la majorité des cas, les détails d'implémentation sont contenus dans le fichier .cpp correspondant au fichier d'en-têtes.

<tblcaption table_table|##HIDDEN##></tblcaption>

< - 100 450 >
cfgread.h Fonctions de lecture et d'écrite de fichiers de configuration. Par exemple, la fonction read_cfg_line() (lit une ligne de configuration) prend comme arguments le flux à utiliser en entrée et le tableau text_tarray (raccourci pour vector <text_t>) à remplir avec les données lues.
display.h Objet sophistiqué utilisé par le réceptionniste pour positionner, stocker et développer les macros, et gérer les types, La section receptionist le détaille.
fileutil.h Support fonctionnel pour des fonctionnalités de gestion de fichiers indépendantes du système d'exploitation. Par exemple, la fonction de nom filename_cat() (affiche nom-de-fichier) accepte jusqu'à arguments text_t et renvoie un text_t qui est le résultat de la concaténation des éléments en utilisant le séparateur de répertoires approprié pour le système d'exploitation en cours d'utilisation.
gsdlconf.h Fonctions propres au système qui répondent à des questions telles que: le système d'exploitation a-t-il besoin d'accéder au fichier strings.h aussi bien qu'au fichier string.h? Toutes les valeurs appropriés pour le verrouillage de fichier sont-elles correctement positionnées?
gsdltimes.h Support fonctionnel pour la date et l'heure. Par exemple, la fonction time2text() (temps vers texte) prend l'heure de l'ordinateur, exprimée en nombre de secondes écoulées depuis le premier janvier 1970, et la convertit sous la forme AAAA/MM/JJ hhmmss, qu'elle renvoie comme un objet text_t.
gsdltools.h Support varié pour le système d'exécution de Greenstone: clarifie le côté petit boutien (littleEndian) ou grand boutien (bigEndian); vérifie si Perl est disponible; exécute une commande système (avec quelques gadgets); et échappe les caractères spéciaux de macros dans une chaîne text_t.
gsdlunicode.h Série d'objets hérités qui gèrent le traitement de chaînes Unicode text_t à travers des flux en entrée et en sortie, comme par exemple les conversions Unicode vers UTF-8 et vice versa; et la destruction des espaces de largeur nulle. Gère également les fichiers de correspondance à travers l'objet mapconvert, et les tables de correspondance sont chargées dans le répertoire GSDLHOME/mappings.
text_t.h Principalement, l'objet de texte Unicode décrit ci-dessus. Fournit également deux classes de conversion de flux: inconvertclass (classe de conversion en entrée) et outconvertclass (classe de conversion en sortie). Ce sont les classes de base utilisées dans le fichier gsdlunicode.h.

Serveur de collection

Nous allons maintenant expliquer systématiquement tous les objets apparaissant dans le cadre conceptuel de la figure <imgref figure_greenstone_runtime_system>. Nous commençons au bas du diagramme – c'est-à-dire aux fondations du système – avec les objets de recherche, source, et de filtre, et remonterons les couches du protocole vers les composants centraux du réceptionniste: actions, mise en forme, et langage de macros. Nous nous concentrerons ensuite sur l'initialisation des objets, puisque cette dernière est plus facile à comprendre quand on connaît le rôle des différents objets.

La plupart des classes centrales du cadre conceptuel sont exprimées à l'aide d'héritage virtuel pour favoriser l'extensibilité. Avec l'héritage virtuel, les objets hérités peuvent être passés en tant que leur classe de base, mais tout appel de fonction invoque la version définie dans l'objet hérité. En assurant que le code source de Greenstone utilise partout la classe de base, sauf aux endroits où s'effectue la construction des objets à proprement parler, nous garantissons donc que différentes implémentations – utilisant peut-être des technologies sous-jacentes radicalement différentes – peuvent facilement être mises en place.

Supposons par exemple qu'une classe de base nommée BaseCalc (calculs de base) fournisse l'arithmétique classique: additions, soustractions, multiplications et divisions. Si toutes ses fonctions sont déclarées virtuelles, et que tous les arguments et types renvoyés sont déclarés comme des chaînes, on peut facilement implémenter des versions héritées de l'objet. L'une, FixedPrecisionCalc (calcul en précision finie), pourrait utiliser les fonctions de la bibliothèque C pour faire les conversions entre chaînes et entiers, et implémenter les calculs en utilisant les opérateurs arithmétiques standard: +, -, *, et /. Une autre, appelée InfinitePrecisionCalc (calcul en précision infinie), pourrait accéder aux arguments des chaînes caractère par caractère, en implémentant les opérations arithmétiques qui sont en principe d'une précision infinie. En écrivant un programme qui utilise partout BaseCalc, l'implémentation peut passer de la précision finie à la précision infinie ou inversement en modifiant une seule ligne: l'endroit où l'objet de calcul est construit.

L'objet de recherche

<imgcaption figure_search_base_class_api|%!– id:870 –%API de la classe de base de recherche Search ></imgcaption>

class searchclass {
public:
   searchclass ();
   virtual ~searchclass ();
   // the index directory must be set before any searching
   // is done
   virtual void setcollectdir (const text_t &thecollectdir);
   // the search results are returned in queryresults
   // search returns 'true' if it was able to do a search
   virtual bool search(const queryparamclass &queryparams,
                         queryresultsclass &queryresults)=0;
   // the document text for 'docnum' is placed in 'output'
   // docTargetDocument returns 'true' if it was able to
   // try to get a document
   // collection is needed to see if an index from the
   // collection is loaded. If no index has been loaded
   // defaultindex is needed to load one
   virtual bool docTargetDocument(const text_t &defaultindex,
                                               const text_t &defaultsubcollection,
                                               const text_t &defaultlanguage,
                                               const text_t &collection,
                                               int docnum,
                                               text_t &output)=0;
protected:
   querycache *cache;
   text_t collectdir; // the collection directory
};

La figure <imgref figure_search_base_class_api> montre l'API de la classe de base de l'objet de recherche présenté dans la figure <imgref figure_greenstone_runtime_system>. Elle définit deux fonctions membre virtuelles: search() (recherche) et docTargetDocument() (doc document-cible). Comme le signifie le =0 qui suit la déclaration des arguments, il s'agit de fonctions pures – ce qui signifie qu'une classe qui hérite de cet objet devra implémenter ces deux fonctions, faute de quoi le compilateur se plaindra.

La classe comprend également deux champs de données protégés: collectdir (répertoire de collection) et cache (tampon). Un objet de recherche est instancié pour une collection particulière, et le champ collectdir sert à stocker l'endroit du système de fichiers où la collection (et en particulier ses fichiers d'index) se trouve. Le champ cache retient les résultats d'une requête. Il sert à accélérer des requêtes ultérieures qui répéteraient la même requête (et ses paramètres). Il peut sembler improbable qu'on effectue des requêtes identiques, mais dans la pratique c'est un phénomène fréquent. Le protocole de Greenstone est sans état. Pour générer une page de résultats semblable à la figure <imgref figure_searching_gutenberg_for_darcy> mais en présentant les résultats 11-20 de la même requête, la recherche est à nouveau transmise, en spécifiant cette fois-ci qu'il faut renvoyer les documents 11-20. L'utilisation de tampon rend ceci efficace, car le fait que cette recherche a déjà eu lieu est détecté et les résultats sont obtenus directement depuis le tampon.

Ces deux champs de données s'appliquent à tout objet hérité qui implémente un mécanisme de recherche. C'est pourquoi ils apparaissent dans la classe de base, et ils sont déclarés dans une section protégée de la classe de manière que les classes héritées puissent y accéder directement.

Recherche et rapatriement avec MG

Greenstone utilise MG (abréviation de «Managing Gigabytes» – gérer les giga-octets; voir Wittenet al., 1999) pour indexer et rapatrier les documents, et son code source est inclus dans le répertoire GSDLHOME/packages. MG utilise des techniques de compression pour optimiser l'utilisation de l'espace disque sans impacter la vitesse d'exécution. Pour une collection de documents écrits en anglais, le texte compacté et les index de corps du texte occupent en tout, en moyenne, un tiers de l'espace disque requis par le texte original, non compacté. La recherche et le rapatriement sont souvent plus rapides que les opérations équivalentes sur la version non compactée car elles impliquent moins d'opérations sur le disque dur.

<imgcaption figure_api_for_direct_access_to_mg|%!– id:876 –%API pour accéder directement à MG (abrégée) ></imgcaption>

enum result_kinds {
   result_docs,           // Return the documents found in last search
   result_docnums,     // Return document id numbers and weights
   result_termfreqs, // Return terms and frequencies
   result_terms           // Return matching query terms
};
int mgq_ask(char *line);
int mgq_results(enum result_kinds kind, int skip, int howmany,
                               int (*sender)(char *, int, int, float, void *),
                               void *ptr);
int mgq_numdocs(void);
int mgq_numterms(void);
int mgq_equivterms(unsigned char *wordstem,
                           int (*sender)(char *, int, int, float, void *),
                           void *ptr);
int mgq_docsretrieved (int *total_retrieved, int *is_approx);
int mgq_getmaxstemlen ();
void mgq_stemword (unsigned char *word);

L'utilisation normale de MG se fait interactivement, en tapant des commandes à l'invite, et une manière d'implémenter la classe mgsearchclass serait d'utiliser l'appel system() de la bibliothèque C au sein de l'objet pour exécuter les commandes MG appropriées. Mais une approche plus efficace consiste à taper directement dans le code de MG avec des appels de fonctions. Bien que ceci nécessite une meilleure compréhension du code de MG, on peut masquer une grande partie de la complexité derrière une nouvelle API qui deviendra le point de contact pour l'objet mgsearchclass. C'est le rôle du fichier colserver/mgq.c, dont l'API est présentée figure <imgref figure_api_for_direct_access_to_mg>.

On fournit des paramètres à MG à travers la fonction mgq_ask() (demande mgq), qui prend des options de texte dans un format identique à celui utilisé à la ligne de commande, tel que:

mgq_ask( ".set casefold off ");

Il sert aussi à invoquer une requête. On accède à ses résultats à travers la fonction mgq_results (résultats de mgq), qui prend un pointeur vers une fonction comme quatrième paramètre. Ceci fournit une manière souple de convertir l'information renvoyée dans les structures de données de MG en le format requis par mgsearchclass. Des appels tels que mgq_numdocs() (mgq-numéros de documents), mgq_numterms() et mgq_docsretrieved() (respectivement, mgq-numéros de termes et mgq-documents rapatriés) renvoient également des informations, qui sont cette fois plus précisément prescrites. Les deux dernières fonctions gèrent la troncature.

L'objet source

<imgcaption figure_source_base_class_api|%!– id:881 –%API de la classe de base source ></imgcaption>

class sourceclass {
public:
   sourceclass ();
   virtual ~sourceclass ();
   // configure should be called once for each configuration line
   virtual void configure (const text_t &key, const text_tarray &cfgline);
   // init should be called after all the configuration is done but
   // before any other methods are called
   virtual bool init (ostream &logout);
   // translate_OID translates OIDs using " .pr " , . " fc " etc.
   virtual bool translate_OID (const text_t &OIDin, text_t &OIDout, comerror_t &err, ostream &logout);
   // get_metadata fills out the metadata if possible, if it is not
   // responsible for the given OID then it return s false.
   virtual bool get_metadata (const text_t &requestParams, const text_t &refParams,
                           bool getParents, const text_tset &fields, const text_t &OID,
                           MetadataInfo_tmap &metadata, comerror_t &err, ostream &logout);
   virtual bool get_document (const text_t &OID, text_t &doc, 
                           comerror_t &err, ostream &logout);
};

Le rôle de l'objet source dans la figure <imgref figure_greenstone_runtime_system> est d'accéder aux méta-données et au texte du document, et l'API de sa classe de base est présentée figure <imgref figure_source_base_class_api>. Une fonction membre associe à chaque tâche les fonctions get_metadata() (obtient les méta-données) et get_document() (obtient le document) , respectivement. Toutes deux sont déclarées virtual, la version fournie par une implémentation particulière de la classe de base n'est donc appelée qu'au moment de l'exécution. Une version héritée de cet objet utilise GDBM pour implémenter get_metadata() et MG pour implémenter get_document(): nous détaillons cette version ci-dessous.

On trouve d'autres fonctions membre dans la figure <imgref figure_source_base_class_api>, telles que configure(), init(), et translate_OID() (traduire l'identifiant d'objet). Les deux premières sont liées au processus d'initialisation décrit section initialisation.

La dernière, translate_OID(), gère la syntaxe pour exprimer les identifiants de documents. Nous avons vu figure <imgref figure_the_golf_course_mystery> comment on pouvait ajouter un numéro de page à un identifiant de document pour ne rapatrier que la page correspondante. Ceci est possible car les pages ont été stockées en tant que «sections» lors de la construction de la collection. Ajouter «.1» à un OID rapatrie la première section du document correspondant. Les sections peuvent être imbriquées; on y accède alors on concaténant les numéros de sections et en séparant les numéros par des points.

En plus des numéros hiérarchiques de sections, la syntaxe de l'identifiant de document permet des accès relatifs. Par rapport à la section actuelle du document il est possible d'accéder aupremier fils(first child) en ajoutant .fc, audernier fils(last child) en ajoutant .lc, auparenten ajoutant .pr, aufrère suivant(next sibling) en ajoutant .ns, et aufrère précédent(previous sibling) en ajoutant .ps.

La fonction translate_OID() utilise les paramètres OIDin (OID en entrée) et OIDout (OID en sortie) pour renfermer la source et le résultat de la conversion. Elle prend aussi deux autres paramètres, err (erreur) et logout (journal en sortie). Ils communiquent tout état d'erreur qui peut se produire lors de l'opération de traduction, et déterminent où envoyer les informations de journalisation. Ces paramètres dépendent étroitement du protocole, comme nous le verrons dans la section protocol.

Rapatriement de base de données avec gdbm

GDBM est le gestionnaire de bases de données de GNU (www.gnu.org). Il implémente une structure d'enregistrements plats de couples clef/données, et il est compatible en arrière avec DBM et NDBM. Les opérations fournies comprennent le stockage, le rapatriement, et la destruction d'enregistrements par clef, et un parcours non ordonné de toutes les clefs.

<imgcaption figure_gdbm_database_for_the_gutenberg_collection|%!– id:889 –%Base de données GDBM pour la collection (extrait) ></imgcaption>

[HASH01d7b30d4827b51282919e9b]
<doctype>         doc
<hastxt>           0
<Title>             The Winter's Tale
<Creator>         William Shakespeare
<archivedir>   HASH01d7/b30d4827.dir
<thistype>       Invisible
<childtype>     Paged
<contains>        " .1; " .2; " .3; " .4; " .5; " .6; " .7; " .8; " .9; " .10; " .11; " .12;            \ <br/>                           " .13; " .14; " .15; " .16; " .17; " .18; " .19; " .20; " .21; " .22;            \ <br/>                           " .23; " .24; " .25; " .26; " .27; " .28; " .29; " .30; " .31; " .32;            \ <br/>                           " .33; " .34; " .35
<docnum>           168483
———————————————————————-
[CL1]
<doctype>         classify
<hastxt>           0
<childtype>     HList
<Title>             Title
<numleafdocs> 1818
<thistype>       Invisible
<contains>       " .1; " .2; " .3; " .4; " .5; " .6; " .7; " .8; " .9; " .10; " .11; " .12;          \ <br/>                           " .13; " .14; " .15; " .16; " .17; " .18; " .19; " .20; " .21; " .22;          \ <br/>                            " .23; " .24
———————————————————————-
[CL1.1]
<doctype>         classify
<hastxt>           0
<childtype>     VList
<Title>             A
<numleafdocs> 118
<contains>      HASH0130bc5f9f90089b3723431f;HASH9cba43bacdab5263c98545;\
                          HASH12c88a01da6e8379df86a7;HASH9c86579a83e1a2e4cf9736;   \
                           HASHdc2951a7ada1f36a6c3aca;HASHea4dda6bbc7cdeb4abfdee;   \
                          HASHce55006513c47235ac38ba;HASH012a33acaa077c0e612b9351;\
                          HASH010dd1e923a123826ae30e4b;HASHaf674616785679fed4b7ee;\
                         HASH0147eef4b9d1cb135e096619;HASHe69b9dbaa83ffb045d963b;\
                         HASH01abc61c646c8e7a8ce88b10;HASH5f9cd13678e21820e32f3a;\
                         HASHe8cbba1594c72c98f9aa1b;HASH01292a2b7b6b60dec96298bc;\
                         ...

La figure <imgref figure_gdbm_database_for_the_gutenberg_collection> montre un extrait de la base de données d'informations de la collection créée lors de la construction de la collection Gutenberg. Cet extrait fut produit à l'aide de l'outil Greenstone db2txt, qui convertit le format binaire de base de données GDBM sous forme textuelle. La figure <imgref figure_gdbm_database_for_the_gutenberg_collection> contient trois enregistrements, séparés par des lignes horizontales. Le premier est une entrée de document, les deux autres sont des portions de la hiérarchie créées par le classificateur AZList pour les titres de la collection. La première ligne de chaque enregistrement est sa clef.

L'enregistrement de document stocke le titre du livre, son auteur, et toute autre méta-donnée fournie (ou extraite) lors de la construction de la collection. Il contient aussi des valeurs pour usage interne: où les fichiers associés à ce document se trouvent (<archivedir>) et le numéro de document utilisé de manière interne par MG (<docnum>).

Le champ <contains> (contient) stocke une liste d'éléments, séparés par des points-virgules, qui pointent vers des enregistrement relatifs dans la base de données. Pour un enregistrement de document, <contains> sert à pointer vers les sections imbriquées. On formera des clefs d'enregistrement en concaténant la clef courante à l'un des éléments fils (le séparateur étant un point).

Le deuxième enregistrement de la figure <imgref figure_gdbm_database_for_the_gutenberg_collection> est le noeud principal de la hiérarchie de classification de titres A-Z. Ses enfants, auxquels on accède à travers le champ <contains>, comprennent CL1.1, CL1.2, CL1.3 etc., et correspondent aux pages individuelles pour les lettres A, B, C, etc. Il n'en existe que 24: le classificateur AZList a rassemblé les lettres Q-R et Y-Z car elles ne concernaient que peu de titres.

Les enfants du champ <contains> du troisième enregistrement, CL1.1, sont les documents proprement dits. On peut trouver des structures plus compliquées – le champ <contains> peut inclure un mélange de documents et d'autres noeuds CL. Les clefs exprimées relativement à la clef courante se reconnaissent des clefs absolues car elles commencent par une apostrophe double. (").

Utiliser MG et GDBM pour implémenter un objet source

<imgcaption figure_api_for_mg_and_gdbm_based_version_of_sourceclass|%!– id:896 –%API de la version de sourceclass basée sur MG et GDBM (abrégée) ></imgcaption>

class mggdbmsourceclass : public sourceclass {
protected:
   // Omitted, data fields that store:
   //     collection specific file information
   //     index substructure
   //     information about parent
   //     pointers to gdbm and mgsearch objects
public:
   mggdbmsourceclass ();
   virtual ~mggdbmsourceclass ();
   void set_gdbmptr (gdbmclass *thegdbmptr);
   void set_mgsearchptr (searchclass *themgsearchptr);
   void configure (const text_t &key, const text_tarray &cfgline);
   bool init (ostream &logout);
   bool translate_OID (const text_t &OIDin, text_t &OIDout,
                                           comerror_t &err, ostream &logout);
   bool get_metadata (const text_t &requestParams,
                                           const text_t &refParams,
                                         bool getParents, const text_tset &fields,
                                         const text_t &OID, MetadataInfo_tmap &metadata,
                                         comerror_t &err, ostream &logout);
   bool get_document (const text_t &OID, text_t &doc,
                                         comerror_t &err, ostream &logout);
};

L'objet qui rassemble MG et GDBM pour réaliser une implémentation de sourceclass s'appelle mggdbmsourceclass. La figure <imgref figure_api_for_mg_and_gdbm_based_version_of_sourceclass> présente son API. Les deux fonctions membre set_gdbmptr() et set_mgsearchptr() (respectivement, positionne le pointeur gdbm et recherche le pointeur mg) stockent des pointeurs vers leurs objets respectifs, de sorte que les implémentations de get_metadata() (obtient les méta-données) et get_document() (obtient le document) puissent accéder aux outils appropriés pour mener leur tâche à bien.

L'objet de filtre

<imgcaption figure_api_for_the_filter_base_class|%!– id:899 –%API de la classe de base de filtre ></imgcaption>

class filterclass {
protected:
   text_t gsdlhome;
   text_t collection;
   text_t collectdir;
   FilterOption_tmap filterOptions;
public:
   filterclass ();
   virtual ~filterclass ();
   virtual void configure (const text_t &key, const text_tarray &cfgline);
   virtual bool init (ostream &logout);
   // returns the name of this filter
   virtual text_t get_filter_name ();
   // returns the current filter options
   virtual void get_filteroptions (InfoFilterOptionsResponse_t &response,
                                 comerror_t &err, ostream &logout);
   virtual void filter (const FilterRequest_t &request,
                         FilterResponse_t &response,
                         comerror_t &err, ostream &logout);
};

L'API de la classe de base de l'objet de filtre de la figure <imgref figure_greenstone_runtime_system> est présentée dans la figure <imgref figure_api_for_the_filter_base_class>. Elle commence par les champs de données protégés gsdlhome, collection, et collectdir, fréquents dans les classes qui ont besoin d'accéder à des fichiers propres à des collections.

mggdbsourceclass est une autre classe qui inclut ces trois champs de données.

Les fonctions membre configure() et init() (d'abord vues dans la fonction sourceclass) sont utilisées par le processus d'initialisation. L'objet lui-même est intimement aligné avec la portion de filtre lui correspondant dans le protocole; en particulier les fonctions get_filteroptions() et filter() correspondent exactement.

<imgcaption figure_how_a_filter_option_is_stored|%!– id:906 –%Manière de stocker une option de filtre ></imgcaption>

struct FilterOption_t {
   void clear (); \   void check_defaultValue ();
   FilterOption_t () {clear();}
   text_t name;
   enum type_t {booleant=0, integert=1, enumeratedt=2, stringt=3};
   type_t type;
   enum repeatable_t {onePerQuery=0, onePerTerm=1, nPerTerm=2};
   repeatable_t repeatable;
   text_t defaultValue;
   text_tarray validValues;
};
struct OptionValue_t {
   void clear ();
   text_t name;
   text_t value;
};

Les deux classes présentées dans la figure <imgref figure_how_a_filter_option_is_stored> sont centrales pour les options de filtre. FilterOption_t (filtre d'option) stocke le nom de l'option, son type, et si elle peut ou non être répétée (repeatable). validValues est interprété selon le type de l'option. Pour un type booléen, la première valeur est la valeur fausse false, la deuxième valeur est la valeur vraie true. Pour un type entier, la première valeur est la valeur minimale, la deuxième valeur est la valeur maximale. Pour un type énuméré toutes les valeurs sont listées. Pour un type de chaîne la valeur est ignorée. Pour des situations plus simples, on utilise OptionValue_t (valeur d'option), qui enregistre sous forme de text_t le nom de l'option et sa valeur.

Les objets de requête et de réponse passés en paramètres à filterclass sont construits à partir de ces deux classes, en utilisant des tableaux associatifs pour stocker un ensemble d'options telles que les options nécessaires à la fonction InfoFilterOptionsResponse_t. On peut trouver des détails complémentaires dans le fichier GSDLHOME/src/recpt/comtypes.h.

Objets de filtre hérités

<imgcaption figure_inheritance_hierarchy_for_filter|%!– id:910 –%Hiérarchie d'héritage pour l'objet de filtre ></imgcaption>

Les filtres utilisent deux niveaux d'héritage, comme illustré dans la figure <imgref figure_inheritance_hierarchy_for_filter>. Tout d'abord on distingue les filtres de requête et de navigation, et il existe pour les premiers une implémentation spécifique, fondée sur MG. Pour fonctionner correctement, mgqueryfilterclass doit accéder à MG à travers mgsearchclass et à GDBM à travers gdbmclass. browsefilterclass n'a besoin que d'accéder à GDBM. Les pointeurs vers ces objets sont stockés sous la forme de champs de données protégés au sein des classes respectives.

Code du serveur de collection

Voici les fichiers d'en-têtes du répertoire GSDLHOME/src/colservr, suivis de leur description. Le nom de fichier correspond généralement au nom de l'objet qu'il renferme.

<tblcaption table_table_1|##HIDDEN##></tblcaption>

< - 120 420 >
browsefilter.h (filtre de navigation) Hérité de filterclass (classe de filtre), cet objet fournit un accès à GDBM (décrit plus haut).
collectserver.h (serveur de collection) Cet objet relie les objets de filtre et source d'une collection, pour former l'objet de collection représenté figure <imgref figure_greenstone_runtime_system>.
colservrconfig.h (configuration du serveur de collection) Support fonctionnel pour la lecture des fichiers propres à une collection etc/collect.cfg et index/build.cfg. Le premier est le fichier de configuration de la collection; le deuxième est un fichier généré par le processus de construction qui enregistre le moment de la dernière construction menée à bien, une liste des correspondances d'index, combien de documents ont été indexés, et leur taille (non compactée) en octets.
filter.h (filtre) La classe de base de l'objet filtre filterclass décrit ci-dessus.
maptools.h Définit une classe stringmap (correspondance de chaîne) qui fournit une correspondance se rappelant l'ordre original d'une correspondance text_t, mais qui est d'une consultation rapide. mggdbmsourceclass ainsi que queryfilterclass s'en servent.
mggdbmsource.h Hérité de sourceclass, cet objet donne accès à MG et GDBM (décrit plus haut).
mgppqueryfilter.h Hérité de queryfilterclass, cet objet fournit une implémentation de QueryFilter fondée sur MG++, version améliorée de MG écrite en C++. Remarquez que Greenstone est par défaut configuré pour utiliser MG, puisque MG++ est toujours en cours de développement.
mgppsearch.h Hérité de searchclass, cet objet fournit une implémentation de l'objet de recherche avec MG++. Comme mgppqueryfilterclass, ceci n'est pas utilisé par défaut.
mgq.h Interface fonctionnelle au paquetage MG. Ses fonctions principales sont mg_ask() et mg_results().
mgqueryfilter.h Hérité de queryfilterclass, cet objet fournit une implémentation de QueryFilter fondée sur MG.
mgsearch.h Hérité de searchclass, cet objet fournit une implémentation de l'objet de recherche qui utilise MG (décrit plus haut).
phrasequeryfilter.h Hérité de mgqueryclass, cet objet fournit une classe de requête fondée sur les groupes de mots. Il n'est pas utilisé dans l'installation par défaut. C'est mgqueryfilterclass qui fournit cette fonctionnalité à travers un support fonctionnel depuis phrasesearch.h.
phrasesearch.h Support fonctionnel pour rechercher des groupes de mots comme une opération post-traitement.
querycache.h Utilisé par searchclass et les classes qui en héritent pour garder dans un tampon les résultats d'une requête, afin de générer plus efficacement la génération des pages de résultats suivantes (décrit plus haut).
queryfilter.h Hérité de la classe de base de filtre filterclass, cet objet établit une classe de base pour les objets de filtre de requête (décrit plus haut).
queryinfo.h Support pour la recherche: structures de données et objets pour contenir les paramètres des requêtes, les documents résultats et les fréquences des termes.
search.h La classe de base de l'objet de recherche searchclass (décrite plus haut).
source.h La classe de base de l'objet source sourceclass (décrite plus haut).

Le protocole

<tblcaption table_list_of_protocol_calls|Liste des appels de protocole></tblcaption>

< - 132 397 >
get_protocol_name() Renvoie le nom de ce protocole. Les valeurs possibles comprennentnullproto(protocole vide),corbaproto(protocole CORBA), etz3950proto. Utilisé par des portions du système d'exécution qui dépendent du protocole pour décider quel code exécuter.
get_collection_list() Renvoie la liste des collections que ce protocole connaît.
has_collection() Renvoie la valeur vraietruesi le protocole peut communiquer avec la collection nommée, c'est-à-dire si cette collection fait partie de sa liste de collections.
ping() Renvoie la valeur vraietruesi on a pu établir une connexion à la collection nommée. Dans le cas du protocole vide l'implémentation est la même que celle dehas_collection().
get_collectinfo() Obtient des informations génériques relatives à la collection nommée: date de dernière construction, nombre de documents qu'elle contient, etc. Inclut aussi des méta-données du fichier de configuration de la collection: le texte «\menu{à propos}»; l'icone de collection à utiliser, etc.
get_filterinfo() Obtient une liste de tous les objets de filtre pour la collection nommée.
get_filteroptions() Obtient toutes les options pour un objet de filtre particulier au sein de la collection nommée.
filter() Permet la recherche et la navigation. Pour un type de filtre donné et des réglages d'options, accède au contenu de la collection nommée pour produire un ensemble résultat qui est filtré selon les réglages d'options. Les champs de données renvoyés dépendent également des réglages d'options, comme par exemple la fréquence des termes de requête et les méta-données de document.
get_document() Obtient un document ou une section d'un document.

La table <tblref table_list_of_protocol_calls> liste les appels de fonction au protocole, en résumant chaque entrée. Les exemples donnés section how_the_conceptual_framework_fits_together ont couvert la plupart de ces appels. Les fonctions non mentionnées précédemment sont les fonctions has_collection(), ping(), get_protocol_name() et get_filteroptions(). Les deux premières répondent par oui ou par non aux questions «la collection existe-t-elle sur ce serveur?» et «est-elle en cours de fonctionnement?», respectivement. Le but des deux autres est de supporter des protocoles multiples au sein d'une architecture distribuée sur des ordinateurs différents, et non pas le simple exécutable fondé sur le protocole vide et décrit ici. L'une d'entre elles fait une distinction selon le protocole en cours d'utilisation. L'autre permet à un réceptionniste d'interroger un serveur de collection pour trouver les options supportées, afin de s'auto-configurer dynamiquement pour profiter des services offerts par un serveur particulier.

<imgcaption figure_null_protocol_api|%!– id:971 –%API du protocole vide (abrégée) ></imgcaption>

class nullproto : public recptproto {
public:
   virtual text_t get_protocol_name ();
   virtual void get_collection_list (text_tarray &collist,
                comerror_t &err, ostream &logout);
   virtual void has_collection (const text_t &collection,
                bool &hascollection,
                comerror_t &err, ostream &logout);
   virtual void ping (const text_t &collection,
                  bool &wassuccess,
                  comerror_t &err, ostream &logout);
   virtual void get_collectinfo (const text_t &collection,
                  ColInfoResponse_t &collectinfo,
                  comerror_t &err, ostream &logout);
   virtual void get_filterinfo (const text_t &collection,
                   InfoFiltersResponse_t &response,
                   comerror_t &err, ostream &logout);
   virtual void get_filteroptions (const text_t &collection,
                   const InfoFilterOptionsRequest_t &request,
                   InfoFilterOptionsResponse_t &response,
                   comerror_t &err, ostream &logout);
   virtual void filter (const text_t &collection,
                   FilterRequest_t &request,
                   FilterResponse_t &response,
                   comerror_t &err, ostream &logout);
   virtual void get_document (const text_t &collection,
                  const DocumentRequest_t &request,
                  DocumentResponse_t &response,
                  comerror_t &err, ostream &logout);
};

La figure <imgref figure_null_protocol_api> montre l'API pour le protocole vide. Les commentaires, ainsi que certains détails de bas niveau, ont été omis (consultez le fichier source, c'est-à-dire recpt/nullproto.h, pour avoir tous les détails).

Ce protocole hérite de la classe de base recptproto. On utilise l'héritage virtuel de telle sorte que plus d'un type de protocole – y compris des protocoles pas encore conçus – puisse être facilement supporté dans le reste du code source. Ceci est possible car l'objet de classe de base recptproto est utilisé tout au long du code source, à l'exception de l'endroit de la construction. Nous spécifions ici la véritable variété de protocole que nous souhaitons utiliser – dans ce cas-ci, le protocole vide.

À l'exception de get_protocol_name(), qui ne prend aucun paramètre et renvoie le nom du protocole sous la forme d'une chaîne au format Unicode, toutes les fonctions du protocole incluent un paramètre d'erreur et un flux de sortie en tant que leurs deux derniers arguments. Le paramètre d'erreur enregistre toute erreur qui se produit pendant l'exécution de l'appel de protocole, et le flux de sortie est utilisé à des fins de journalisation. Les fonctions sont de type void–elles ne renvoient pas explicitement des informations dans leur dernière instruction, mais renvoient plutôt des données à travers des paramètres désignés tels que le paramètre d'erreur (dont on vient de parler). Dans certains langages de programmation, de tels sous-programmes s'appelleraient «procédures» et non «fonctions», mais le C++ n'opère pas de telle distinction syntaxique.

La plupart des fonctions prennent le nom de la collection en argument. Trois des fonctions membre (les fonctions get_filteroptions(), filter(), et get_document()) suivent le schéma de fournir un paramètre de requête et de recevoir les résultats dans un paramètre de réponse.

Le réceptionniste

C'est la dernière couche du modèle conceptuel. Après analyse des arguments du CGI, l'activité principale est l'exécution d'une action, assistée par les objets de mise en forme et de langage de macros. Ces derniers sont décrits plus bas. Bien qu'ils soient représentés comme des objets dans le cadre conceptuel, les objets de mise en forme et de langage de macros ne sont pas strictement des objets au sens du C++. En fait, l'objet de mise en forme est une collection de structures de données et un ensemble de fonction qui opère sur ces dernières, et l'objet de langage de macros est construit autour de displayclass, définie dans lib/display.h, avec un support de conversion de flux provenant de lib/gsdlunicode.h.

Les actions

<tblcaption table_actions_in_greenstone|Les actions dans Greenstone></tblcaption>

< - 132 397 >
action Classe de base pour l'héritage virtuel.
authenaction Support de l'authentification utilisateur: demande à l'utilisateur un mot de passe si aucun n'a été saisi; vérifie sa validité; et oblige l'utilisateur à se ré-authentifier si trop de temps s'est écoulé entre deux accès.
collectoraction Génère les pages pour le Collector.
documentaction Rapatrie les documents, les sections de documents, les portions de la hiérarchie de classification, ou les informations de mise en forme.
extlinkaction Emmène un utilisateur directement vers une URL externe à la collection, en générant ou non d'abord une page d'alerte (selon lesPréférences).
pageaction Génère une page en conjonction avec le langage de macros.
pingaction Vérifie si une collection est en ligne ou non.
queryaction Effectue une recherche.
statusaction Génère les pages d'administration.
tipaction Affiche une astuce aléatoire à l'utilisateur.
usersaction Gère l'ajout, la destruction, et la gestion des accès utilisateur.

Greenstone supporte les onze actions résumées dans la table <tblref table_actions_in_greenstone>.

<imgcaption figure_using_the_cgiargsinfoclass_from_pageactioncpp|%!– id:1003 –%Utilisation de cgiargsinfoclass depuis pageaction.cpp %!– withLineNumber –%></imgcaption>

cgiarginfo arg_ainfo;
arg_ainfo.shortname = " a " ;
arg_ainfo.longname = " action" ;
arg_ainfo.multiplechar = true;
arg_ainfo.argdefault = " p" ;
arg_ainfo.defaultstatus = cgiarginfo::weak;
arg_ainfo.savedarginfo = cgiarginfo::must;
argsinfo.addarginfo (NULL, arg_ainfo);
 
arg_ainfo.shortname = " p" ;
arg_ainfo.longname = " page" ;
arg_ainfo.multiplechar = true;
arg_ainfo.argdefault = " home" ;
arg_ainfo.defaultstatus = cgiarginfo::weak;
arg_ainfo.savedarginfo = cgiarginfo::must;
argsinfo.addarginfo (NULL, arg_ainfo);

Les arguments CGI nécessaires à une action sont formellement déclarés dans sa fonction constructeur avec cgiarginfo (définie dans recpt/cgiargs.h). La figure <imgref figure_using_the_cgiargsinfoclass_from_pageactioncpp> montre un extrait de la fonction constructeur pageaction, qui définit la taille et les propriétés des arguments CGI a et p.

Pour chaque argument CGI, le constructeur doit spécifier son nom court (lignes 2 et 10), qui est le nom de la variable CGI à proprement parler; un nom long (lignes 3 et 11) qui est utilisé pour fournir une description plus parlante de l'action; s'il représente une valeur de caractère simple ou multiple (lignes 4 et 12); possiblement, une valeur par défaut (lignes 5 et 13); ce qui se produit quand plusieurs valeurs par défaut sont fournies (lignes 6 et 14, étant donné qu'on peut positionner des valeurs par défaut dans les fichiers de configuration); et s'il faut ou non que la valeur soit conservée à la fin de cette action (lignes 7 et 15).

Étant donné qu'elle est embarquée dans le code, on peut générer automatiquement des pages web qui détaillent cette information. L'action statusaction produit cette information, et on peut la visualiser en saisissant l'URL de la page d'administration de Greenstone.

Les douze actions héritées sont construites dans main(), la fonction de premier niveau de l'exécutable library, dont la définition est donnée dans le fichier recpt/librarymain.cpp. C'est aussi là que l'objet receptionist (défini dans recpt/receptionist.cpp) est construit. La responsabilité de toutes les actions est passée à l'objet receptionist, qui les traite en maintenant, sous la forme d'un champ de données, un tableau associatif de la classe de base d'action, indexé par les noms d'actions.

<imgcaption figure_action_base_class_api|%!– id:1008 –%API de la classe de base d'action ></imgcaption>

class action {
protected:
   cgiargsinfoclass argsinfo;
   text_t gsdlhome;
public:
   action ();
   virtual ~action ();
   virtual void configure (const text_t &key, const text_tarray &cfgline);
   virtual bool init (ostream &logout);
   virtual text_t get_action_name ();
   cgiargsinfoclass getargsinfo ();
   virtual bool check_cgiargs (cgiargsinfoclass &argsinfo,
              cgiargsclass &args, ostream &logout);
   virtual bool check_external_cgiargs (cgiargsinfoclass &argsinfo,
              cgiargsclass &args,
              outconvertclass &outconvert,
              const text_t &saveconf,
              ostream &logout);
   virtual void get_cgihead_info (cgiargsclass &args,
              recptprotolistclass *protos,
              response_t &response,
              text_t &response_data,
              ostream &logout);
   virtual bool uses_display (cgiargsclass &args);
   virtual void define_internal_macros (displayclass &disp,
              cgiargsclass &args,
              recptprotolistclass *protos,
              ostream &logout);
   virtual void define_external_macros (displayclass &disp,
              cgiargsclass &args,
              recptprotolistclass *protos,
              ostream &logout);
   virtual bool do_action (cgiargsclass &args,
              recptprotolistclass *protos,
              browsermapclass *browsers,
              displayclass &disp,
              outconvertclass &outconvert,
              ostream &textout,
              ostream &logout);
};

La figure <imgref figure_action_base_class_api> montre l'API de la classe de base d'action. Lors de l'exécution d'une action, l'objet receptionist appelle plusieurs fonctions, en commençant par check_cgiargs(). La plupart l'aident à vérifier, positionner, et définir des valeurs et des macros, alors que la fonction do_action() engendre pour de bon la page produite. Si un objet hérité particulier ne dispose d'aucune définition pour une fonction membre donnée, il passe à la définition de la classe de base qui implémente un comportement par défaut approprié.

Voici des explications des fonctions membre:

Au début de la définition de classe, argsinfo est le champ de données protégé (utilisé dans l'extrait de code représenté dans la figure <imgref figure_using_the_cgiargsinfoclass_from_pageactioncpp>) qui stoke les informations d'argument CGI spécifiées dans une fonction constructeur d'action héritée. L'autre champ de données, gsdlhome, enregistre GSDLHOME pour pouvoir y accéder plus facilement1). L'objet inclut également les fonctions configure() et init() à des fins d'initialisation.

Mise en forme

<imgcaption figure_core_data_structures_in_format|%!– id:1021 –%Structures de données centrales de la classe de mise en forme ></imgcaption>

enum command_t {comIf, comOr, comMeta, comText, comLink, comEndLink,
         comNum, comIcon, comDoc,
         comHighlight, comEndHighlight};
enum pcommand_t {pNone, pImmediate, pTop, pAll};
enum dcommand_t {dMeta, dText};
enum mcommand_t {mNone, mCgiSafe};
struct metadata_t {
   void clear();
   metadata_t () {clear();}
   text_t metaname;
   mcommand_t metacommand;
   pcommand_t parentcommand;
   text_t parentoptions;
};
// The decision component of an {If}{decision,true-text,false-text}
// formatstring. The decision can be based on metadata or on text;
// normally that text would be a macro like
// _cgiargmode_.
struct decision_t {
   void clear();
   decision_t () {clear();}
   dcommand_t command;
   metadata_t meta;
   text_t text;
};
struct format_t {
   void clear();
   format_t () {clear();}
   command_t command;
   decision_t decision;
   text_t text;
   metadata_t meta;
   format_t *nextptr;
   format_t *ifptr;
   format_t *elseptr;
   format_t *orptr;
};

Bien que la mise en forme soit représentée comme une seule entité dans la figure <imgref figure_greenstone_runtime_system>, elle constitue en réalité une collection de structures de données et de fonctions, rassemblées dans le fichier d'en-têtes recpt/formattools.h. Les structures de données centrales de la classe sont représentées figure <imgref figure_core_data_structures_in_format>.

<imgcaption figure_data_structures_built_for_sample_format_statement|%!– id:1023 –%Structures de données construites pour l'exemple d'instruction de mise en forme ></imgcaption>

L'implémentation est plus facile à expliquer avec un exemple. Quand l'instruction de mise en forme

format CL1Vlist
"[link][Title]{If}{[Creator], par [Creator]}[/link]"

est lue dans un fichier de configuration de collection, elle est analysée par les fonctions de formattools.cpp et la structure de données interconnectée montrée figure <imgref figure_data_structures_built_for_sample_format_statement> est construite. Quand il faut faire évaluer l'instruction de mise en forme par une action, la structure de données est traversée. Le chemin pris au niveau des noeuds comIf et comOr dépend des méta-données renvoyées par un appel au protocole.

Il se peut que lors du rapatriement de méta-données, le résultat contienne d'autres macros et syntaxes de mise en forme. Ceci est géré par des allers-retours entre l'analyse et l'évaluation, en fonction des besoins.

Le langage de macros

L'entité de langage de macros de la figure <imgref figure_greenstone_runtime_system>, tout comme celle de mise en forme, ne correspond pas à une unique classe C++. Dans ce cas-ci il existe une classe centrale, mais l'implémentation du langage de macros dépend aussi de fonctions et de classes de support.

Ici encore, on expliquera mieux l'implémentation en utilisant un exemple. Donnons d'abord quelques définitions d'échantillons de macros qui illustrent les priorités entre macros, puis – à l'aide d'un diagramme – nous décrirons les structures de données de base construites pour supporter cette activité. Enfin, nous présenterons et décrirons les fonctions membre publiques de displayclass, l'objet de macro de premier niveau.

<imgcaption figure_illustration_of_macro_precedence|%!– id:1031 –%Illustration de la priorité entre les macros ></imgcaption>

package query
_header_ []         {_querytitle_}
_header_ [l=en]     {Search page}
_header_ [c=demo]   {<table bgcolor=green><tr><td>_querytitle_</td></tr></table>}
_header_ [v=1]      {_textquery_}
_header_ [l=fr,v=1,c=hdl] {HDL Page de recherche}

Dans une collection Greenstone habituelle, la priorité entre les macros est normalement: c (pour la collection) passe avant v (pour une interface graphique ou textuelle), qui passe avant l (pour la langue). Ceci est mis en place par la ligne

macroprecedence c,v,l

dans le fichier de configuration principal main.cfg. Les instructions de macros de la figure <imgref figure_illustration_of_macro_precedence> définissent des exemples de macros pour l'en-tête _header_ du paquetage de requête pour diverses valeurs de c, v, et l. Si les arguments CGI donnés lors de l'invocation d'une action comprennent c=hdl, v=1, et l=en, la macro _header_[v=1] serait alors sélectionnée pour l'affichage. Elle serait sélectionnée avant _content_[l=en] car v a la priorité sur l. La macro _content_[l=fr,v=1,c=hdl] ne serait pas sélectionnée parce que le paramètre de page pour l est différent.

<imgcaption figure_data_structures_representing_the_default_macros|%!– id:1034 –%Structures de données représentant les macros par défaut ></imgcaption>

La figure <imgref figure_data_structures_representing_the_default_macros> montre la structure de données principale construite lors de la lecture des fichiers de macros spécifiés dans etc/main.cfg. Il s'agit essentiellement d'un tableau associatif de tableaux associatifs de tableaux associatifs. Le niveau le plus élevé (à gauche) indexe le paquetage dont provient la macro, et le deuxième niveau indexe le nom de la macro. Le dernier niveau indexe tous les paramètres spécifiés, en stockant chacun sous le type mvalue qui enregistre, en plus de la valeur de la macro, le fichier dont elle provient. Par exemple, on peut trouver le texte défini pour _header_[l=en] dans la figure <imgref figure_illustration_of_macro_precedence> stocké dans le plus bas des deux enregistrements mvalue dans la figure <imgref figure_data_structures_representing_the_default_macros>.

<imgcaption figure_displayclass_api|%!– id:1036 –%API de la classe d'affichage displayclass ></imgcaption>

class displayclass
{
public:
   displayclass ();
   ~displayclass ();
   int isdefaultmacro (text_t package, const text_t &macroname);
   int setdefaultmacro (text_t package, const text_t &macroname,  
         text_t params, const text_t &macrovalue);
   int loaddefaultmacros (text_t thisfilename);
   void openpage (const text_t &thispageparams,
         const text_t &thisprecedence);
   void setpageparams (text_t thispageparams,
         text_t thisprecedence);
   int setmacro (const text_t &macroname,
         text_t package,
         const text_t &macrovalue);
   void expandstring (const text_t &inputtext, text_t &outputtext);
   void expandstring (text_t package, const text_t &inputtext,
         text_t &outputtext, int recursiondepth = 0);
   void setconvertclass (outconvertclass *theoutc) {outc = theoutc;}
   outconvertclass *getconvertclass () {return outc;}
   ostream *setlogout (ostream *thelogout);
};

L'objet central de support du langage de macros est displayclass, défini dans lib/display.h. Ses fonctions membre publiques sont représentées figure <imgref figure_displayclass_api>. La classe lit les fichiers de macros spécifiés à l'aide de loaddefaultmacros(), en stockant dans une section protégée de la classe (non représentée), le type de la structure de données montrée figure <imgref figure_data_structures_representing_the_default_macros>. Il est également permis au système d'exécution de positionner des macros à l'aide de setmacro() (dans le dernier exemple de la section how_the_conceptual_framework_fits_together, pageaction positionne _homeextra_ comme étant la table des collections dynamiquement générée en utilisant cette fonction). Ceci est supporté par un ensemble de tableaux associatifs similaires à ceux utilisés pour représenter les fichiers de macros (ce n'est pas identique, car le premier n'a pas besoin d'une couche de «paramètres»). Dans displayclass, Les macros lues dans le fichier s'appellent lesmacros par défaut. Les macros locales spécifiées à travers setmacro() s'appellentmacros courantes, et sont effacées de la mémoire après la génération de la page.

Quand il faut produire une page, openpage() est d'abord appelée pour communiquer les réglages courants des paramètres de page (l=en etc.). Ensuite, le texte et les macros passent dans un flux à travers la classe – typiquement depuis une actionclass – en utilisant du code tel que:

cout << text_t2ascii << display << "_amacro_ "
         << "_anothermacro_ ";

Le résultat est que les macros sont développées selon les réglages des paramètres de page. Si nécessaire, ces réglages peuvent être modifiés en cours de procédure à l'aide d'une action en utilisant setpageparams(). Les fonctions membre publiques restantes fournissent le support de bas niveau.

Le code du réceptionniste

Nous avons maintenant décrit les objets principaux du réceptionniste. Nous détaillons ci-dessous les classes de support, situées dans GSDLHOME/src/recpt. À l'exception des cas où l'efficacité prime – les définitions sont alors inline – les détails d'implémentation sont contenus dans le fichier .cpp correspondant au fichier d'en-têtes. Les fichiers de support comportent souvent le mot tool (outil) dans leur nom, comme par exemple OIDtools.h et formattools.h.

Un autre ensemble de fichiers de portée lexicale comprend le préfixe z3950. Ces fichiers fournissent un accès distant aux bases de données et aux catalogues en ligne publiquement accessibles à travers le protocole Z39.50.

Le terme browserclass est inclus dans un autre groupe important de fichiers de support. Ces fichiers sont liés à travers une hiérarchie d'héritage virtuel. En tant que groupe, ils supportent une notion abstraite de navigation: génération de pages en série comportant des contenus de documents compartimentés ou des méta-données. Les possibilités des navigation comportent la lecture de documents triés par ordre alphabétique des titres ou par ordre chronologique des dates; l'exploration des résultats d'une requête par lot de dix entrées; et l'accès aux pages individuelles d'un livre en utilisant le mécanisme aller à la page…. Chaque possibilité de navigation hérite de la classe de base, browserclass:

Les actions accèdent aux objets browserclass à travers browsetools.h.

<tblcaption table_table_2|##HIDDEN##></tblcaption>

< - 140 390 >
OIDtools.h Support fonctionnel pour l'évaluation des identifiants de documents sur le protocole.
action.h Classe de base pour l'entité d'action décrite figure <imgref figure_greenstone_runtime_system>.
authenaction.h Action héritée pour la gestion de l'authentification d'un utilisateur.
browserclass.h Classe de base pour les activités de navigation abstraites.
browsetools.h Support fonctionnel pour l'accès à browserclass (hiérarchie). Les fonctionnalités comprennent le développement et la contraction de contenus, l'affichage d'une table des matières, et la génération de contrôles tels que le mécanisme aller à la page….
cgiargs.h Définit cgiarginfo, utilisé dans la figure <imgref figure_using_the_cgiargsinfoclass_from_pageactioncpp>, ainsi que le support d'autres structures de données pour les arguments CGI.
cgiutils.h Support fonctionnel pour les arguments CGI utilisant les structures de données définies dans le fichier cgiargs.h.
cgiwrapper.h Support fonctionnel qui fait tout ce qui est nécessaire pour produire une page en utilisant le protocole CGI. L'accès se fait à travers la fonction
void cgiwrapper (receptionist &recpt, text_t collection);
qui est la seule fonction déclarée dans le fichier d'en-têtes. Tout le reste du fichier .cpp correspondant a une portée lexicale locale au fichier (par l'utilisation du mot-clef C++ static). Si la fonction est employée pour une collection particulière alors il faudrait positionner collection, sinon cette chaîne doit être la chaîne vide "". Ce code comprend du support pour Fast-CGI.
collectoraction.h Action héritée qui facilite la construction de collections par l'utilisateur final à travers le Collector. La page générée provient de collect.dm et elle est contrôlée par l'argument CGI p=page.
comtypes.h Types centraux du protocole.
converter.h Support objet pour les convertisseurs de flux.
datelistbrowserclass.h Hérité de browserclass, cet objet fournit un support de navigation pour les listes chronologiques telles que celle qu'on peut voir dans la collection «Archives» de Greenstone sous Dates dans la barre de navigation.
documentaction.h Action héritée utilisée pour rapatrier une document ou une portion d'une hiérarchie de classification.
extlinkaction.h Action héritée qui contrôle si un utilisateur se rend directement à un lien externe, ou s'il doit passer par une page d'avertissement qui l'alerte du fait qu'il s'apprête à sortir du système de bibliothèque numérique.
formattools.h Support fonctionnel pour l'analyse et l'évaluation des instructions format de configuration de collection. Détaillé dans la section formatting, ci-dessus.
historydb.h Structures de données et support fonctionnel pour la gestion d'une base de données des requêtes précédentes de telle sorte qu'un utilisateur puisse faire une nouvelle requête en reprenant d'anciens termes.
hlistbrowserclass.h Hérité de browserclass, cet objet fournit le support de navigation pour les listes horizontales.
htmlbrowserclass.h Hérité de browserclass, cet objet fournit le support de navigation pour les pages HTML.
htmlgen.h Support fonctionnel pour mettre en valeur les termes de requête dans une chaîne text_t.
htmlutils.h Support fonctionnel qui convertit une chaîne text_t en le HTML équivalent. Les symboles ", &, <, et > sont respectivement convertis en &quot;, &amp;, &lt;, et &gt;.
infodbclass.h Définit deux classes: gdbmclass et infodbclass. La première fournit l'API de Greenstone pour GDBM; la deuxième est la classe objet utilisée pour stocker une entrée d'enregistrement lue dans une base de données GDBM, et c'est essentiellement un tableau associatif de tableaux de chaînes text_t indicées par des entiers.
invbrowserclass.h Hérité de browserclass, cet objet fournit le support de navigation pour les listes qui ne sont pas censées être affichées (invisibles).
nullproto.h Héritée de recptproto, cette classe réalise le protocole vide, implémenté par des appels de fonctions du réceptionniste vers le serveur de collection.
pageaction.h Action héritée qui, en conjonction avec le fichier de macros nommé dans l'argument p=page, génère une page web.
pagedbrowserclass.h Hérité de browserclass, cet objet fournit le support de navigation pour le mécanisme aller à la page…, vu par exemple dans la collection Gutenberg.
pingaction.h Action héritée qui vérifie si une collection particulière répond aux requêtes.
queryaction.h Action héritée qui prend la requête, les réglages et les préférences stipulés, et effectue une recherche, en générant comme résultat le sous-ensemble des documents o=num correspondants, en commençant à la position r=num.
querytools.h Support fonctionnel pour les requêtes.
receptionist.h Objet de premier niveau du réceptionniste. Maintient un enregistrement des informations d'arguments CGI, des instantiations de chaque action héritée, des instantiations de chaque objet de navigation, l'objet central de langage de macros displayclass, et tous les convertisseurs possibles.
recptconfig.h Support fonctionnel pour la lecture des fichiers de configuration principal et de site.
recptproto.h Classe de base pour le protocole.
statusaction.h Action héritée qui génère, en conjonction avec le fichier status.dm, les diverses pages d'administration.
tipaction.h Action héritée qui produit, en conjonction avec tip.dm, une page web contenant une astuce choisie au hasard parmi la liste d'astuces stockée dans le fichier tip.dm.
userdb.h Structure de données et support fonctionnel pour maintenir une base de données GDBM d'utilisateurs: leur mot de passe, leurs groupes, etc.
usersaction.h Une action d'administration héritée de la classe de base qui supporte l'ajout et la destruction d'utilisateurs, ainsi que la modification de la liste des groupes auxquels ils appartiennent.
vlistbrowserclass.h Hérité de browserclass, cet objet fournit un support de navigation pour les listes verticales, point d'appui des classificateurs. Par exemple, les fils du noeud des titres commençant par la lettre N sont stipulés comme formant une VList.
z3950cfg.h Support de structure de données pour le protocole Z39.50. Utilisé par z3950proto.cpp, qui définit la classe de protocole principale (héritée de la classe de base recptproto) et l'analyseur de fichier de configuration zparse.y (écrit en YACC).
z3950proto.h Héritée de recptproto, cette classe réalise le protocole de référence Z39.50 de sorte que le réceptionniste de Greenstone puisse accéder à des sites de bibliothèques distantes hébergés sur serveur Z39.50.
z3950server.h Support complémentaire pour le protocole Z39.50.

Initialisation

Dans Greenstone, l'initialisation est une opération compliquée qui traite les fichiers de configuration et affecte des valeurs par défaut aux champs de données. En plus des fonctions d'héritage et constructeur, les objets centraux définissent les fonctions init() et configure() pour faciliter la normalisation de la tâche. Même ainsi, l'ordre d'exécution peut être difficile à suivre. La présente section décrit ce qui se passe.

Greenstone utilise plusieurs fichiers de configuration pour des raisons différentes, mais tous suivent la même syntaxe. À moins qu'une ligne ne contienne que des blancs ou commence par un caractère dièse (#), le premier mot définit un mot-clef et les mots suivants représentent une configuration particulière pour ce mot-clef.

Les lignes des fichiers de configuration sont passées une à une à la fonction configure() sous la forme de deux arguments: le mot-clef et un tableau des mots restants. En se fondant sur le mot-clef, un version particulière de la fonction configure() décide si l'information l'intéresse, et dans l'affirmative, la stocke. Par exemple, collectserver (qui correspond à l'objet de collection de la figure <imgref figure_greenstone_runtime_system>) traite les instructions de mise en forme du fichier de configuration de collection. Quand la fonction configure() reçoit le mot-clef format, elle déclenche une instruction if qui stocke dans l'objet une copie du deuxième argument de la fonction.

Après le traitement du mot-clef et avant de rendre la main, certaines versions de configure() passent les données aux fonctions configure() d'autres objets. L'objet réceptionniste appelle les fonctions configure() des objets d'actions, de protocoles, et de navigation. L'objet de protocole vide appelle la fonction configure() de chaque objet de collection; l'objet de collection appelle les objets de filtre et source.

En C++, les champs de données sont normalement initialisés par la fonction qui construit l'objet. Cependant, dans Greenstone, certaines initialisations dépendent de valeurs lues dans des fichiers de configuration, il faut donc procéder à un deuxième tour d'initialisations. C'est le but des fonctions membre init(), et dans certains cas cela mène à d'autres appels de fonctions configure().

<imgcaption figure_initialising_greenstone_using_the_null_protocol|%!– id:1136 –%Initialisation de Greenstone avec le protocole vide ></imgcaption>

============
Main program
============
Statically construct Receptionist
Statically construct NullProtocol
Establish the value for 'gsdlhome' by reading gsdlsite.cfg
Foreach directory in GSDLHOME/collect that isn't "modelcol":
   Add directory name (now treated as collection name) to NullProtocol:
       Dynamically construct Collection
       Dynamically construct Gdbm class
       Dynamically construct the Null Filter
       Dynamically construct the Browse Filter
       Dynamically construct MgSearch
       Dynamically construct the QueryFilter
       Dynamically construct the MgGdbmSource
       Configure Collection with 'collection'
           Passing 'collection' value on to Filters and Sources:
       Configure Receptionist with 'collectinfo':
           Passing 'collectinfo' value on to Actions, Protocols, and Browsers:
Add NullProtocol to Receptionist
Add in UTF-8 converter
Add in GB converter
Add in Arabic converter
Foreach Action:
   Statically construct Action
   Add Action to Receptionist
Foreach Browsers:
   Statically construct Browser
   Add Browser to Receptionist
Call function cgiwrapper:
   =================
   Configure objects
   =================
   Configure Receptionist with 'collection'
       Passing 'collection' value on to Actions, Protocols, and Browsers:
       NullProtocol not interested in 'collection'
   Configure Receptionist with 'httpimg'
       Passing 'httpimg' value on to Actions, Protocols, and Browsers:
       NullProtocol passing 'httpimg' on to Collection
       Passing 'httpimg' value on to Filters and Sources:
   Configure Receptionist with 'gwcgi'
       Passing 'gwcgi' value on to Actions, Protocols, and Browsers:
       NullProtocol passing 'gwcgi' on to Collection
           Passing 'gwcgi' value on to Filters and Sources:
   Reading in site configuration file gsdlsite.cfg
       Configure Recptionist with 'gsdlhome'
           Passing 'gsdlhome' value on to Actions, Protocols, and Browsers:
           NullProtocol passing 'gsdlhome' on to Collection
               Passing 'gsdlhome' value on to Filters and Sources:
       Configure Recptionist with ...
       ... and so on for all entries in gsdlsite.cfg
   Reading in main configuration file main.cfg
       Confiugre Recptionist with ...
       ... and so on for all entries in main.cfg
   ====================
   Initialising objects
   ====================
   Initialise the Receptionist
       Configure Receptionist with 'collectdir'
           Passing 'collectdir' value on to Actions, Protocols, and Browsers:
           NullProtocol not interested in 'collectdir'
       Read in Macro files
       Foreach Actions
           Initialise Action
       Foreach Protocol
           Initialise Protocol
           When Protocol==NullProtocol:
               Foreach Collection
                   Reading Collection's build.cfg
                   Reading Collection's collect.cfg
                       Configure Collection with 'creator'
                           Passing 'creator' value on to Filters and Sources:
                       Configure Collection with 'maintainer'
                           Passing 'maintainer' value on to Filters and Sources:
                       ... and so on for all entries in collect.cfg
       Foreach Browsers
           Initialise Browser
   =============
   Generate page
   =============
   Parse CGI arguments
   Execute designated Action to produce page
End.

Les lignes <imgref figure_initialising_greenstone_using_the_null_protocol> présentent les messages de diagnostics générés par une version de Greenstone configurée pour mettre en valeur le processus d'initialisation. Le programme démarre à la fonction main() du fichier recpt/librarymain.cpp. Il construit un objet de réceptionniste et un objet de protocole vide, puis parcourt le fichier gsdlsite.cfg (situé dans le même répertoire que l'exécutable library) à la recherche de gsdlhome et stocke son résultat dans une variable. Pour chaque collection en ligne – liste établie en lisant les répertoires présents dans GSDLHOME/collect – le programme construit un objet de collection, à travers l'objet de protocole vide, qui contient des objets de filtre, de recherche et source, ainsi que quelques appels câblés à la fonction configure().

La fonction main() ajoute ensuite l'objet de protocole vide au réceptionniste, qui conserve un tableau de classe de base de protocoles dans un champ de données protégé, et positionne ensuite plusieurs convertisseurs. La fonction main() construit tous les objets d'action et de navigation utilisés dans l'exécutable et les ajoute à l'objet réceptionniste. La fonction conclut en appelant la fonction cgiwrapper() de cgiwrapper.cpp, qui comporte elle-même une certaine quantité d'initialisation d'objets.

La fonction cgiwrapper() comprend trois parties: configuration, initialisation, et génération de page. Tout d'abord, quelques appels câblés à la fonction configure() sont effectués. Le fichier gsdlsite.cfg est ensuite lu et la fonction configure() est appelée pour chacune de ses lignes. Même chose pour le fichier etc/main.cfg.

La deuxième phase de la fonction cgiwrapper() effectue des appels à init(). L'objet réceptionniste ne fait qu'un appel à sa fonction, mais cette invocation appelle les fonctions init() des différents objets qu'il contient. Un appel câblé à configure() est d'abord effectué pour positionner collectdir, puis les fichiers de macros sont lus. La fonction init() de chaque action est appelée. Même chose pour chaque protocole utilisé dans le réceptionniste – mais dans le cas du système décrit ici, seul le protocole vide y est stocké. L'appel de la fonction init() de cet objet provoque d'autres configurations: pour chaque collection du protocole vide, les fichiers propres à la collection build.cfg et collect.cfg sont lus et traités, chaque ligne provoquant un appel à la fonction configure().

La phase finale de cgiwrapper() consiste à analyser les arguments CGI, et appeler ensuite l'action appropriée. Tous ces appels sont effectués avec le support de l'objet réceptionniste.

Les codes de configuration, initialisation, et de génération de page sont séparés car Greenstone est optimisé pour fonctionner en tant que serveur (avec Fast-CGI, ou le protocole CORBA, ou la version «Bibliothèque locale» sous Windows). Dans ce mode de fonctionnement, le code de configuration et d'initialisation n'est effectué qu'une fois, et le programme demeure ensuite en mémoire et génère de nombreuses pages web en réponse aux requêtes des clients, sans nécessiter de réinitialisation.

1)
… facilement La valeur gsdlhome provient du fichier gsdlsite.cfg situé dans le même répertoire que l'exécutable CGI library, alors que GSDLHOME est positionné en exécutant le script setup qui accède à un fichier différent; il est donc techniquement possible que les deux valeurs soient différentes. C'est possible, mais pas désirable, et le texte ci-dessus est écrit dans l'hypothèse où ces deux valeurs sont égales.