Tutoriel XGQL

Rémi Masson

Dans un précédent article , nous avions réalisé une brève introduction à l'API XGQL autour d'une étude de cas. Ici, nous allons plus nous intéresser au formalisme du langage et aux nouveautés de la version 2.0

- Titre : Tutoriel XGQL

- Auteur : Rémi Masson

- Parution : 13 juillet 2007

- Licence : Copyright ® 2007 Remi Masson. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Introduction

- 2. Au commencement fut le namespace

- 3. Les variables

- 3-1. Opérateurs arithmétiques

- 3-2. Affectation de variable

- 3-3. Opérateurs de chaînes

- 3-4. Variables externes

- 4. Les instructions de contrôle

- 4-1. Les tests conditionnels

- 4-1-1. Instruction xgql:if

- 4-1-2. La branche conditionnel

- 4-1-3. Opérateurs de comparaisons

- 4-1-4. Opérateurs logiques

- 4-2. Les boucles

- 5. Les procédures

- 6. Les sources de données

- 6-1. Travailler sur les données récupérées

- 7. Interroger des bases relationnelles

- 7-1. Configuration

- 7-2. CREATE, INSERT, UPDATE, DELETE et DROP

- 7-3. SELECT

- 7-3-1. Traitement de requête

- 7-3-2. Branche conditionnelle

- 7-4. Travailler sur les données provenant des bases relationelles

- 8. Les extensions de langage

- 9. Les librairies

- 10. XGQL et le web

- 10-1. Cookies

- 10-2. Requests

- 10-3. Sessions

- 11. Conclusion

- 12. Remerciements

1 - Introduction

XGQL est un langage procédural utilisant le formalisme XML permettant d'agréger de nombreuses sources de données :

- Plusieurs bases de données (depuis la version 2.0)

- Flux XML, SOAP, Texte

- Session, request et cookie en mode web (depuis la version 2.0)

Via les protocoles suivants :

- File system (Local ou points de montage)

- HTTP

- FTP

- TCP

- JDBC

Nous allons voir comment utiliser ce formalisme.

2 - Au commencement fut le namespace

Un script XGQL est déclaré dans le namespace <xgql:myTag/>. C'est pour cela qu'un listing XGQL devra débuter de la façon suivante :

<xgql:root xmlns:xgql="http://www.symeria.com/xgql/">
</xgql:root>

3 - Les variables

Une variable XGQL se déclare de la façon suivante :

<xgql:var name="maVariable">Ma variable</xgql:var>

Par défaut, une variable n'a pas de type, elle peut être de type :

- Entier

- Chaîne de caractères

- XML

Contrairement au XSLT, il est possible de modifier le contenu d'une variable :

<xgql:var name="maVariable">$maVariable et mon nouveau contenu</xgql:var>

Si vous déclarez une variable dès le début du traitement, celle-ci sera globale. Par contre, si vous la déclarez dans une condition, une fonction ou une boucle, elle sera locale.

3-1 - Opérateurs arithmétiques

Pour travailler sur les variables, XGQL permet d'utiliser les opérateurs mathématiques de base.

Opérateur

Description

+

Addition

-

Soustraction

*

Multiplication

/

Division

Mod

Modulo

3-2 - Affectation de variable

L'affectation de variable s'effectue de la même façon que la déclaration de celle-ci :

<xgql:var name="maVariable">1</xgql:var>

A ce moment, maVariable vaut 1.
Il est possible d'utiliser tous types d'opérateurs mathématiques :

<xgql:var name="maVariable"><xgql:process>$maVariable + 1</xgql:process></xgql:var>
<!--maVariable est à 2 -->
<xgql:var name="maVariable"><xgql:process>$maVariable * $maVariable </xgql:process></xgql:var>
<!--maVariable est à 4 -->
<xgql:var name="maVariable"><xgql:process>$maVariable mod $maVariable </xgql:process></xgql:var>
<!--maVariable est à 0 -->

L'instruction xgql:process indique qu'il s'agit d'une opération. Sinon, il s'agirait d'une concaténation de chaînes.
xgql:process : permet aussi de réaliser des opérations XQuery, comme par exemple :

