Télécharger
    Installer
    Présentation
       Architecture
       Serveur
       Applications
       Bases de documents
       Entrepôt
       Multilinguisme
       Analyseurs
      +Débuter <-
    Configuration
    Indexation
    Recherche
    OAI
    Javadoc
    Référence API-XSP
    Migration
    Schemas
    Performances


SDX

Bien débuter avec SDX-2

Ca y est ! Vous venez d'installer SDX-2 avec succès : vous avez pu prendre contact avec les possibilités d'administration du serveur et accédé à l'application de démonstration. Vous y avez chargé des documents XML, peut-être même des documents HTML en provenance d'un système de fichiers local ou d'Internet, ce qui vous a donné une idée de la façon dont SDX indexait les documents. Ensuite, vous avez vu de quelle façon SDX pouvait restituer les documents à la suite d'une requête. Bref, vous avez compris les grandes lignes du fonctionnement de SDX.

C'est décidé, vous allez monter une application, offrir un moteur de recherche permettant la diffusion de vos milliers de documents XML ou HTML sur la toile : votre chef sera content, vous allez être promu, vous pourrez négocier une augmentation en position de force, vous brillerez dans les salons, vos écrouelles vont miraculeusement disparaître si bien que vous allez pouvoir envisager des tas de conquêtes f/m... STOP !!!

Gardons la tête froide

Avant toute chose, reportez vous à la documentation sur les applications : elle donne des pistes pour résoudre les épineux problèmes de responsabilité éditoriale et de responsabilité informatique. Dans ce document, nous prendrons le parti que vous êtes à la fois l'éditeur et le responsable informatique. Ainsi, vous pourrez commencer à développer votre première application sans avoir à vous soucier du fait que vos documents puissent être diffusés ou pas, que votre serveur puisse assumer la montée en charge qui sied à un site attractif, de qui a les droits en écriture sur votre base de documents... De plus, votre pare-feu vous protège du monde sauvage des hackers si bien que vous pouvez sans appréhension ouvrir un serveur HTTP comme l'est un moteur de servlets. N'oubliez cependant jamais que vous aurez tôt ou tard à résoudre le déploiement de votre application sur un serveur de production.

Ensuite, vous devez réflechir au concept même de base SDX. A ce sujet il peut être intéressant de voir ou revoir ce que ce terme signifie.

  1. Au sens strict du terme, une base de données est un endroit où se trouvent des données.

    La définition est simple, mais elle doit cependant être discutée. Doit-on considérer que :

    1. la base de données s'accapare, grâce aux privilèges quasi-exclusifs qu'elle a sur eux, les fichiers informatiques sur disque et/ou en mémoire qui ne sont rien d'autre que la réalité physique des données,

    2. la base de données peut se contenter de référencer les données et de déléguer la gestion des fichiers à d'autres systèmes, éventuellement distants, répondant au critère ci-dessus défini ?

    Quelle que soit la réponse, SDX est une base de données. En effet, il stocke les données dans des entrepôts. Certains stockent physiquement les données (FSRepository, JDBCRepository), d'autres non (URLRepository). Certes, on joue sur les mots : ce n'est pas SDX qui stocke les documents ou leurs références, ce sont des composants travaillant avec SDX : Lucene, HSQL, MySQL, Oracle, PostGreSQL... Soit !

  2. Une autre approche du terme consiste à considérer une base de données comme un moyen de retrouver des données grâce à un langage d'interrogation, si possible accessible à un utilisateur humain. Le concept initial glisse ainsi d'une réalité physique à une réalité logique (d'ailleurs, qui se soucie encore aujourd'hui de la forme physique des fichiers sur lesquels travaille un SGDB à part un développeur de SGDB ?).

    Dans ce contexte, SDX est encore une base de données.

Tout ceci étant dit, pourquoi choisir SDX ? Si l'on veut stocker des documents XML, pourquoi ne pas se contenter de bases de données XML comme XIndice ou eXist ? D'autant plus que celles-ci disposent d'un langage de requête standardisé, de cautions prestigieuses, de nombreux utilisateurs, d'une plus grande activité, d'un support technique plus performant...

La réponse est simple... sur le plan théorique : parce que, à l'inverse des autres bases de données XML, SDX fait une distinction entre  :

  1. la forme que les documents revêtent lorsqu'il sont stockés (ou référencés),

  2. la forme que les documents revêtent lorsqu'ils sont interrogés.

Qu'est-ce que c'est que ce charabia ?

Prenons deux exemples simples : si vous stockez la valeur John Doe dans le champ name d'un SGDB SQL, vous vous attendez à pouvoir y accéder via une requête SELECT name FROM a_table WHERE name = 'John Doe';. De même, si vous stockez <NAME>John Doe</NAME> dans une base de données XML, vous vous attendez à pouvoir retrouver cet élément grâce à une requête de ce type : document(*)//NAME = 'John Doe'.

Le problème est précisément là : pour rechercher le contenu d'un document on doit interroger sa structure et donc... la connaître !

Bien sûr, si vous pouvez garantir que tous vos documents XML auront la même structure (c'est à dire qu'ils partagent une même grammaire), vous pouvez envisager de les déployer sur une base de données, XML... ou autre. Mais, êtes vous prêt à payer le prix de langages de requêtes complexes et, surtout, pouvez-vous exiger des utilisateurs qu'ils connaissent la grammaire de vos documents ? Pour prendre un exemple simple, une DTD HTML, pensez vous que les utilisateurs penseront à interroger title alors qu'ils veulent un titre ? Et croyez-vous qu'ils penseront à interroger également h1, h2, h3... qui sont également des titres ?

Si vous pensez que oui, rendez vous directement aux liens mentionnés ci-dessus. Si vous pensez que non, vous pouvez passer à la section suivante...

L'indexation dans SDX

Comme on vient de le voir, SDX stocke ou référence des documents XML : le concept est suffisament traditionnel pour pouvoir être immédiatement compris. En revanche, quelle est cette mystérieuse forme que revêtent les documents lorsqu'ils sont interrogés ?

