UPDATE 01-2012: Le projet NACA a donné naissance à
Eranea, société dédiée à la migration 100% automatisée de grandes
applications métier vers Java et Linux. Voir www.eranea.com ou email à contact@eranea.com pour plus d'informations
_______________________________________________________________________
Mise à jour: le code source des outils du transcodage NACA est maintenant en Open Source. Voir
http://media-tech.blogspot.com/2008/07/les-outils-du-projet-naca-de-publicitas.html.
[Introduction: cet article fait partie d'une série qui décrit le projet NACA ayant conduit au remplacement d'un mainframe IBM sous MVS/OS390 par des serveurs Intel sous Linux. Le projet a été lancé en Janvier 2003 et s'est terminé avec succès au 30 Juin 2007.
Il a été réalisé volontairement de manière 100% iso-fonctionnelle (i.e. sans aucune modification pendant et par le transcodage) pour l'application et a engendré la conversion automatisée de 4 millions de lignes de Cobol vers leur équivalent Java. L'économie en cash-outs - paiements externes - est de plus de 85% de leur montant annuel = initial d'environ 3 millions d'euros annuels
Articles déjà parus:
]
L'enjeu majeur du projet NACA était sans aucun doute le transcodage de l'applicatif "maison" de PubliGroupe appelée PUB 2000 (4 millions de lignes de Cobol - applications transactionnelles sous CICS + programmes batches - base de données relationnelles DB2 + procédures stockées sous DB2 en Cobol -750'000 transactions/jour).
Pour les développeurs qui veulent juger par la structure, la palette d'outils développés pour le projet et décrits dans la suite représente:
- transcodeur lui-même (NacaTRANS) : 83'000 lignes de code source Java; 688 classes.
- environnement d'exécution (NacaRT) des programmes transcodés: 153'000 lignes; 890 classes.
- outils de test automatiques (NacaTools): 16000 lignes; 128 classes.
A cela, il faut ajouter les codes sources suivants: Java lui-même,
Apache Tomcat, l'IDE Eclipse,
Berkeley Db java Edition, quelques composants
l'immense librairie Apache Commons,
Apache Xerces comme parser XML,
Apache XSLTC, quelques petits composants de
Apache Struts , Apache Log4J ,
Apache Ant,
CVS
Pourquoi transcodage = enjeu majeur ? Parce que finalement quand on passe d'un système à l'autre, on retrouve en général les mêmes composants (couches de télécommunications, système de contrôle de la machine, gestionnaire de sécurité, moniteur transactionnel, séquenceur des travaux batches, etc…) : les nouveaux composants sont sur base de technologies modernes avec des concepts plus récents et efficaces mais à la fin on finit toujours par imprimer, sauvegarder et gérer le système de manière similaire à la génération précédente.
Ce ne donc pas les composants systèmes ni les ingénieurs qui les pilotent, habitués à ces générations successives de système, qui posent problème. Le point d'achoppement d'une migration technologique, c'est toujours l'applicatif. Quand il est fait "maison", il faut toujours l'adapter de manière importante. Et comme ce chef d'œuvre (en tout cas, vu comme tel…) a naturellement la vertu d'être unique, tous les coûts de migration d'un système à l'autre sont exclusivement à la charge de l'entreprise propriétaire.
Comme expliqué dans l'épisode [1], la pierre angulaire du projet NACA, c'est le Logiciel Libre (OSS- "Open Source Software") avec Linux comme fondation de l'édifice. Nous ne voulions pas déroger à ce principe pour l'applicatif. Aussi, un choix comme le Cobol Microfocus (qui fonctionne sous Linux) aurait peut-être permis un chemin peut-être plus facile a été abandonné car il n'est pas Open Source. Il est de plus (fort) coûteux et ne nous pas aurait permis de restructurer / instrumenter notre application comme la couche objet (Java) que nous intercalée nous a permis le faire [J'y reviens plus bas]
Des premiers essais en quelques semaines par un prototype interne nous ont montré que le transcodage automatisé de Cobol à Java était "jouable". Sur le bon principe "Buy before Make", nous avons donc passé un appel d'offres à plus de 20 sociétés (quel que soit leur pays d'origine) susceptibles de répondre: beaucoup de sociétés en liens offshore avec l'Inde mais personne capable de faire un transcodage 100% automatique.
Doté d'un groupe de développeurs-experts, PubliGroupe à pu se permettre de poursuivre en interne la voie du transcodeur (i.e une sorte de compilateur Cobol vers Java) développé "maison" parce que le transcodage 100% automatique était un must dans la stratégie NACA développée dans le premier billet.
Les objectifs que nous nous sommes fixés pour ce transcodage:
- fonctionnement 100% automatique: le transcodeur reçoit les fichiers source de l'application Cobol: programmes, définitions des zones de données (les fameuses COPY CLAUSE de Cobol), les ordres SQL (souvent séparées en fichiers INCLUDE isolés), les définitions d'écrans (BMS Maps pour un mainframe IBM) et génère à partir de tout cela une application Java structurée en servlets Tomcat accédant à la base de données relationnelles via JDBC et assurant le dialogue vers les écrans par une interface http/HTML native.
- lisibilité du nouveau code source Java: certains des répondants à notre appel d'offres généraient du Java mais ce Java avait l'aspect d'un langage machine (… une sorte d'assembleur pour ceux qui connaissent encore ce langage…). Ce Java était certes opérationnel et sans faute mais absolument illisible et non maintenable pour un être humain. L'objectif de notre transcodeur (tenu par la suite…) de générer un Java totalement lisible par un programmeur standard. La motivation en est très simple: à la sortie du transcodage, nous voulions un code pouvant vivre encore 10 ans au moins. Une nouvelle génération de PUB 2000 "from scratch" est maintenant mise en chantier mais on connaît la durée de ce genre de chantier !... Il faut donc que le programmeur qui connaissait la version Cobol ne soit pas noyé dans la nouvelle mouture transcodée. Sinon gare aux coûts de maintenance…
- maintenabilité du nouveau code source Java: encore une fois, il s'agit de pouvoir poursuivre l'évolution fonctionnelle de l'application transcodée. Nous avons donc pris l'option de générer des servlets Java copiant le plus fidèlement possible le Cobol initial. Le Java généré n'est ensuite pas dans le plus pur style orienté objet mais si vous êtes prêts (nous l'étions à 100%) à des concessions dans la pureté théorique du code généré, le résultat obtenu est un programme Java calqué quasiment ligne pour ligne sur le programme Cobol originel. Un vrai bonheur pour les programmeurs applicatifs qui à la fois connaissait parfaitement leur "poésie" Cobol et ne sentent pas encore tout à fait à l'aise avec les dernières subtilités OO de Java.
- homogénéité du code produit: comme personne ne met ces "petites mimines délicates" dans le code Java nouveau, ce dernier est incroyablement homogène car le code est généré à partir de l'analyse syntaxique du Cobol selon un algorithme toujours le même. On efface donc les touches personnelles de poésie d'un code maintenu pendant longtemps par des personnes différentes.
- nettoyage du code: le Cobol est structurellement simple. Il est donc possible de détecter puis de retirer certaines zones de code mort. Nous en avons encore sûrement laissé derrière nous, mais pas mal ont quand même été purgées.
- iso-fonctionnalité du code: c'est une conséquence naturelle du transcodage automatique. Mais, elle est importante: en ne travaillant jamais à la main, on n'introduit pas d'erreurs et on ne met pas en danger le projet par "l'ajout en passant de ces quelques petites fonctions que tout le monde attend depuis longtemps" qui finissent par s'avérer fatales car , par leurs bugs, elles troublent tout le planning du projet.
- découpage du code transcodé en 2 niveaux: nous avons découpé notre code généré en 2 couches: (a) la partie visible: un objet par verbe Cobol et un objet par variable du programme. Ces objets s'utilisent mutuellement pour exécuter le programme. Dans cette partie du code se trouve la correspondance ligne à ligne entre Cobol et Java. Ce sont donc ces 4 millions de lignes Java que les programmeurs applicatif maintiennent désormais chez nous (b) une bibliothèque d'exécution ("runtime"), appelée NacaRT - développée spécifiquement pour émuler Cobol et son environnement d'exécution CICS ou batch ). Cette bibliothèque est elle très (très) fortement orientée objet. Elle est le pilier d'appui des objets verbes et variables du (a). L'intérêt de cette architecture est multiple: (1) on préserve visuellement l'ordre et la structure du programme Cobol initial (2) la très forte orientation objet avec une hiérarchie d'héritage très profonde permet de faire évoluer de manière fondamentale le comportement du programme (performances, répartition de charge, etc…) par modification de l'implémentation des objets sous-jacents de la hiérarchie. Une hiérarchie profonde permet ensuite de circonscrire ces modifications de comportement à des zones plus ou moins vastes de l'application en fonction de l'endroit où l'on place la modification
Les conséquences d'un tel choix d'architecture de transcodage:
- transcodage répétable autant que nécessaire: le processus est 100% automatique. L'absence d'intervention manuelle permet de refaire l'opération autant que nécessaire à coût nul ou presque.
- stratégie de test devient encore plus critique: puisque l'on peut transcoder sans effort, on a envie de le faire souvent (voir les 2 points ci-dessous). Par contre, il faut alors pouvoir tester facilement le résultat effectif du transcodage. On arrive donc très vite à la conclusion qu'un outil de test automatique va de pair: nous avons donc développé cet outil de test (NacaTools) autant pour le batch que le transactionnel. Il s'agit de comparer une base de données résultant d'une batterie de transactions-tests en Java sur une base de départ connue à une copie de la même base de données résultant de la même série de transactions-tests faites depuis les transactions CICS. En plus bien sûr, on compare chaque champ de chaque écran de chaque transaction-test afin de vérifier que les 2 extrémités de l'application (les données permanentes et l'interface utilisateur) sont bien identiques. Nous avons ainsi développé plusieurs centaines de tests de nos applications que nous avons repassé des dizaines de fois pendant la mise au point du transcodeur et du runtime. L'objectif était de ne déranger les utilisateurs qu'une seule fois et pas chaque semaine pour tester les bugs corrigés durant la semaine (….et vivre de pleins fouets les nouveaux): ils sont venus pour nous constituer la batterie de tests. Et ensuite, on les a revus que lors des tests finaux. Même si la migration est terminée, vous vous doutez sûrement que nous continuons à utiliser cette batterie de test comme outil d'assurance-qualité de la maintenance applicative faite maintenant manuellement par nos développeurs sur le nouveau-code Java.
- continuité de l'évolution fonctionnelle applicative: le transcodage et les tests afférents étant sans douleur, on peut continuer à faire évoluer son application en Cobol pendant toute la période de bascule. En effet, on intègre les nouvelles fonctions en Cobol, on transcode et on teste automatiquement. Et hop, la nouvelle fonction fonctionne aussi en Java. Une telle migration entre le 100% des utilisateurs sur Cobol - 0% sur Java vers 0% sur Cobol - 100% sur Java dure forcément plusieurs mois, Il est impensable d'interrompre la maintenance fonctionnelle sur une telle durée.
- possibilité de migration progressive: come nous disposons d'après ce qui précède d'un moyen d'avoir une application identique dans le monde mainframe/COBOL et dans le monde Linux/Java sans peine ni coûts excessifs: nous avons pu prendre le temps de passer nos utilisateurs de l'ancien environnement au nouveau par "petits paquets" au début puis par paquets plus gros au fur et à mesure de la confiance croissante. Cela s'est ainsi fait sans douleur ni retour arrière. Encore aujourd'hui, je recommanderais exactement la même approche à tout projet de migration similaire. Cette souplesse de migration est le bon (meilleur ?) moyen de ne pas trop faire monter la pression. Dans une migration "big bang", on ne dispose que de quelques heures pour réagir quand un problème important survient sinon c'est retour arrière et le nombre des allers-retours est en général limité avant que le projet ne s'arrête définitivement… Dans une migration douce où les 2 applis cohabitent aussi longtemps que nécessaire, une telle pression n'existe jamais: on voit les goulets de performances arriver en ajoutant les paquets d'utilisateurs et en voyant leur impact. Quand le "mur" approche, on met la bascule en pause, on révise l'implémentation des objets du runtime qui posent souci et puis on repart. Pas de stress inutile!
- possibilité de cohabitation de versions Cobol & Java à long terme: ce point ne nous concerne pas directement. Mais la stratégie NACA est intéressante pour un éditeur de logiciel qui aurait à faire cohabiter 2 versions de son application pendant la durée de migration d'un portefeuille de licences placées chez des clients: la méthode migration douce rend une mutation technologique quasiment transparente (… et très économique) car il dispose en permanence d'une application identique dans "l'ancien et le nouveau monde".
- extension transparente des fonctionnalités de l'application originelle: la logique business est préservée lors du transcodage par la couche supérieure clonant le Cobol (cf plus haut). Ensuite, l'extension des objets de la bibliothèque d'exécution permet d'ajouter des fonctions supplémentaires à forte valeur ajoutée avec un minimum d'effort. Pour donner quelques exemples (déjà réalisées dans NACA ou dans nos plans d'améliorations complémentaires):
- techniques: par ex: introduction de messages de logging partout où nécessaire, profiling de l'application, instrumentation automatique de l'application pour récupérer des données plus fine sur son utilisation, etc…
- fonctionnelles: par ex
- lien "live" (par RPC) avec d'autres applications pour leur transmettre des en temps des données nouvelle / modifiées au sein de l'application commerciale
- génération automatisée d'hyperliens vers d'autres applications en fonction de la donnée affichée (ex.: l'affichage d'un code client est agrémenté d'un hyperlien vers le système de CRM externe ou vers une autre transaction de la même application utilisant le code client en entrée
Afin de ne pas faire trop long, je réserverai les détails d'architecture technique du système de transcodage à un prochain billet de la série promise sur le projet NACA.
J'espère par ce qui précède vous avoir convaincu de l'intérêt d'une migration progressive combinée à (… ou permise par) un transcodage automatique. En synthèse, on pourrait dire que c'est sûrement un peu plus long et coûteux qu'une migration "big bang" parfaitement menée. Mais, encore faut-il que le "parfaitement menée" soit une réalité sinon, pour le projet, c'est la trappe quasi-assurée! Et chacun sait que la perfection n'est pas de ce monde….
Note (cf début d'article) pour les (très) férus de détails technologiques: en plus de nos propres packages, nous avons fait appel aux composants Open Source suivants:
- Java lui-même qui est maintenant un vrai Open Source alors qu'il ne l'était pas au début du projet NACA
- Apache Tomcat comme conteneur des servlets représentant les clones des transactions CICS originelles. JBoss a été envisagé au début. Mais, n'ayant pas trouvé de réelle utilité à la lourde mécanique des EJBs ("Enterprise Java Beans") et ayant discuté avec d'autres les ayant mal vécus (performances et complexité, nous avons décidé de rester avec le très efficace (et incroyablement stable...) Tomcat jusqu'à ce qu'il ne suffise plus. Et, il a suffi jusqu'au bout ;-)
- l'IDE Eclipse avec divers plug-ins dont un spécifique développé "maison" sur lequel je reviendrai ultérieurement
- Berkeley Db java Edition pour ses fonctions de tris massifs dans les batches. C'est un moteur complet de fichiers ISAM sequentiels indexés mais nous n'avons pris que le tri car toutes nos données sont 100% relationnelles. Il a par ailleurs fallu l'enrichir pour supporter le très propriétaire format EBCDIC d'IBM.
- quelques composants de l'immense librairie Apache Commons pour l'implémentation de certaines structures de données et fonctions très standards à toute application
- Apache Xerces comme parser XML dans le transcodeur lui-même et dans NacaRT pour le passage des XMLs générés par l'application à l'HTML envoyé par le navigateur
- Apache XSLTC comme compilateur XSLT pour les transformations successives des XMLs utilisés à l'éxécution: enrichissements /modifications successifs du XML initial pour arrriver à du HTML finalement envoyé au navigateur
- quelques petits composants de Apache Struts pour une meilleure gestion des URLs, des servlets et de leur correspondance
- Apache Log4J pour assurer la journalisation (logging) de tous les messages émis par le transcodeur, l'application initiale et par les objets "instrumentés" de la biblliothèque NacaRT
- Apache Ant comme outil de transcodage / compilation / contruction de l'ensemble de l'application ( NacaTrans, NacaRT, Nacatools + 4 millions de lignes de source Java de l'application transcodée. En fait, nous avons utilisé AntHill un dérivé commercial de Ant qui ne nous a pas donné entière satisfaction durant le projet: aujourd'hui, on prendrait Ant natif ou carrément autre chose dans le même domaine de la construction d'applications.
- CVS pour la gestion de tous ces sources. Aujourd'hui, ce serait vraisemblablement Subversion (SVN) qui était encore très / trop jeune quand nous avons commencé.
PS (pour ceux arrivés jusqu'à là dans la lecture...): tout n'est pas aussi idyllique que cela pourrait en avoir l'air. J'ai demandé à nos développeurs / experts de faire un inventaire des "points durs" du transcodage. Ils feront l'objet d'un prochain billet.