Mule est un outil léger d’intégration qui permet de connecter différents systèmes. Par rapport à d’autres ESB plus complexes, Mule est un système rapide à mettre en place pour répondre par exemple aux cas d’usage suivants : lire un fichier CSV et l’importer vers une base de données. rerouter un appel d’un web service vers un envoi d’email, lire des fichiers plats puis les envoyer via FTP, lire le contenu d’une page via HTTP pour ensuite envoyer un email… Comme on le voit, Mule est donc avant tout un système d’interconnexion simple et conçu pour faciliter l’intégration de données dans une application Java.

La version Entreprise de Mule propose un exemple complet d’utilisation du connecteur JDBC. Le code source de cet exemple n’étant pas distribuable ni libre de droits, je vous propose dans cet article un exemple simple dans lequel je vais lire un fichier texte de type CSV, le transformer puis ensuite l’importer dans une base HSQLDB.

Cet exemple vous montre comment écrire un transformer, déclarer le connecteur JDBC de Mule puis enfin mette en oeuvre le tout.

Installation de Mule

Téléchargez la version de Mule 2.1.2 via le site de Mule
Décompressez dans un répertoire l’archive
Déclarer une variable d’environnement MULE_HOME
Mettez à jour le script startMule.sh donné avec l’archive à la fin de cette article pour pointer vers le bon répertoire de Mule.

Mise en place de la base de données

Dans un premier temps, si vous souhaitez tester par vous même, téléchargez l’archive suivante « mule-poc.tar.gz ». Je vous ai préparé une arborescence avec l’ensemble du code source. Le répertoire database contient de quoi créer une base HSQLDB en mémoire mais autonome, car déclarée sous la forme serveur.
HSQLDB nécessite un fichier « sqltools.rc » pour que le mode serveur autorise des connexions sur le serveur. Pour cela, copiez le fichier sqltools.rc vers votre répertoire $HOME (ou C:\Document and Settings\)
Le script startDBServer.sh assume que Java 5 ou 6 se trouve dans le Path, et démarre le serveur HSQLDB. N’étant pas un expert HSQLDB, j’ai fait au plus simple.
Le script createDatabase.sql sera exécuté via le script shell « createDatabase.sh » afin d’importer le contenu du fichier sample.dsv.

Structure simple de la base

L’unique table de la base est vraiment ce qu’il y a de plus simple, il n’y a pas d’index ni de clé primaire. Simplement des types Date, boolean, int et Varchar afin de montrer les capacités de transtypage de Mule.

CREATE TABLE sampletable(id INT, lastUpdate DATE NOT NULL, msg VARCHAR, isValid BOOLEAN);

Le fichier de configuration de Mule

Le fichier de configuration de Mule est le coeur du système. Basé sur Spring, Mule partage des concepts de Spring 2.5 qui en font un système très facile à apprendre. Contrairement à une solution basée sur Spring Integration, vous verrez qu’ici je ne vais écrire qu’une seule classe Java. J’aimerai refaire le même exemple avec Spring Integration plus tard afin de comparer.

L’entête du fichier déclare les namespaces des connecteurs file, stdio, vm et jdbc ainsi que les entêtes pour Spring.

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesource.org/schema/mule/core/2.1"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:spring="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:file="http://www.mulesource.org/schema/mule/file/2.1"
      xmlns:stdio="http://www.mulesource.org/schema/mule/stdio/2.1"
      xmlns:vm="http://www.mulesource.org/schema/mule/vm/2.1"
      xmlns:jdbc="http://www.mulesource.org/schema/mule/jdbc/2.1"
      xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
       http://www.mulesource.org/schema/mule/core/2.1 http://www.mulesource.org/schema/mule/core/2.1/mule.xsd
       http://www.mulesource.org/schema/mule/file/2.1 http://www.mulesource.org/schema/mule/file/2.1/mule-file.xsd
       http://www.mulesource.org/schema/mule/vm/2.1 http://www.mulesource.org/schema/mule/vm/2.1/mule-vm.xsd
       http://www.mulesource.org/schema/mule/stdio/2.1 http://www.mulesource.org/schema/mule/stdio/2.1/mule-stdio.xsd
       http://www.mulesource.org/schema/mule/file/2.1 http://www.mulesource.org/schema/mule/file/2.1/mule-file.xsd
       http://www.mulesource.org/schema/mule/jdbc/2.1 http://www.mulesource.org/schema/mule/jdbc/2.1/mule-jdbc.xsd">

Ensuite je déclare simplement un DriverManagerDataSource en utilisant la base HSQLDB démarrée sur mon serveur. Le login par défaut est ‘sa’ et il n’y a pas de mot de passe.

    <spring:bean id="touilleurDB"
                 class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <spring:property name="driverClassName"
                         value="org.hsqldb.jdbcDriver"/>
        <spring:property name="url"
                         value="jdbc:hsqldb:hsql://localhost"/>
        <spring:property name="username" value="sa"/>
        <spring:property name="password" value=""/>
    </spring:bean>

Je déclare ensuite un connecteur mule de type JDBC avec pour datasource-ref, mon bean « touilleurDB ».
Dans ce connecteur vous pouvez déclarer 3 types de requêtes, je ne déclare ici qu’une requête d’insertion appelée « writeTest ». Cette requête va recevoir une List de Map. Chaque élément de la List correspond à une ligne de fichier. Chaque élément de la ligne séparé par des virgules, sera enregistré dans une Map. J’y reviens plus loin.
La lecture des arguments utilise ici map-payload, l’une des techniques de Mule pour récupérer des couples clés-valeurs facilement.