<xgql:var name=' maVariable'>
<xgql:process>
let $doc:=document{<account id='42'><name>Aurel</name><firstnm>Marc</firstnm><log>martin_p</log><pwd>mypassword</pwd></account>}
return $doc/account/data(@id)
</xgql:process>
</xgql:var>

Ici, maVariable sera à 42, puisqu'il s'agit de la valeur de l'attribut id de l'élément account.

3-3 - Opérateurs de chaînes

Le but premier du script XGQL étant de générer du XML, il est évidemment possible de travailler sur les chaînes.

<xgql:var name="maVariable">Ma variable</xgql:var>

maVariable vaut 'Ma variable'.

<xgql:var name="maVariable">$maVariable et mon nouveau contenu</xgql:var>

maVariable vaut 'Ma variable et mon nouveau contenu'. Bien entendu, les possibilités de traitement de chaînes sont bien plus poussées, puisqu'il est également possible d'accéder à l'ensemble des fonctionnalités XQuery pour le traitement des chaînes. Exemple de concaténation de chaîne en XQuery :

< !-- Renvoi montestmontest -->
<xgql:var name="maVariable"><xgql:process>let $doc := string('montest') return concat($doc, $doc)</xgql:process></xgql:var>

Mais nous ne nous étendrons pas plus sur les fonctions XQuery car ces dernières sont déjà décrites dans d'autres tutoriaux.

3-4 - Variables externes

xgql:param est la commande qui permet à un script XGQL d'être initialisé par des paramètres extérieurs quand il est utilisé en mode API ou generator cocoon.
Exemple de paramètre :

<xgql:param name="externalParam"/>

Cette déclaration est à placer au début du script.

4 - Les instructions de contrôle

Les instructions de contrôle constituent le squelette du script XGQL. Ce sont ces instructions qui procurent à XGQL sa capacité décisionnelle permettant de contrôler les flux d'exécution du programme.
Les instructions capables de modifier l'exécution du script en fonction des données métiers sont les suivantes :

- xgql:if : exécute un bloc de code lorsque certaines conditions sont satisfaites.

- xgql:while : exécute plusieurs fois un même bloc de code.

4-1 - Les tests conditionnels

4-1-1 - Instruction xgql:if

L'une des fonctionnalités les plus importante est l'instruction xgql:if. L'instruction xgql:if exécute les instructions si la condition renvoie la valeur booléenne true.
Exemple de délcaration d'un bloc conditionnel :

<xgql:if test="1 = 1">
instruction
</xgql:if>

Ces instructions peuvent être imbriquées :

<xgql:var name="condition1">true</xgql:var>
<xgql:var name="condition2">2</xgql:var>
<xgql:if test="'$condition1' = 'true'">
<xgql:if test="'$condition2' = '2'">
instruction
</xgql:if>
</xgql:if>

4-1-2 - La branche conditionnel

Pour simplifier l'écriture des scripts, il est possible d'exécuter un bloc d'instructions spécifiques si le résultat de la condition est à la valeur booléenne false.
Avec le script suivant :

<xgql:root xmlns:xgql="http://www.symeria.com/xgql/">
<xgql:var name="condition1">true</xgql:var>
<xgql:var name="condition2">2</xgql:var>
<result>
<xgql:if test="'$condition1' = 'true'">
<xgql:if test="'$condition2' = '3'">
<test1/>
<xgql:else>
<test2/>
</xgql:else>
<test3/>
</xgql:if>
<xgql:else>
<test4/>
</xgql:else>
<test5/>
</xgql:if>
</result>
</xgql:root>

on obtient le résultat suivant :

<result>
<test2></test2>
<test5></test5>
</result>

Dans le deuxième saut conditionnel, il est possible de mettre des instructions après le xgql:else, par contre, il n'est pas possible de mettre plusieurs branches conditionnelles dans le même xgql:if.

Un exemple qui ne fonctionnera pas :

<xgql:root xmlns:xgql="http://www.symeria.com/xgql/">
<xgql:var name="condition1">true</xgql:var>
<xgql:var name="condition2">2</xgql:var>
<result>
<xgql:if test="'$condition1' = 'true'">
<xgql:if test="'$condition2' = '3'">
<test1/>
<xgql:else>
<test2/>
</xgql:else>
<test3/>
<xgql:else>
<test2/>
</xgql:else>
</xgql:if>
<xgql:else>
<test4/>
</xgql:else>
<test5/>
</xgql:if>
</result>
</xgql:root>

