…ou la descente aux enfers d’une application JSF, RICHFACES, SPRING
Voici un retour d’expérience sur un événement projet comme on est appelé à en rencontrer beaucoup sur le cycle de vie d’une application, notamment lors de la maintenance, les enseignements à en tirer sont nombreux.
Il s’agissait de migrer de version une application développée en JSF1.2/RichFaces de navigateur Internet Explorer 8 (IE8) vers IE9.
Pour avoir passé il y a peu la redoutable montée de version de IE6 vers IE8, l’équipe en charge du développement, parfaitement rompue à ce contexte technique, avançait avec confiance sur ce sujet. Tout au plus devait-il s’agir d’ajustements cosmétiques isolés…. Mais ce petit battement d’aile de papillon a provoqué une belle tornade sur toute l’application, dont voici les détails.
Cadre technique: l’application en question est conçue selon une architecture multicouche avec les librairies & versions suivantes, qui ont leur importance pour la suite des événements :
Après un premier test rapide, les comportements Ajax qui sont massivement utilisés par RichFaces ne fonctionnent pas sous IE9. C’est étonnant à double titre :
- Les appels ajax sont motorisés par jQuery dont la portabilité est un gros point fort,
- Les développements jQuery faits par ailleurs ne présentent aucune régression
RichFaces fonctionnant en ‘boîte noire’, il a été décidé de migrer en version 4.0, réputée compatible avec IE9.
Des changements en cascade
Comme représenté ci-dessus, RichFaces 4 ne se branche que sur du JSF 2.0 : ce sera donc notre première étape de montée de version collatérale.
Puis s’enchainent les étapes suivantes :
- JSF2.0 impose Spring Web Flow 2.3
- Spring Web Flow nécessite Spring (Core & Security)>=3.0 et Spring Faces >=2.3.0
- Spring 3.0 nécessite de passer JUnit en 4.9 ; en effet, le chargement du fichier de contexte applicatif bloquait lors des lancements de test.
En arrivant à la situation où les briques au cœur de notre application ont été remplacées… ainsi que celles qui sont plus ‘périphériques’ :
Il faut surtout se figurer que chacune de ces montées de version a provoqué de difficultés :
- Bugs notoires ( a4j :commandLink non opérationnels dans Spring faces 2.3.0) : application d’un patch ou bien attente de la version corrective ? c’est selon vos priorités.
- Changement de configuration xml (faces-config.xml, web.xml, tous ces fichiers au cœur de l’application sont impactés),
- Incompatibilité de versions de servlet avec Tomcat,
- Modification de certaines balises JSF : impact sur toutes les JSPs,
- Perte des espaces de nom sous Eclipse (entrant en confilt au niveau des xsd préalablement utilisés : Une montée de version du plugin spring d’eclipse peut corriger ces problèmes
- …et encore bien d’autres plus ou moins intrusives
De plus, ces incohérences étaient parfois à l’origine de problèmes rencontrés seulement à l’exécution du serveur au travers d’exceptions NoSuchMethodException et java.lang.UnsupportedOperationException pour lesquelles la difficulté était de trouver la librairie en cause. Elles survenaient au chargement de l’application ou bien au détour d’une page, ce qui exigeait de faire des tests exhaustifs.
Voici quelques conclusions que nous pouvons en tirer :
1) Basique : IE9 et RichFaces 3.3.3 ne font pas bon ménage !
2) Architecture : les applications composites sont une approche rationnelle pour tirer profit des innombrables librairies adressant chacune au mieux le rôle pour lequel elles ont été créées. Si elles mettent effectivement à disposition une grande richesse de fonctionnalités, elles peuvent se révéler instables quand on doit en modifier un élément technique.
3) JSF (et autres frameworks Web à forte cohésion avec le serveur): la structuration d’applications JSF est intrinsèquement orientée serveur (règles de navigation, de contrôle, cycle de vie des Beans…) et de ce fait entraîne des changements profonds dans les cas problématiques comme celui-ci. Les pistes désormais explorées vers une séparation plus nette entre client Web et un serveur orienté REST permettra de clarifier certaines pratiques et alléger la responsabilité du serveur dans la génération de l’interface utilisateur.
4) Gestion des dépendances : engagés dans une application composite, il faudra couvrir les versions parfois conflictuelles de librairies, d’implémentations. Même outillée avec Maven, cela reste difficile quand c’est à l’exécution, avec des erreurs absconses, que cela se caractérise.
5) Tests : sur une application riche fonctionnellement, qui va traverser malgré elle quelques générations techniques, un portefeuille de tests automatisés sera le garant d’une bonne maîtrise de la non régression (Unitaires, Fonctionnels) ; Dans ce cas présent, l’application dispose d’un riche panel de tests JUnit à connotation ‘Intégration’, faute de scripts Selenium exhaustifs (seuls quelques uns on été créés).
6) Projet : De la difficulté récurrente de connaître l’impact d’une évolution, même sur un environnement technique et fonctionnel bien maîtrisé.
7) Open source : à la fois riche et ouvert, il peut se rendre problématique pour des demandes de support. Nous sommes dans le cas de communautés très importantes, très réactives, ce qui facilite la prise en compte des anomalies / évolutions ; mais cela sera dommageable sur des librairies de niches, peu documentée ou suivies…. La pérennité est à prendre en compte.




