Récupération FTP de fichiers par lot avec un port d’envoi (NSoftware)

Bastien BRAILLY-VIGNAL
Catégorie : BizTalk
18/07/2017

AVANT DE COMMENCER

 

Cette solution a été réalisée sur un BizTalk Server 2013 R2 et nécessite l’installation des adapteurs NSoftware v4.
Plus d’informations : https://www.nsoftware.com/adapters/biztalk/

 

CONTEXTE

 

Dans le cadre d’un projet récent, nous avons rencontré une problématique inhabituelle chez un client.
En voici le bref exposé :
Le partenaire de ce dernier (que nous appellerons ici « Y ») fournit à notre client (ici « X ») une liste de fichiers à heure régulière via un emplacement FTP.
La principale problématique réside dans le fait que X doit, par rapport à l’ensemble des fichiers présents sur le FTP à un instant T, récupérer l’ensemble des fichiers ET les intégrer dans une base de données Oracle dans un ordre bien précis. Ici, l’ordonnancement des messages se fait sur une date fonctionnelle contenue dans le corps du message lui-même.
A priori (et dans un monde idéal !), ce genre de scénario devrait être géré en amont :par exemple en envoyant les messages déjà dans l’ordre et via un protocole en mesure de garantir l’ordre (ex : Microsoft ServiceBus, MSMQ, IBM MQSeries…).
Mais grâce à l’adapteur FTP fourni par NSoftware (https://www.nsoftware.com/adapters/biztalk/), ce scénario peut tout à fait être géré avec BizTalk.
En voici le détail :

 

DESCRIPTION DE LA SOLUTION

 

Nous allons ici exploiter une fonctionnalité assez méconnue de l’adapteur FTP NSoftware : l’option « DownloadSingleFile » (propriété « Other settings » de l’adapter).
Je vous invite à parcourir la documentation de l’adapteur (celle-ci est plutôt bien fournie et souvent accompagnée d’exemples) : https://cdn.nsoftware.com/help/EAB/bt/FTPConfig.htm
Comme le dit très bien la documentation, cette option, utilisée sur un port d’envoi, permet de télécharger un fichier plutôt que l’uploader.
Ce qui est inhabituel est d’utiliser un port d’envoi pour réceptionner des messages et non un port de réception.
Pour cela, le port d’envoi doit être utilisé en « solicit-response ».
Nous utiliserons d’abord un DIR qui va retourner la liste des fichiers présents sur le FTP puis une boucle qui récupèrera les fichiers 1 par 1.
Voici les étapes de notre flux :

    1. Déclencher régulièrement le processus pour récupérer les fichiers ;
    2. Interroger le serveur FTP pour récupérer la liste des fichiers présents et qui devront donc être récupérés dans un seul lot ;
    3. Récupérer unitairement chaque fichier.
    4. Facultatif : traitement spécifique à notre problématique

 

1/ Déclenchement de l’orchestration

Nous sommes dans une situation où nous devons planifier le déclenchement du processus de récupération des fichiers.
Voici comment, dans notre cas, nous gérons cette planification avec le « Task Scheduler Windows ».
Il existe plusieurs façons de déclencher une orchestration mais dans notre cas, l’orchestration s’activera de la manière suivante :

  • Création d’une tâche dans le « Task Scheduler Windows » qui va, à intervalles donnés, déclencher le lancement d’un programme ;
  • Création d’un fichier de script de commande Windows (.cmd) qui va générer un message dans un répertoire en particulier :

Code exemple :
set FilePath=D:\Data\EAI\…\Scheduler
echo ^<Trigger xmlns^= »https://Monnamespace.Trigger »^> ^<Value^>1^</Value^> ^</Trigger^>>%FilePath%\triggerOrc.xml

  • Création d’une receive location dans BizTalk qui va écouter ce répertoire ET qui sera liée à l’orchestration.

Note : il serait tout à fait possible d’utiliser ici l’adapteur ScheduleTask. Plus d’infos : https://biztalkscheduledtask.codeplex.com/

 

2/ Liste des fichiers présents sur l’emplacement FTP

*Début de l’orchestration :

 

 

  1. Port logique correspondant à l’emplacement de réception précédemment créé ;
  2. Shape Receive du message Trigger. Bien veiller à mettre Activate à true ;
  3. Shape Send. Envoi du message Trigger au port logique d’envoi. Ici, on envoie un message de type Trigger (modélisé au préalable) mais on pourrait tout à fait envoyer n’importe quel type de message ;
  4. Port logique d’envoi en Request-Response dont le message de réponse attendu est de type System.Xml.XmlDocument (la configuration du port physique dans BizTalk est détaillée plus loin) ;
  5. Shape Receive du message de réponse. Pour information, le format de ce message de réponse est fourni par l’adapteur NSoftware et structuré de cette façon :

    Comme on peut le constater, ce message contient le répertoire principal ainsi que la liste des fichiers présents et leurs propriétés.
  6. Shape expression qui récupère le nombre de fichiers dont voici le code :

intFilesNumber = xpath(msgDirResponse, »count(Directory/File) »);

*Configuration du port d’envoi :
Comme dit précédemment, l’adapteur du port d’envoi doit être nsoftware.FTP v4. Pas de configuration particulière pour les 2 pipelines (PassThru), pas de maps, pas de filtres (car lié à l’orchestration).

Ci-dessous la configuration de l’adapteur :

 

L’élément le plus important ici est bien sûr : ListDirectory=true à mettre dans « Other ». Cette propriété permet d’indiquer à l’adapter que nous voulons déclencher un « DIR » dans le répertoire FTP distant. L’adapter sait qu’il doit nous retourner non pas des fichiers mais une liste des fichiers présents dans le répertoire.

*Boucle pour récupérer les messages 1 par 1 :
– Suite de l’orchestration

 

 

  1. Map permettant d’initialiser le message de type ListeItemsTemp (juste un « true » sur le nœud Root) ;
  2. Boucle sur tous les fichiers trouvés précédemment avec le DIR dont voici le code :
  3. Shape expression qui va définir le nom du fichier à récupérer sur le FTP dont voici le code :
  4. Construction du message msgTriggerGetMsg. Ici : simple copie du msgTrigger auquel on rajouter dans le contexte le nom du fichier à récupérer. Voici le code de la shape Message Assignment :
  5. Shape Send. Envoi du message msgTriggerGetMsg au port logique d’envoi.
  6. Port logique d’envoi en Request-Response dont le message de réponse attendu est dans notre cas de type Item (la configuration du port physique dans BizTalk est détaillée plus loin) ;
  7. Shape Receive du message de réponse ;
  8. Map permettant de construire notre message msgListeItems au fur et à mesure que nous récupérons nos messages de type Item (unitairement). Sans rentrer dans le détail, cette map à double entrée (msgItem + msgListeItemsTemp) permet d’abord de copier le msgListeItemsTemp rempli à mesure que l’on passe dans la boucle (mass copy) et d’ajouter le msgItem (simples liens directs). On peut dire donc que c’est ici qu’on « stocke » tous les messages récupérés ;
  9. Construction du message msgListeItemsTemp. Voici le code de la shape Message Assignment :
  10. Shape expression qui va décrémenter le nombre de fichiers (sur lequel on boucle). Code :

Remarque : ce n’est pas nécessaire dans notre cas mais on pourrait tout à fait « stamper » les messages du même lot en créant une propriété promue custom qui contiendrait le numéro du lot par exemple. Tout comme le fait l’InterchangeId lors d’un débatching.
– Configuration du port d’envoi
Même principe que pour le précédent port d’envoi, l’adapteur de celui-ci doit être nsoftware.FTP v4. Pas de configuration particulière pour les 2 pipelines (PassThru pour le pipeline d’envoi et XMLReceive en réception), pas de maps, pas de filtres (car lié l’orchestration).
Ci-dessous la configuration de l’adapteur :


Comme tout à l’heure, l’élément le plus important ici est dans « Other » :

     DownloadSingleFile=true

    DeleteAfterDownload=true

La propriété DownloadSingleFile=true indique à l’adapter que l’on souhaite récupérer un fichier dont le nom est précisé dans la propriété de contexte
nsoftware.BizTalk.FTP.RemoteFile.

La propriété DeleteAfterDownload=true indique que l’on souhaite supprimer le fichier après l’avoir téléchargé.

*Facultatif : traitement spécifique à notre problématique
– Trier les messages
Pour rappel, le client impose l’intégration des messages du lot récupéré dans un ordre bien précis (basé sur une date fonctionnelle contenue dans le message).
Pour faire cela, nous utilisons simplement une map avec comme message source, notre msgListeItemsTemp et en message destination un msgListeItemsOrdered, tous les 2 de type ListeItems.

 

Cette map réalisée en full xslt ressemble à quelque chose comme cela :

<s0:ListeItemsEnvelope>

      <xsl:for-each select=« s0:Item« >

        <xsl:sort select=« MyFunctionalDate data-type=« number » order=« ascending« />

        <xsl:element name=« s0:Item« >

          <xsl:copy-of select=« ./*«  />

        </xsl:element>

      </xsl:for-each>

    </s0:ListeItemsEnvelope>

– Débatcher les messages
Ici, le choix a été fait de débatcher les messages en appelant un pipeline directement dans l’orchestration.
Sachant qu’il est toujours préférable de débatcher un message via un pipeline qui est appelé directement dans l’orchestration.
Voici tout de même la capture d’écran :

 

On obtient ainsi à la fin de la shape Construct message un message unitaire de type Item.

*Traitement spécifique et envoi des messages 1 par 1 :
A partir de là, nous sommes libres d’effectuer n’importe quel traitement / transformation sur notre message Item.
Dans notre cas, comme vous pouvez le constater sur la capture d’écran ci-dessous, nous allons simplement appliquer une map (2) selon un certain statut (1) récupéré au préalable.
Puis, comme nous sommes toujours dans la boucle qui débatche notre message de type msgListeItems, nous allons envoyer les messages 1 par 1 (3) sur un port d’envoi (4). Dans notre cas, le binding est en « Specify later » et le port d’envoi physique de type WCF-OracleDB.

CONCLUSION

 

Avec cette solution, nous pouvons donc garantir à notre client qu’au moment où l’orchestration va se déclencher, tous les fichiers présents sur l’emplacement FTP seront listés, pris ensemble et traités par la suite de la façon de notre choix.