4-1-3 - Opérateurs de comparaisons

Les opérateurs de comparaisons servent à comparer deux valeurs (variable et constante). Ils renvoient toujours un booléen.

Opérateur

Description

= ou eq

Egal à

!=

Différent de

lt ou <

Inférieur à

gt ou >

Supérieur à

<=

Inférieur ou égal à

>=

Supérieur ou égal à

4-1-4 - Opérateurs logiques

Les opérateurs logiques peuvent être combinés entre eux pour obtenir des résultats booléens plus fins.

Opérateur

Description

and

Et logique

or

Ou logique

not()

Négation

4-2 - Les boucles

La boucle xgql:while est une expression proche du xgql:if. Il évalue une expression booléenne et exécute un bloc de code aussi longtemps que l'expression booléenne renvoie true. Il n'existe pas de branchement conditionnel sur le xgql:while.

<xgql:var name="condition1">0</xgql:var>
<xgql:while test='$condition1 lt 5'>
$condition1
<xgql:var name="condition1"><xgql:process>$condition1 + 1</xgql:process></xgql:var>
</xgql:while>

Cet exemple renvoie le résultat suivant : 1234

5 - Les procédures

Une procédure est un bloc de script qui peut être invoqué à tout moment. Une procédure accepte un ou plusieurs paramètres, mais ne peut pas renvoyer de valeur.
Une procédure se caractérise par trois éléments :

- la balise xgql:function