Tout d'abord, cette forme a un nom, l'indexation. Ce concept, lui aussi traditionnel, est clair : on prend les valeurs contenues dans les documents et on les référence, ce qui permet une recherche rapide car on n'a pas à interroger les documents un par un pour voir si la valeur recherchée y est présente... dans le cas le plus favorable.

Ici encore, les SGDB sont responsables d'un glissement de sens. Un index est, chez eux, une liste de valeurs dont on pense qu'elles seront plus souvent interrogées que d'autres. Les SGDB sont donc éternellement en quête d'un compromis entre la sélectivité des index et leur taille. En effet, il serait contre-productif pour eux d'indexer l'intégralité de la base de données car l'interrogation des index prendrait, par définition, autant de temps que la consultation de chacun des documents. Certes, chaque SGDB y va de son optimiseur de requêtes, de son analyseur d'index, de son détecteur de requêtes longues, de son cache d'index... mais, fondamentalement, les index n'ont, pour eux, que vocation à être un résumé de la base de données.

Avec SDX, la définition d'un index est la même, mais la doctrine d'emploi en est radicalement différente de celle d'un SGDB :

  1. vous pouvez définir des jeux d'index. C'est selon moi, dans le vocabulaire SDX, la définition même d'une base. Ainsi, vous pouvez définir une logique applicative qui travaillera sur tels ou tels index plutôt que sur l'ensemble d'entre eux ce qui, bien balancé, permet une optimisation des performances. La logique applicative doit donc être un critère important sinon déterminant dans la définition d'une base au sein d'une application SDX,

  2. le contenu des index est sous votre responsabilité et SDX ne vous impose pratiquement pas de limites (longueur, occurences...),

  3. il n'est pas mal vu d'avoir de gros index : il est prévisible que beaucoup de bases auront un index fulltext,

  4. il est tout à fait concevable que vos index aient un contenu qui n'a aucune relation avec celui de vos documents : vous pouvez donc vous en servir pour stocker toutes sortes de métadonnées (date de chargement, responsable du document, état de complétude...) qui seront fournies par la logique applicative.

En résumé : vous pouvez définir (ou ne pas définir) vos index comme vous l'entendez et vous pouvez définir leur contenu comme vous l'entendez. Par ailleurs, vous pouvez les typer mais, pour l'instant, seuls les index de type chaîne de caractère et de type date sont possibles.

De la théorie à la pratique