Une fois cette requête « writeTest » déclarée, elle est alors disponible pour mon service Mule

    <jdbc:connector name="myJdbcConnector"
                    dataSource-ref="touilleurDB"
                    pollingFrequency="1000">
        <jdbc:query key="writeTest"
                    value="INSERT INTO SAMPLETABLE (ID, lastUpdate, msg, isValid) VALUES (#[map-payload:id], #[map-payload:lastUpdated], #[map-payload:msg], #[map-payload:isValid])"/>
    </jdbc:connector>

Je déclare ensuite 1 transformer :


    <custom-transformer name="StringToListOfMap" class="com.innoteria.mule.simple.StringToListOfMap"/>

Le code de la classe StringToListOfMap n’est pas très élégant mais l’intérêt ici est de vous expliquer le principe, pas de vous donner un cours de Java. Mule lit un fichier texte simple et par défaut transforme son contenu en une String. A noter que ce comportement est bien entendu désactivable mais ici il me rend service car mon transformer reçoit directement une String contenant tout le fichier. Je vais ensuite iterer et créer une Map clé-valeur correspondant aux paramètres de ma requête « writeTest » précédemment déclarée.
Enfin je mets chacune de mes Map dans une List pour ensuite utiliser l’encoding transformer map-payload

public class StringToListOfMap extends AbstractTransformer {

    @Override
    protected Object doTransform(Object src, String encoding) throws TransformerException {
        String payload = (String) src;

        if (payload == null) return null;
        StringTokenizer stLine = new StringTokenizer(payload, "\n");

        String line;
        Map m;
        List resultList=new ArrayList();
        StringTokenizer st;
        while (stLine.hasMoreTokens()) {
            line=stLine.nextToken();
            st=new StringTokenizer(line,",");
            m = new HashMap();
            m.put("id", st.nextToken());
            m.put("lastUpdated", st.nextToken());
            m.put("msg", st.nextToken());
            m.put("valid", st.nextToken());
            resultList.add(m);

        }
        return resultList;

    }
}

Pour terminer la configuration de mon service Mule, je déclare ensuite un connecteur de type file. L’instance de Mule va scruter le répertoire out/source. Dès qu’un fichier y sera déposé, il sera lu et transformé ensuite via notre transformer « StringToListOfMap » dont nous venons de voir le code.

<model name="fromFileToDatabase">
  <service name="myImportService">
    <inbound>
       <file:inbound-endpoint path="/Users/nicolas/Dev/mule-poc/out/source"
                  pollingFrequency="3000" fileAge="5000"
                  moveToDirectory="/Users/nicolas/Dev/mule-poc/out/destination"
                  transformer-refs="StringToListOfMap">
       </file:inbound-endpoint>
   </inbound>
   ...
   ...

Je n’utilise aucuns composants particuliers. Il est temps de déclarer maintenant un outbound endpoint de type JDBC, utilisant notre query « writeTest ». La seule chose un peu spéciale est que j’utilise ici un list-message-splitter-router. Celui-ci prend en entrée une List d’élément et va automatiquement itérer cette List pour passer ensuite chacun des éléments au « jdbc:outbound-endpoint » en l’occurence une Map.

En cas d’exceptions je route les messages vers la sortie standard. Je pourrai aussi sauver vers un fichier les entrées invalides de mon fichier CSV.

 <echo-component/>
   <outbound>
        <list-message-splitter-router>
            <jdbc:outbound-endpoint queryKey="writeTest">
                <payload-type-filter expectedType="java.util.Map"/>
            </jdbc:outbound-endpoint>

            <payload-type-filter expectedType="java.util.List"/>
          </list-message-splitter-router>
        </outbound>

        <default-connector-exception-strategy>
                <outbound-endpoint address="stdio://OUT"/>
        </default-connector-exception-strategy>

     </service>
  </model>
</mule>

Une fois le tout compilé, je démarre mon instance de Mule via le script « startMuleServer.sh » en l’exécutant dans le répertoire bin.
A l’execution, si je dépose le fichier « testDatabase.txt » dans le répertoire out/source, Mule le charge et le message suivant s’affiche dans la console :

INFO 2008-12-23 15:24:48,927 [myImportService.2] org.mule.component.simple.LogComponent:
********************************************************************************
* Message received in service: myImportService. Content is: '0,2008-12-12,[Un *
* exemple de message tr?s simple],true *
* 1,2008-11-12,[Un autre exemple pour tester],fa...[100 of 141]' *
********************************************************************************

Un select dans la table retourne les 3 lignes du fichier testDatabase.txt comme attendu

Conclusion
Dans cet exemple nous avons vu comment lire un fichier texte simple, comment transformer son contenu pour ensuite insérer directement des entrées dans une base de données. Mule Enterprise Edition propose des connecteurs avancés pour les traitements par Batch, plus efficace que l’import utilisé ici. Par ailleurs il est aussi possible de configurer les pools de connections en se basant sur les méthodes Spring standards.
Mule est une solution qui évite l’écriture de code d’infrastructure, par essence du code souvent sans valeur ajouté. Cependant comme on peut le remarquer ici, il y a rapidement un besoin d’ajouter un peu de logique, ne serait-ce que pour la validation. Et cette logique s’écrit avec du code Java. J’ai aussi en stock un exemple avec Apache Camel, préparé pour Devoxx, que je vous proposerai prochainement dans un article sur le Touilleur.

Code source de l’exemple :
mule-poc-touilleur.tar.gz