- un nom de fonction (identifié par l'attribut name)

- un bloc d'instruction

Une procédure peut intéragir avec les variables déclarées en global dans le programme.

<xgql:var name="var1">5</xgql:var>
<xgql:function name="func">
<xgql:var name="var1">$var1 - 1</xgql:var>
</xgql:function>

L'appel d'une procédure se fait de la façon suivante :

<xgql:call name="func"/>

Attention, si plusieurs procédures ont le même nom, c'est la dernière procédure avant l'appel qui sera interprété.

6 - Les sources de données

Un des principaux avantages d'XGQL, en plus d'accéder à toutes les fonctionnalités XQuery, est de pouvoir gérer simultanément plusieurs sources de données :

- FTP

- http

- FILE

Les données sont accessibles avec l'instruction xgql:datasource.

Accès à un document via FTP

<xgql:datasource>ftp://username:password@ftp.whatever.com/file.zip;type=i </xgql:datasource>

Accès à un document via HTTP

<xgql:datasource>http://www.symeria.com </xgql:datasource>

accès à un document sur le file system

<xgql:datasource>C:/My Documents/ALetter.html</xgql:datasource>

Lors d'une utilisation simple, le flux du datasource sera transcrit directement en résultat du script. Pour travailler sur ce flux, il suffit juste d'encapsuler le xgql:datasource dans une déclaration de variable.

6-1 - Travailler sur les données récupérées

Bien entendu, il est possible de travailler sur ces datasources en les récupérant dans une variable. Considérons le fichier test.xml dans le répertoire suivant C:\myTest :

<?xml version="1.0" encoding="UTF-8"?>
<account id='42'>
<name>Aurel</name>
<firstname>Marc</firstname>
<login>martin_p</login>
<pwd>mypassword</pwd>
</account>

Pour récupérer son contenu dans une variable XGQL, il suffit de faire la déclaration suivante :

<xgql:var  name="maVariable"><xgql:datasource>C:\myTest\test.xml</xgql:datasource></xgql:var>

Il est alors possible de travailler sur cette variable, donc sur le XML qu'elle représente :

<xgql:process>let $doc:= document{$maVariable} return $doc/account/data(@id)</xgql:process>

Dans cette exemple, xgql:process va renvoyer 42 comme résultat. Ceci illustre la facilité avec laquelle il est possible d'utiliser des sources externes de données afin de la manipuler avec XGQL !

7 - Interroger des bases relationnelles

Depuis la version 2.0 de XGQL, en plus de gérer de nombreuses sources de données, il est également possible de travailler sur plusieurs bases de données relationnelles simultanément (jusqu'à 6).

7-1 - Configuration

Le fichier xgql.properties est le fichier de configuration XGQL. C'est dans ce fichier que sont spécifiés les données de connections aux bases de données :

- idConnect : nom de la connexion. Ce nom sera utilisé tout au long du script XGQL pour indiqué quel pool de connexion est utilisé

- className : nom du driver JDBC à utiliser pour se connecter

- url : url d'accès à la base

- login : identifiant de la base

- password : mot de passe pour accéder à la base

Ces informations sont à configurer autant de fois qu'il y a de bases de données à interroger. Exemple de fichier xgql.properties.

# La première connexion est celle par défaut
xgql.connectId=myHsql
xgql.className=org.hsqldb.jdbcDriver
xgql.url=jdbc:hsqldb:file:testdb
xgql.login=SA
xgql.password=

# Les connexion suivantes peuvent actuellement aller de 1 à 5
xgql.1.connectId=mySql
xgql.1.className=com.mysql.jdbc.Driver
xgql.1.url=jdbc:mysql://localhost:3308/valkyrie
xgql.1.login=root
xgql.1.password=pwd

Ici, la configuration est prévue pour interroger deux bases.

7-2 - CREATE, INSERT, UPDATE, DELETE et DROP

L'exécution d'une requête ne retournant pas d'informations (create, insert, update, delete ou drop) se décompose en deux parties :

- déclaration de la requête dans une variable

- exécution de la requête sur une base précise. Par défaut, c'est la première du xgql.properties qui est prise en compte

Si ces deux étapes ne sont pas respectées et que la requête est directement inclus dans la balise d'exécution, le script ne pourra pas lancer cette opération.

Création d'une table :

<xgql:var name="create">CREATE TABLE testXGQL (id integer, name varchar(20))</xgql:var>
<xgql:execute name="create" connectionId="myHsql"/>

Insertion de données :

<xgql:var name="insert1">INSERT INTO testXGQL VALUES (1, 'test 1')</xgql:var>
<xgql:execute name="insert1" connectionId="myHsql"/>

Suppression de données :

<xgql:var name="delete">DELETE FROM testXGQL WHERE id=2</xgql:var>
<xgql:execute name="delete" connectionId="myHsql"/>

Mise à jour de données :

<xgql:var name="delete">UPDATE testXGQL SET id=4, name='test 4' WHERE id=3</xgql:var>
<xgql:execute name="delete" connectionId="myHsql"/>

Suppression de la table :

<xgql:var name="drop">DROP TABLE testXGQL</xgql:var>
<xgql:execute name="drop" connectionId="myHsql"/>

7-3 - SELECT

7-3-1 - Traitement de requête

Une requête de sélection se réalise de la même que les requêtes déjà vues précédemment, par le xgql:execute. La gestion des éléments retour se fait par le biais du xgql:row et du xgql:column.

<xgql:var name="select">SELECT * FROM testXGQL</xgql:var>
<xgql:execute name="select" connectionId="myHsql">
<xgql:row>
<xgql:var name="id">
<xgql:column name="id" wrap="false"/>
</xgql:var>
</xgql:row>
</xgql:execute>

Le xgql:row définit le bloc sur lequel le script va itérer pour chaque tuple résultat de la requête. xgql:column indique, via l'attribut name, quel est le nom colonne dont on souhaite obtenir la valeur. L'attribut wrap définit le format de cette sortie. Si wrap est à true, la valeur sera renvoyée dans une balise du nom de la colonne, sinon, c'est uniquement la valeur qui est renvoyé.
Dans l'exemple précédent,

<xgql:column name="id" wrap="false"/>

renvoie la valeur de l'id, alors que

<xgql:column name="id" wrap="true"/>

renvoie

<id>value</id>

En fonction du type de sortie ou du traitement choisi, il est donc possible de générer de simples données textes ou un flux XML depuis une ou plusieurs bases de données. Exemple de sortie en texte :

<xgql:var name="select">SELECT * FROM testXGQL</xgql:var>
<xgql:execute name="select" connectionId="myHsql">
<result>
<xgql:row>
<xgql:var name="id"><xgql:column name="id" wrap="false"/></xgql:var>
<xgql:var name="test"><xgql:column name="name" wrap="false"/></xgql:var>
Id is <b>$id</b> and value is <b>$test</b><br/>
</xgql:row>
</result>
</xgql:execute>

7-3-2 - Branche conditionnelle

Un peu comme le xgql:if, le branchement conditionnel est possible sur le xgql:execute avec la balise xgql:norow. Le bloc d'instruction de cette balise est interprété si la requête ne renvoie aucun résultat.

<xgql:var name="select">SELECT * FROM testXGQL</xgql:var>
<xgql:execute name="select" connectionId="myHsql">
<xgql:row>
<xgql:var name="id"><xgql:column name="id" wrap="false"/></xgql:var>
</xgql:row>
<xgql:norow>Pas de resultat</xgql:norow>
</xgql:execute>

Il ne peut pas y avoir plusieurs instructions xgql:row dans le même xgql:execute. Le résultat du xgql:execute est du XML. Les données sont donc pré-formatées.

7-4 - Travailler sur les données provenant des bases relationelles

Les bases relationnelles constituent bien sûr un datasource.
Lors de l'exécution d'un SELECT, on constate que le résultat est formaté directement en XML.
Il est donc possible d'encapsuler ce résultat dans une variable et de manipuler le XML obtenu grâce à du XQuery comme nous l'avons vu précédement.
Cela signifie également qu'il est très facile d'interpoler des données provenant des sources de données XML avec les données provenant de sources relationnelles.

8 - Les extensions de langage

XGQL reste un langage très ouvert au travers de nombreuses extensions fournies ou non en natif. Par exemple, l'extension web est fournie en natif avec la version 2.0, incluant toute une série d'instructions supplémentaires pour la gestion du web (cookies, sessions, etc.)
Pour inclure de nouvelles extensions, il suffit de le déclarer de la façon suivante :

<xgql:extension name="SaxParserExtnExample" package="my.own.package"/>

name est le nom de l'extension et l'attribut package indique le nom du package de l'extension. Par défaut, le nom du package est 'org.symeria.xgql.parser.extension'
Il existe de nombreuses extensions de langage, tel que XGQL:DB2XML qui va rendre transparente la gestion de données XML dans un base de données relationnelles.

9 - Les librairies

Nous avons vu précédemment qu'il était possible de réaliser des procédures pour simplifier et factoriser le code. Dans un souci de rentabilité, il est assez possible d'importer des librairies XGQL :

<xgql:datasource>C:Documents and Settings/yourpath/SWAS_XGQL/webapps/XGQL/myLib.xgq</xgql:datasource>

Les librairies peuvent être accédées aussi par http et ftp.

10 - XGQL et le web

XGQL dans sa version SWAS (API XGQL, serveur Jetty et base Hsql, le tout préconfiguré pour fonctionner en servlet) s'adapte au fonctionnalité web, avec la gestion :

- des cookies

- des requests

- des sessions

Néanmoins, l'utilisation de XGQL sous cette forme n'est pas la plus optimum et n'est pas recommandée pour la réalisation de sites complexes ou professionnels.

10-1 - Cookies

Création d'un cookie :

<xgql:cookie id="test" expiration="100000">MyCookie</xgql:cookie>

Récupération de la valeur d'un cookie:

<xgql:cookie id="test"/>

A ce niveau, la valeur est juste lue. Pour être traitée, elle doit être placée dans une variable.

10-2 - Requests

Récupération d'une valeur passée en reqests :

<xgql:request name="aRequestKeyName"/>

10-3 - Sessions

Création d'une valeur en session :

<xgql:session name="aSessionKeyName">My session Value</xgql:session>

Récupération de la valeur en session:

<xgql:session name="aSessionKeyName"/>

A ce niveau, la valeur est juste lue. Pour être traitée, elle doit être placée dans une variable.

11 - Conclusion

En conclusion, il n'y aura plus qu'une chose à préciser, mis à part ce petit détail : "Où trouver les dernières distributions XGQL ?"
Sur le site officiel XGQL est à l'adresse suivante XGQL. Différentes versions de l'API XGQL y sont disponibles (API, source, SWAS, etc.) ainsi que de nombreux exemples.

12 - Remerciements

Je tiens à remercier toute l'équipe responsable de la rubrique XML, et tout particulièrement Erwy et Swoög et également Pierre Martins, pour leur aide. Ainsi que Arnaud F. pour sa relecture.