Vous disposez de documents XML (ou HTML que SDX transformera en documents XML grâce à l'intégration native de JTidy dans son architecture) et vous allez devoir les indexer. Quelle doit être votre réflexion pour que ces documents puissent être indexés dans SDX ?

Dans la plupart des cas, c'est l'information sémantique qui guidera votre choix. En effet, les informations de structure n'ont souvent aucun intérêt pratique pour un utilisateur de base et, parfois, pour un utilisateur avancé. Vous devez donc dresser la liste des sémantiques que vous voulez rendre interrogeables et, vous ne pourrez pas y passer sauf à développer des fonctions ad hoc, chercher les moyens de retrouver les sémantiques choisies dans vos différents types de documents.

Ainsi, si vous disposez des documents suivants :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE chapter PUBLIC "-//Norman Walsh//DTD DocBk XML V1.3//EN"
  "n:/share/sgml/Norman_Walsh/db31xml/db3xml.dtd">
<chapter>
  <title>Premier chapitre</title>
  <para>
    La première fois que j'ai vu <firstname>Nikita</firstname>...
  </para>
</chapter>
	
<?xml version="1.0" encoding="ISO-8859-1"?>
<movie>
  <title>Nikita</title>
  <director>Luc Besson</director>
  <type>Thriller</type>
  <date>1990</date>
  <roles>
    <role>
      <character>Nikita</character>
      <actor>Anne Parillaud</actor>    
    </role>    
  </roles> 
</movie>
	
<?xml version="1.0" encoding="ISO-8859-1"?>
<rapport>
  <de>4eme bureau</de>
  <a>2eme bureau</a>
  <resume>Activités de <nom>Nikita</nom></resume>
  <detail>A récemment été vue à <lieu>Venise</lieu> où on la soupçonne d'avoir...</detail>
</rapport>
	

... vous avez matière à indexer, et donc à rendre cherchable, des informations concernant des noms de personnes (mais n'oubliez pas de déclarer votre fichier à la CNIL).

Pour ce faire, une solution évidente s'impose : XSLT. Profitons-en pour rappeler que SDX réclame une bonne connaissance de ce langage. C'est précisément le parti qu'a choisi SDX pour générer les valeurs d'index : il attend une transformation des documents XML d'origine pour parvenir à un document transformé répondant à la DTD suivante :

<!ELEMENT sdx:document (sdx:field | sdx:attachedDocument)*>
<!ATTLIST sdx:document
	id CDATA #REQUIRED
>
<!ELEMENT sdx:field (#PCDATA)>
<!ATTLIST sdx:document
	name CDATA #REQUIRED
>
<!ELEMENT sdx:attacheddocument (EMPTY)>
<!ATTLIST sdx:attacheddocument
	id CDATA #REQUIRED
	url CDATA #REQUIRED
	mimetype CDATA #REQUIRED
>

Votre document d'indexation devra donc comporter les éléments suivants 

  1. Un élément racine <sdx:document> contenant un attribut obligatoire id. Cet attribut affecte au document indexé un identifiant unique dans la base ; nous en reparlerons.

  2. D'éventuels éléments <sdx:field> dont le contenu sera précisément le contenu des index. A première vue, il est assez paradoxal qu'un index soit défini par un élément nommé field. Cela s'explique pour des raisons historiques : les index de SDX étaient, et sont encore, transcrits dans des objets Field de Lucene.

    L'attribut obligatoire name indique le nom de l'index ; nous en reparlerons également. Important : les index dont le nom commence par sdx sont réservés à SDX.

    D'ores et déjà, il est important de préciser qu'un index ayant un nom déterminé peut avoir plusieurs occurences dans le même document d'indexation, ce qui revient à dire qu'un même index SDX peut recevoir plusieurs parties d'un même document.

    Certes, ces éléments sont facultatifs... mais si vous ne créez pas de contenu pour vos index, votre base ne sera pas interrogeable.

  3. D'éventuels éléments <sdx:attachedDocument> qui permettent de définir une liste de documents qui doivent être chargés en même temps que le document XML et qui ne sont pas indexés. Ces documents seront considérés comme propriété du document XML et seront détruits si le document "maître" venait à l'être... à moins que le document attaché n'ait été mis à disposition d'autres documents "maîtres" par la suite.

    L'attribut obligatoire id indique l'identifiant unique du document attaché dans la base. C'est grâce à cette id que les documents attachés peuvent être éventuellement partagés : il suffit de la réutiliser.

    L'attribut obligatoire url indique l'adresse du document attaché, adresse qui sera utilisée par SDX pour le chargement du document attaché. Le cas échéant, ce peut être une adresse relative au document maître (mais veillez à ce qu'elle soit accessible au serveur SDX).

    L'attribut obligatoire mimetype indique le type MIME du document attaché (image/jpeg, video/mpeg...), ce qui permettra aux API URL de SDX de mettre ce document attaché à la disposition du navigateur ou d'autres applications respectant le protocole HTTP.

Et maintenant, un peu de pratique

Vous disposez maintenant de tous les éléments pour créer un document d'indexation SDX. Vous pouvez, au choix, rédiger une ou plusieurs (mais une seule est préférable pour des raisons dont nous reparlerons : si vraiment vous ne pouvez résoudre votre problème qu'à l'aide de plusieurs XSL, considérez la définition de plusieurs bases) XSL qui seront à même de générer un document d'indexation à partir de vos documents XML. Dans l'exemple suivant, nous allons essayer de réaliser une feuille qui sera capable d'indexer, dans un index nommé personne, les noms contenus dans les documents montrés ci-dessus :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ne pas oublier le namespace SDX -->
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
	<!-- un paramètre recevant une valeur pour l'id du document --> 
	<xsl:param name="doc_id">xxx</xsl:param>
	<xsl:template match="/">
		<!-- soyons prudent : ne traitons que les documents que nous connaissons -->
		<xsl:apply-templates select="chapter"/>
		<xsl:apply-templates select="movie"/>
		<xsl:apply-templates select="rapport"/>
	</xsl:template>
	<xsl:template match="chapter">
		<!-- on définit un document avec l'id définie en paramètre -->
		<sdx:document id="{$doc_id}">
			<sdx:field name="personne">
				<xsl:value-of select="//firstname"/>
			</sdx:field>
		</sdx:document>
	</xsl:template>
	<xsl:template match="movie">
		<!-- on définit un document avec l'id définie en paramètre -->		
		<sdx:document id="{$doc_id}">
			<xsl:for-each select="//director | //character | //actor">
				<sdx:field name="personne">
					<xsl:value-of select="."/>
				</sdx:field>
			</xsl:for-each>
		</sdx:document>
	</xsl:template>
	<xsl:template match="rapport">
		<!-- on définit un document avec l'id définie en paramètre -->
		<sdx:document id="{$doc_id}">
			<sdx:field name="personne">
				<xsl:value-of select="de"/>
			</sdx:field>
			<sdx:field name="personne">
				<xsl:value-of select="a"/>
			</sdx:field>
			<sdx:field name="personne">
				<xsl:value-of select="resume/nom"/>
			</sdx:field>						
		</sdx:document>
	</xsl:template>
</xsl:stylesheet>			

Cette transformation produit les résultats suivants :

  1. <?xml version="1.0" encoding="UTF-8"?>
    <sdx:document id="xxx" xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
    	<sdx:field name="personne">Nikita</sdx:field>
    </sdx:document>
    
  2. <?xml version="1.0" encoding="UTF-8"?>
    <sdx:document id="xxx" xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
    	<sdx:field name="personne">Luc Besson</sdx:field>
    	<sdx:field name="personne">Nikita</sdx:field>
    	<sdx:field name="personne">Anne Parillaud</sdx:field>
    </sdx:document>
    
  3. <?xml version="1.0" encoding="UTF-8"?>
    <sdx:document id="xxx" xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
    	<sdx:field name="personne">4eme bureau</sdx:field>
    	<sdx:field name="personne">2eme bureau</sdx:field>
    	<sdx:field name="personne">Nikita</sdx:field>
    </sdx:document>
    

Facile, non ? A vous de jouer désormais. Lorsque votre ou vos XSL seront prêtes et que tous vos documents XML vous donneront les résultats escomptés, nous pourrons passer à l'étape suivante. Si d'ores et déjà vos documents possèdent en leur sein un identifiant unique, ou que vous pouvez rendre unique (par une concaténation par exemple), n'hésitez pas à alimenter l'attribut id de <sdx:document/> avec une valeur moins arbitraire que celle de la XSL ci-dessus.

Bonus : l'indexation plein-texte

En guise de cadeau, voici une XSL permettant d'indexer l'intégralité du contenu des éléments d'un document XML dans un index nommé contenu :

<?xml version="1.0" encoding="ISO-8859-1"?>					
<!-- ne pas oublier le namespace SDX -->
<xsl:stylesheet version="1.0" 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
	<xsl:template match="/">
		<sdx:document id="xxx">
			<sdx:field name="contenu">
				<!-- 
				la hiérarchie est passée en revue 
				sous le contrôle du mode "fulltext"
				-->
				<xsl:apply-templates mode="fulltext"/>
			</sdx:field>
		</sdx:document>
	</xsl:template>
	<!-- un modèle générique pour tous les éléments -->
	<xsl:template match="*" mode="fulltext">
		<!-- 
		un appel, éventuellement récursif, 
		sur le noeud texte 
		et/ou les éléments contenus 
		-->
		<xsl:apply-templates select="*|text()" mode="fulltext"/>
	</xsl:template>
	<!-- traitement des noeuds texte -->
	<xsl:template match="text()" mode="fulltext">
		<xsl:value-of select="normalize-space(.)"/>
		<!-- on ajoute une espace entre les contenus des différents noeuds -->
		<xsl:if test="normalize-space(.) !=''">
			<xsl:text>&#32;</xsl:text>
		</xsl:if>
	</xsl:template>
</xsl:stylesheet>

... ce qui donne (en ISO-8859-1) :

<?xml version="1.0" encoding="ISO-8859-1"?>					
<sdx:document id="xxx" xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
	<sdx:field name="contenu">4eme bureau 2eme bureau Activités de Nikita 
	A récemment été vue à Venise où on la soupçonne d'avoir... </sdx:field>
</sdx:document>

Ne vous inquiétez pas de cette agglutination de mots qui peut être très longue. Nous verrons sous peu comment SDX les gère. Considérez pour l'instant que cette transformation est équivalente à ce document d'indexation :

<?xml version="1.0" encoding="ISO-8859-1"?>					
<sdx:document id="xxx" xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
	<sdx:field name="contenu">4eme</sdx:field>
	<sdx:field name="contenu">bureau</sdx:field>
	<sdx:field name="contenu">2eme</sdx:field>
	<sdx:field name="contenu">bureau</sdx:field>
	<sdx:field name="contenu">Activités</sdx:field>
	<sdx:field name="contenu">de</sdx:field>
	<sdx:field name="contenu">Nikita</sdx:field>
	<sdx:field name="contenu">A</sdx:field>
	<sdx:field name="contenu">récemment</sdx:field>
	<sdx:field name="contenu">été</sdx:field>
	<sdx:field name="contenu">vue</sdx:field>
	<sdx:field name="contenu">à</sdx:field>
	<sdx:field name="contenu">Venise</sdx:field>
	<sdx:field name="contenu">où</sdx:field>
	<sdx:field name="contenu">on</sdx:field>
	<sdx:field name="contenu">la</sdx:field>
	<sdx:field name="contenu">soupçonne</sdx:field>
	<sdx:field name="contenu">d'avoir...</sdx:field>
</sdx:document>

Configuration d'une application et premier test

Vous voilà donc en mesure de disposer de documents d'indexation pour tous vos documents grâce à une XSL ad hoc. Le grand moment est arrivé : nous pouvons passer au déploiement. Rappelons au préalable la structure de répertoires de SDX apès son installation :

[TOMCAT_HOME]/webapps/sdx
                        +META-INF
                        +sdx
                        +sdxworld
                        +WEB-INF                        

Le répertoire supérieur est le répertoire du contexte SDX. Les répertoires fils contiennent respectivement un répertoire pour les manifestes (qui permettent par exemple l'authentification des composants logiciels), un répertoire pour l'application d'administration de SDX (qui contient également quelques ressources que nous aurons l'occasion d'utiliser), un répertoire pour une l'application exemple sdxworldet un répertoire de configuration du serveur SDX.

Vous pouvez maintenant créer un répertoire pour votre application. Appelons-le mon_app :

[TOMCAT_HOME]/webapps/sdx
                        +META-INF
                        +mon_app                        
                        +sdx
                        +sdxworld
                        +WEB-INF                        

mon_app se déclare donc d'une façon strictement identique à celle des applications SDX "officielles". Etablissons maintenant une hiérarchie minimale dans ce répertoire :

[TOMCAT_HOME]/webapps/sdx
                        +META-INF
                        -mon_app                        
                         +conf
                         +xsl
                        +sdx
                        +sdxworld
                        +WEB-INF                        

Les noms sont assez explicites : le premier répertoire recevra les fichiers de configuration de SDX, le second recevra les XSL à même de produire un résultat pour les clients, à savoir du code HTML.

Nous allons passer à la configuration proprement dite. Copiez au préalable le fichier sdxworld/sitemap.xmap dans le répertoire mon_app : nous verrons quelques-unes de ses fonctionnalités par la suite. Copiez ensuite votre XSL d'indexation dans le répertoire mon_app/conf sous le nom indexation.xsl. Créez dans le même répertoire un autre fichier, XML, que vous devez appeler application.xconf. Vous devez donc disposer de la hiérarchie suivante :

[TOMCAT_HOME]/webapps/sdx
                        +META-INF
                        -mon_app
                         -conf
                          application.xconf
                          indexation.xsl
                         +xsl
                         sitemap.xmap
                        +sdx
                        +sdxworld
                        +WEB-INF                        

Rédaction du fichier de configuration de l'application

Nous devons désormais alimenter le fichier application.xconf de notre application :

  1.  

    <?xml version="1.0" encoding="UTF-8"?>
    <sdx:application 
    	xmlns="http://www.culture.gouv.fr/ns/sdx/sdx"
    	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx" 
    	id="domaine.sdx.mon_app" xml:lang="fr-FR">
    

    L'élément racine, <sdx:application> comporte :

    • La définition obligatoire de namespaces SDX.

    • La définition obligatoire d'une id pour l'application. Voir la documentation pour en savoir plus sur les règles de nommage qu'il convient d'adopter.

    • La définition obligatoire d'une locale par défaut pour l'application. Voir la documentation pour en savoir plus.

  2. Ensuite, viennent normalement des informations sur les autorisations d'accès. Nous ne nous en soucierons pas pour l'instant et nous nous passerons de cette déclaration.

  3. Ensuite, vient la définition de la base des utilisateurs qui est... une base SDX. Recopions la configuration par défaut qui devrait suffire dans la plupart des cas :

    <sdx:userDocumentBase>
    	<sdx:repositories>
    		<sdx:repository id="users" type="FS" 
    			baseDirectory="users/xml" 
    			depth="0" extent="1000"/>
    	</sdx:repositories>
    	<sdx:fieldList xml:lang="fr-FR" variant="">
    		<sdx:field name="name" type="field" brief="true"/>
    		<sdx:field name="firstname" type="field" brief="true"/>
    		<sdx:field name="lastname" type="field" brief="true"/>
    		<sdx:field name="description" type="word"/>
    		<sdx:field name="lang" type="field" brief="true"/>
    		<sdx:field name="variant" type="field" brief="true"/>
    		<sdx:field name="email" type="field" brief="true"/>
    		<sdx:field name="content" type="word" default="true">
    			<sdx:name xml:lang="fr-FR">Texte intégral</sdx:name>
    		</sdx:field>
    	</sdx:fieldList>
    	<sdx:index>
    		<sdx:pipeline>
    			<sdx:transformation id="index" type="XSLT" 
    				src="/sdx/resources/indexation/index-identity.xsl"/>
    		</sdx:pipeline>
    	</sdx:index>
    </sdx:userDocumentBase>
    

    Etant donné que cette base d'utilisateurs est une base SDX, nous préférons commenter la structure de cet élément lors d'une phase suivante. Notons cependant que cette base n'a pas d'identifiant (attribut id).

  4. Sujet connexe au précédent, nous définissons le groupe des administrateurs de la base et, dans ce groupe, nous définissons un membre ainsi que son éventuel mot de passe :

    <sdx:admin groupId="admins" userId="admin" userPassword="secret"/>
    
  5. Entrons maintenant dans le vif du sujet en définissant notre base :

    •  

      <sdx:documentBases>
      	<sdx:documentBase id="ma_base" type="lucene" 
      		default="true" keepOriginalDocuments="true">
      

      Le premier élément rappelle que SDX est multibase ; on peut donc définir autant de bases que l'on veut.

      Le second élément définit la base proprement dite : l'id doit être unique dans l'application, le type est pour l'instant limité à lucene, l'attribut default est utile lorsque l'application est multibase et l'attribut keepOriginalDocuments définit le comportement à adopter lors du stockage des documents dans la base : nous en reparlerons.

    • Vient ensuite la définition du parseur de requêtes qui sera utilisé pour les requêtes simples. Nous allons utiliser le parseur par défaut de SDX qui est le parseur de Lucene.

      <sdx:queryParser class="fr.gouv.culture.sdx.search.lucene.queryparser.DefaultQueryParser"/>
      
    • Définissons ensuite deux entrepôts, le premier stockera les documents dans le système de fichiers de SDX, le second référencera les documents grâce à leur URL.

      <sdx:repositories>
      	<sdx:repository type="FS" id="stockage" 
      		baseDirectory="entrepots/documents"
      		depth="0" extent="100" default="true"/>
      	<sdx:repository id="references" type="URL"/>
      </sdx:repositories>
      

      Rappel : rappelons que le protocole file:// est un protocole valide pour une URL et que, par conséquent, il est possible de référencer des fichiers qui sont sur le système de fichiers local sans pour autant appartenir au système de fichiers que le moteur de Servlets met à la disposition de SDX : nous en reparlerons.

    • Vient ensuite la liste d'index. Nous en définirons deux, l'un s'appelant contenu, sensé recevoir notre indexation plein-texte, l'autre s'appelant personne. Nous fournissons également des traductions de ces concepts dans quelques langues afin de pouvoir offrir des traductions aux utilisateurs le moment venu.

      						
      <sdx:fieldList xml:lang="fr-FR" variant="" 
      	analyzerConf="/sdx/resources/conf/analysis/fr.xml">	
      	<sdx:field name="contenu" type="word" default="true">
      		<sdx:name xml:lang="fr">Mots</sdx:name>
      		<sdx:name xml:lang="en">Words</sdx:name>
      		<sdx:name xml:lang="ge">W_rter</sdx:name>
      		<sdx:name xml:lang="ar">الكلمات</sdx:name>
      		<sdx:name xml:lang="br">Gerioù</sdx:name>
      		<sdx:name xml:lang="eu">Hitzak</sdx:name>
      	</sdx:field>	
      	<sdx:field name="personne" type="field" brief="true">
      		<sdx:name xml:lang="fr">Personnes</sdx:name>
      		<sdx:name xml:lang="en">Persons</sdx:name>
      		<sdx:name xml:lang="ge">Personnen</sdx:name>
      		<sdx:name xml:lang="ar">الأشخاص</sdx:name>
      		<sdx:name xml:lang="br">Tud</sdx:name>
      		<sdx:name xml:lang="eu">Pertsonak</sdx:name>
      	</sdx:field>
      </sdx:fieldList>
      

      La définition de la liste fait encore une fois appel à une définition de locale par défaut. De plus, elle associe un fichier de configuration pour l'analyseur d'index ; ce fichier contient donc une liste-type de mots vides de la langue française. Libre à vous de faire pointer la configuration de l'analyseur vers votre propre fichier.

      Les définitions d'index comportent un attribut obligatoire name qui correspond à l'attribut name de chacun des éléments <sdx:field> de vos transformations d'indexation. Si votre transformation génére des attributs name qui ne figurent pas dans cette liste, SDX vous renverra une erreur. En revanche, il est tout à fait possible que certains index puissent n'être utilisés que plus tard, lorsque votre transformation d'indexation sera capable de générer du contenu pour eux.

      L'attribut type sert à définir la façon dont l'index sera constitué :

      • Un index de type word stockera des mots qui seront au préalable analysés (on vient de définir l'analyseur par défaut). Par ce terme, on entend par exemple que les mots vides ne seront pas indexés, les majuscules seront tranformées en minuscules, les accents seront supprimés...

      • Un index de type field verra son contenu stocké tel quel. Les recherches devront donc se faire en tenant compte des majsucules, des minuscules, des espaces...

      • Un index de type date stockera une date dans un format interne à SDX. Ainsi, quel que soit le format de date fourni, SDX sera en principe capable d'assurer la cohérence des contenus et de donc de garantir des requêtes homogènes sur ce type d'index.

      L'attribut brief indique que le contenu de l'index sera retourné dans les résultats d'une requête : on verra l'usage qu'on peut en faire mais, d'ores et déjà, on peut dire que cela sert essentiellement à :

      • afficher des listes de résultats,

      • donner les moyens à SDX de trier les résultats.

    • <sdx:index>
      	<sdx:pipeline>
      		<sdx:transformation id="mon_indexation" 
      		type="XSLT" src="indexation.xsl"/>
      	</sdx:pipeline>
      </sdx:index>
      

      Ce contenu concerne évidemment l'indexation mais il doit être noté que cela concerne l'indexation par défaut (car il est possible de définir des transformations d'indexation dynamiques). SDX utilise un pipeline Cocoon, ce qui permet éventuellement d'opérer la transformation d'indexation en plusieurs phases pour, au final, parvenir à un document d'indexation tel qu'il est attendu par SDX. Les adresses sont relatives au répertoire de configuration de l'application.

  6. Fermons les éléments ouverts :

    		</sdx:documentBase>
    	</sdx:documentBases>
    </sdx:application>
    

Récapitulons le contenu de notre fichier application.xconf :

<?xml version="1.0" encoding="UTF-8"?>
<sdx:application 
	xmlns="http://www.culture.gouv.fr/ns/sdx/sdx" 
	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx" 
	id="domaine.sdx.mon_app" xml:lang="fr-FR">
	<sdx:userDocumentBase>
		<sdx:repositories>
			<sdx:repository id="users" type="FS" 
			baseDirectory="users/xml" 
			depth="0" extent="1000"/>
		</sdx:repositories>
		<sdx:fieldList xml:lang="fr-FR" variant="">
			<sdx:field name="name" type="field" brief="true"/>
			<sdx:field name="firstname" type="field" brief="true"/>
			<sdx:field name="lastname" type="field" brief="true"/>
			<sdx:field name="description" type="word"/>
			<sdx:field name="lang" type="field" brief="true"/>
			<sdx:field name="variant" type="field" brief="true"/>
			<sdx:field name="email" type="field" brief="true"/>
			<sdx:field name="content" type="word" default="true">
				<sdx:name xml:lang="fr-FR">Texte intégral</sdx:name>
			</sdx:field>
		</sdx:fieldList>
		<sdx:index>
			<sdx:pipeline>
				<sdx:transformation id="index" type="XSLT" 
				src="/sdx/resources/indexation/index-identity.xsl"/>
			</sdx:pipeline>
		</sdx:index>
	</sdx:userDocumentBase>
	<sdx:admin groupId="admins" userId="admin" userPassword="secret"/>
	<sdx:documentBases>
		<sdx:documentBase id="ma_base" type="lucene" 
		default="true" keepOriginalDocuments="true">
			<sdx:queryParser 
			class="fr.gouv.culture.sdx.search.lucene.queryparser.DefaultQueryParser"/>
			<sdx:repositories>
				<sdx:repository type="FS" id="stockage" 
				baseDirectory="entrepots/documents" 
				depth="0" extent="100" default="true"/>
				<sdx:repository id="references" type="URL"/>
			</sdx:repositories>
			<sdx:fieldList xml:lang="fr-FR" variant="" 
			analyzerConf="/sdx/resources/conf/analysis/fr.xml">
				<sdx:field name="contenu" type="word" default="true">
					<sdx:name xml:lang="fr">Mots</sdx:name>
					<sdx:name xml:lang="en">Words</sdx:name>
					<sdx:name xml:lang="ge">W_rter</sdx:name>
					<sdx:name xml:lang="ar">الكلمات</sdx:name>
					<sdx:name xml:lang="br">Gerioù</sdx:name>
					<sdx:name xml:lang="eu">Hitzak</sdx:name>
				</sdx:field>
				<sdx:field name="personne" type="field" brief="true">
					<sdx:name xml:lang="fr">Personnes</sdx:name>
					<sdx:name xml:lang="en">Persons</sdx:name>
					<sdx:name xml:lang="ge">Personnen</sdx:name>
					<sdx:name xml:lang="ar">الأشخاص</sdx:name>
					<sdx:name xml:lang="br">Tud</sdx:name>
					<sdx:name xml:lang="eu">Pertsonak</sdx:name>
				</sdx:field>
			</sdx:fieldList>
			<sdx:index>
				<sdx:pipeline>
					<sdx:transformation id="mon_indexation" 
					type="XSLT" src="indexation.xsl"/>
				</sdx:pipeline>
			</sdx:index>
		</sdx:documentBase>
	</sdx:documentBases>
</sdx:application>

C'est parti !

Enfin, presque... il nous reste à faire une dernière petite chose avant de commencer. Créer une page XSP d'accueil pour notre application.

Créons un fichier XML avec le contenu (très minimaliste) suivant :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ne pas oublier le namespace XSP -->
<!-- ne pas oublier le namespace SDX -->
<xsp:page language="java" 
	xmlns:xsp="http://apache.org/xsp" 
	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
	<sdx:page/>
</xsp:page>

Et copions-le dans le répertoire de notre application sous le nom index.xsp (nous verrons sous peu pourquoi choisir ce nom plutôt qu'un autre). On a donc :

-mon_app
 -conf
  application.xconf
  indexation.xsl
 +xsl  
 index.xsp 
 sitemap.xmap

Démarrons maintenant le moteur de servlets et connectons-nous en tant que super-utilisateur (http://serveur:port/sdx/sdx/admin/loginsu.xsp) : nous devons en effet ouvrir notre application.

Truc : on peut également ouvrir une application un créant un fichier du nom du répertoire de l'application dans le répertoire [TOMCAT_HOME]/sdx/WEB-INF/sdx/applications et en redémarrant le moteur de sevlets. Son contenu importe peu, aussi le fichier peut-il être vide. Réciproquement, supprimer un fichier de ce type ferme une application.

Après ouverture, osons enfin cliquer sur le lien qui nous amène vers l'application... (http://serveur:port/sdx/mon_app)

Patatras !!!

...SDX nous renvoie une magnifique erreur :

org.apache.cocoon.ProcessingException: Exception during processing of file:/C:/tomcat4/webapps/sdx/mon_app/sitemap.xmap: java.io.FileNotFoundException: [TOMCAT_HOME]\sdx\mon_app\sitemap.xmap (Le fichier spécifié est introuvable)

Que s'est-il donc passé ? La réponse est dans sitemap.xmap. Premier point :

<map:match pattern="">
    <map:redirect-to uri="index.xsp"/>
</map:match>

Etant donné que nous avons demandé un répertoire plutôt qu'un fichier particulier, Cocoon a cherché à vous rediriger vers une page nommée... index.xsp. Second point :

<map:match pattern="*.xsp">
    <map:generate type="xsp" src="{1}.xsp"/>
    <map:transform src="xsl/{1}.xsl">
        <map:parameter name="use-request-parameters" value="true"/>
    </map:transform>
    <map:serialize/>
</map:match>

... Cocoon a cherché à transformer index.xsp avec la XSL xsl/index.xsl. Cette XSL n'existant pas (encore), ce qui s'est passé est tout à fait normal !

Maintenant, si vous cherchez à accéder à http://serveur:port/sdx/mon_app/index.xsp2sdx...

... vous avez enfin du contenu !

 <?xml version="1.0" encoding="ISO-8859-1" ?> 
<sdx:document 
	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx" 
	xmlns:xsp="http://apache.org/xsp" xml:lang="fr-FR" 
	server="http://localhost:8080/sdx" 
	api-url="http://localhost:8080/sdx/sdx/api-url" 
	uri="http://localhost:8080/sdx/mon_app/index.xsp2sdxX" 
	date="Thu Jan 23 22:57:38 CET 2003">
  <sdx:user anonymous="true" /> 
</sdx:document>			

Certes, ce contenu n'est pas très intéressant, mais il est déjà bien plus fourni que votre XSP source puisque vous pouvez constater que SDX est en mesure de donner des informations sur sa configuration et de dire quel est statut de l'utilisateur.

Comment ce "miracle" a-t-il pu avoir lieu ? Ici encore, la réponse est dans sitemap.xmap :

<map:match pattern="**.xsp2sdx">
    <map:generate type="xsp" src="{1}.xsp"/>
    <map:transform src="../sdx/resources/xsl/xml.xsl"/>
    <map:serialize type="html"/>
</map:match>

On voit que les extensions xsp2sdx sont sérialisées en HTML (c'est pour cela que le navigateur est capable d'afficher un résultat) et que la transformation est faite grâce à la précieuse XSL utilitaire mis à notre disposition dans sdx/sdx/resources/xsl/xml.xsl. Si votre navigateur est capable d'afficher décemment le XML, vous pouvez utiliser l'extension xsp2sdxX qui, elle, sérialise en text et que le navigateur sera en principe capable de reconnaître comme étant du XML, forme particulière de text.

Nous voici désormais au coeur de SDX, notre application étant bien configurée, nous allons pouvoir définir sa logique applicative.

Un peu de XSL et de HTML

Définition de l'interface utilisateur

Nous allons utiliser les réglages prédéfinis dans sitemap.xmap pour rédiger une XSL à même de transformer la XSP traitée par SDX en HTML utilisable sur un navigateur. Mais au préalable, nous allons définir une XSL commune à toute les pages de notre application ; cette XSL contiendra la logique applicative qui sera partagée entre toutes les pages de l'application. Pour faire simple, nous l'appellerons common.xsl et nous la mettrons, naturellement, dans le répertoire xsl.

Cette XSL remplira les fonctions suivantes :

  • générer la structure du document HTML,

  • afficher une "barre de menus" qui proposera des liens vers les différentes pages de l'application,

  • appliquer les modèles spécifiques contenus dans les XSL attachées aux XSP,

  • afficher des informations sur l'utilisateur courant, si tant est que celui-ci soit identifié,

  • proposer, à fins de débogage, des liens permettant de visionner les différentes étapes de traitement de la XSP.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ne pas oublier le namespace SDX -->
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx" 
  exclude-result-prefixes="xsl sdx">
   <!-- doit-on proposer les liens d'aide au débogage ? -->
   <xsl:variable name="debug" select="true()"/>
	<!-- on récupère les infos concernant l'utilisateur -->
	<xsl:variable name="user" select="/sdx:document/sdx:user"/>
	<!-- le modèle pour la "racine" du contexte SDX -->
	<xsl:template match="/sdx:document">
		<!-- le squelette du document HTML -->
		<html>
			<head>
				<title>Mon application</title>
			</head>
			<body>
				<!-- la barre de menus -->
				<xsl:call-template name="menubar"/>
				<!-- les modèles spécifiques à chaque XSP -->
				<xsl:apply-templates/>
				<!-- le bas de page -->
				<xsl:call-template name="footer"/>
			</body>
		</html>
	</xsl:template>
	<xsl:template name="menubar">
	   <!-- le contexte de notre application -->
	   <xsl:variable name="app_context">/mon_app/</xsl:variable>
		<!-- on récupère le contexte SDX -->
		<!-- http://server:port/sdx par exemple -->
		<xsl:variable name="SDX_context" select="/sdx:document/@server"/>
		<!-- on récupère l'URI de la page courante -->
		<!-- http://server:port/sdx/mon_app/index.xsp par exemple -->
		<xsl:variable name="page_URI" select="/sdx:document/@uri"/>
		<!-- on en déduit le contexte de la page dans SDX -->
		<!-- /mon_app/index.xsp par exemple -->
		<xsl:variable name="page_SDX" select="substring-after($page_URI,$SDX_context)"/>
		<!-- on en déduit le contexte de la page dans l'application -->
		<!-- index.xsp par exemple -->
		<xsl:variable name="page_name" select="substring-after($page_URI,$app_context)"/>
		<!-- 
		notre application insèrera un élément <admin_page/>
		pour les pages nécessitant des privilèges d'administrateur,
		s'il est detecté, on est dans une telle page
		-->
		<xsl:variable name="admin_page" select="boolean(/sdx:document/admin_page)"/>
		<table align="center" cellpadding="10">
			<tr>
				<!--
				si on n'est pas dans la page d'index,
				propose un lien vers elle
				-->
				<xsl:if test="not($page_name = 'index.xsp')">
					<td>
						<a href="index.xsp">Index</a>
					</td>
				</xsl:if>
				<!-- 
				si on n'est pas dans la page d'interrogation,
				propose un lien vers elle
				 -->
				<xsl:if test="not($page_name = 'query.xsp')">
					<td>
						<a href="query.xsp">Interrogation</a>	
					</td>
				</xsl:if>
				<!--
				si on n'est pas dans la page d'identification,
				propose un lien vers elle
				 -->
				<xsl:if test="not($page_name = 'login.xsp')">
					<td>
						<a href="login.xsp">Identification</a>
					</td>
				</xsl:if>
				<!--
				si on est identifié hors d'une page d'administration,
				propose une déconnection : on reste sur la même page
				 -->
				<xsl:if test="not($admin_page) and not($user/@anonymous)">
					<td>
						<!-- 
						truc SDX : 
						il suffit de passer un paramètre logout non vide 
						-->
						<a href="{$page_URI}?logout=yes">Fin d'identification</a>
					</td>
				</xsl:if>
				<!-- 
				si on n'est pas sur la page d'ajout de documents
				et si on est administrateur,
				propose un lien vers elle
				-->
				<xsl:if test="not($page_name = 'query.xsp') and $user/@admin='true'">
					<td>
						<a href="upload.xsp">Ajouter des documents</a>
					</td>
				</xsl:if>
			</tr>
		</table>
		<hr/>
		<br/>
	</xsl:template>
	<xsl:template name="footer">
		<br/>
		<hr/>
		<xsl:call-template name="userinfo"/>
		<xsl:if test="$debug">
			<xsl:call-template name="views"/>
		</xsl:if>
	</xsl:template>
	<xsl:template name="userinfo">
		<!-- rien à faire si l'utilisateur est anonyme -->
		<xsl:if test="not($user/@anonymous)">
			<!-- on reconstitue le nom de l'utilisateur -->
			<xsl:variable name="fullname">
				<xsl:if test="$user/@firstname or $user/@lastname">
					(
					<xsl:if test="$user/@firstname">
						<xsl:value-of select="$user/@firstname"/>&#160;
					</xsl:if>
					<xsl:if test="$user/@lastname">
						<xsl:value-of select="$user/@lastname"/>&#160;
					</xsl:if>
					),
				</xsl:if>
			</xsl:variable>
			<!-- on détermine son statut -->
			<xsl:variable name="user_status">
				<xsl:choose>
					<xsl:when test="$user/@superuser='true'">
						le super-utilisateur
					</xsl:when>
					<xsl:when test="$user/@admin='true'">
						un administrateur
					</xsl:when>
					<xsl:when test="$user/@app">
						un utilisateur
					</xsl:when>
					<!-- au cas où on aurait oublié quelque chose... -->
					<xsl:otherwise>bug !</xsl:otherwise>
				</xsl:choose>
			</xsl:variable>
			<!-- et on sort l'info -->
			<div class="user">	
				Vous <xsl:value-of select="$fullname"/> êtes identifié sous le code 
				<span class="user_id"><xsl:value-of select="$user/@id"/></span>. 
				Vous êtes <xsl:value-of select="$user_status"/> de l'application&#160;
				<span class="app_id"><xsl:value-of select="$application"/></span>.
			</div>
		</xsl:if>
	</xsl:template>
	<xsl:template name="views">
		<table>
			<tr>
				<td>Les étapes de cette page : </td>
				<td>1) <a href="{/sdx:document/@uri}2xsp{/sdx:document/@query}">XSP</a>
				</td>
				<td>2) <a href="{/sdx:document/@uri}2sdx{/sdx:document/@query}">SDX</a>
				</td>
				<td>3) <a href="{/sdx:document/@uri}2htm{/sdx:document/@query}">HTM</a>
				</td>
			</tr>
		</table>
	</xsl:template>
	<!-- 
	un template qu'on pourra utiliser
	si un utilisateur cherche à faire des opérations
	pour lesquelles il n'a pas les privilèges suffisants
	-->
	<xsl:template match="nosuchprivileges">
		<div class="privileges">
		Vous n'avez pas les privilèges suffisants pour effectuer cette opération.
		</div>
	</xsl:template>
</xsl:stylesheet>

On le voit, l'essentiel de l'opération consiste à se procurer les différents attributs et sous-éléments de <sdx:document> pour définir la logique applicative en fonction de leurs valeurs. En attendant une documentation plus complète, une idée de l'architecture du contexte SDX est donnée dans un document exemple. L'objectif des étapes suivantes consistera à s'approprier ce contexte.

Retouchons maintenant très légèrement notre index.xsp :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ne pas oublier le namespace XSP -->
<!-- ne pas oublier le namespace SDX -->
<xsp:page language="java" 
	xmlns:xsp="http://apache.org/xsp" 
	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx">
	<sdx:page>
		<!-- 
		si un paramètre logout est présent,
		on se déconnecte
		-->
		<sdx:logout/>			
		<!-- 
		un élément personnalisé
		qui n'est pas dans le namespace SDX
		-->
		<myindex/>
	</sdx:page>
</xsp:page>

Nous devons maintenant rédiger la XSL chargée de traiter le code, ô combien spécifique, de index.xsp. Rappelons que selon sitemap.xmap, elle doit s'appeler index.xsl et se trouver dans le répertoire xsl. Nous allons faire très simple :

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ne pas oublier le namespace SDX -->
<xsl:stylesheet version="1.0" 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
	xmlns:sdx="http://www.culture.gouv.fr/ns/sdx/sdx" 
	exclude-result-prefixes="sdx">
<!-- on insère la XSL commune... -->
	<xsl:include href="common.xsl"/>	
<!-- puis un modèle spécifique pour notre contenu -->	
	<xsl:template match="/sdx:document/myindex">
		<!-- du code HTML -->
		<h1>Mon application</h1>
		<p>Voici ma première application SDX.</p>
		<p>Elle a une barre de menus, mais la plupart des pages sont en construction.</p>
		<p>Ne pas hésiter à utiliser les liens en bas de page pour le débogage&#160;!</p>
	</xsl:template>	
</xsl:stylesheet>

Nous pouvons désormais accéder à l'URL http://serveur:port/sdx/mon_app/index.xsp et... voir le résultat.

Comme prévu, nous avons bien une barre de menus (avec des liens qui ne sont pas encore actifs), notre contenu et nos liens d'aide au débogage qui nous permettent :

  • d'accéder au code source de la XSP,

  • d'accéder au contexte SDX, c'est à dire à la façon dont SDX a interprété la XSP. En plus du traitement lui-même, SDX greffe de nombreuses informations destinées à aider le développeur d'application. A lui de décider s'il veut utiliser cette information et comment il veut le faire...

  • d'accéder au code source du HTML sérialisé par Cocoon.

En revanche, aucune information sur l'utilisateur : c'est normal, nous avons volontairement choisi de masquer l'information quand l'utilisateur est anonyme. C'est aussi pour cela que nous n'avons pas de lien vers la page d'ajouts de documents.

Plus loin avec les XSP

Pistes pour des configurations personnalisées



Auteur : Pierrick Brihaye ( Service Régional de l'Inventaire, DRAC Bretagne, MCC ) - 2003-06-03