Notes de migration vers JSF 2 et Richfaces 4

JBoss Richfaces LogoDébut 2014, j’ai étudié la faisabilité technique d’une migration de JSF 1.2 + Richfaces 3.3 vers JSF 2.1 + Richfaces 4.3 sans changer de serveur d’application.
Notre serveur JBoss 5.1 EAP étant certifié JavaEE 5, la première difficulté consistait à désinstaller l’implémentation Mojarra de JSF 1.2 embarquée dans JBoss. Cette opération est le pré-requis à l’installation de la version de JSF de son choix. Cette dernière aura alors pour unique contrainte d’être compatible avec le moteur de Servlet 2.5 sur lequel repose JBoss Web.
Plus classique, la seconde difficulté consistait à monter les versions de JSF et de Richfaces d’une application existante.
J’ai arrêté mon étude après avoir migré le premier écran de cette application. Ayant conservé quelques notes, je me suis dit qu’elles pourraient intéresser certains ou certaines d’entre vous.
Ce billet commence par expliquer comment désinstaller JSF 1.2, se poursuit par le déploiement du Showcase de Richfaces 4.3.5 dans JBoss 5.1 EAP et se termine par la mise à disposition de mes notes de migration.

Désinstaller JSF 1.2

La base de connaissance de RedHat indique la procédure à suivre. Elle met en garde sur le fait que le support RedHat n’applique pas sur les versions de JSF ajoutées en dehors d’une distribution EAP.

  1. Supprimer les JAR de JSF

Supprimer les JAR jsf-impl.jar, jsf-api.jar et jboss-faces.jar du répertoire deploy/jbossweb.sar/jsf-libs.

  1. Supprimer la configuration JSF de JBoss

La désactivation de JSF dans JBoss passe par la suppression des 4 blocs de lignes XML suivantes dans le fichier deployers/jbossweb.deployer/web.xml :

La dernière manipulation consiste à supprimer la ligne suivante dans le fichier deploy/jbossweb.sar/META-INF/jboss-structure.xml :
<path name="jsf-libs" suffixes=".jar" />

Pour se prémunir de toute erreur de syntaxe, démarrer JBoss à vide.

Déployer une application JSF 2

Afin de valider le déploiement d’une application JSF 2 / Richfaces 4, le candidat retenu a naturellement été l’application démo Richfaces Showcase de JBoss. Seuls les quelques composants utilisant les fonctionnalités asynchrones de Servlet 3.0 ne seront pas disponibles. Sortie début 2014, la version utilisée est la 4.3.5.
Disponible librement sur le site de JBoss, l’archive richfaces-4.3.5.Final.zip donne accès au code source de Richfaces et de son Showcase. Accessible dans le sous-répertoire examples\richfaces-showcase\, le WAR de l’application web peut être construit avec maven.
Plusieurs profiles maven sont configurés dans le pom.xml. Certains permettent de choisir l’implémentation de JSF (Myfaces ou Sun RI/Mojarra). D’autres permettent de construire le WAR en fonction du serveur d’application cible : Tomcat 6, JBoss AS 7 et 7.1 ou bien encore un serveur certifié JavaEE 6. Les derniers permettent de le déployer sur les PAAS OpenShift et Google App Engine.
Aucun profil n’existe pour JBoss 5.1 EAP. Le profil se rapprochant le plus de notre JBoss 5.1 EAP est celui pour Tomcat 6. Ce profil est celui activé par défaut lors d’un mvn clean install.

Quelques adaptations du pom.xml sont nécessaires afin que le WAR généré par maven puisse être déployé dans JBoss 5.1 EAP :

1. Supprimer les dépendances vers Hibernate

Les 4 dépendances suivantes peuvent être supprimées car JBoss embarque ses propres versions d’Hibernate 3.3. A noter que le showcase est rétro-compatible avec Hibernate 3.3 et JPA 1:

  • hibernate-commons-annotations-4.0.1.Final.jar
  • hibernate-core-4.0.0.Final.jar
  • hibernate-entitymanager-4.0.0.Final.jar
  • hibernate-jpa-2.0-api-1.0.1.Final.jar

Dans le fichier src/main/resources-tomcat/META-INF/persistence.xml, la déclaration suivante doit être supprimée :
<provider>org.hibernate.ejb.HibernatePersistence</provider>

2. Adapter les descripteurs de déploiement à JBoss 5

Les descripteurs de déploiement de tomcat6 ont été adaptés, en particulier le fichier src/main/webapp-tomcat/WEB-INF/web.xml.
Premier changement, son namespace référence l’API Servlet 2.5 :

La fonctionnalité de requête asynchrone, la balise < async-supported> du filtre PushFilter doit être supprimée.

JSF étant désormais embarqué dans le répertoire lib du war, l’ajout du ConfigureListener est nécessaire :

Enfin, CDI n’étant pas géré par le conteneur JEE, la ressource BeanManager doit être supprimée.

Une fois déployé dans JBoss, le showcase RichFaces est disponible à cette URL : http://localhost:8080/richfaces-showcase-tomcat6/

Richfaces Showcase Screenshot

Les logs de démarrage du serveur JBoss et de l’application montrent clairement que JSF 2 et Richfaces 4 sont utilisés :

Migrer vers JSF 2 et Richfaces 4

Migrer une application développée en Richfaces 3 vers du Richfaces 4 est loin d’être indolore. Outre la montée de version de JSF, c’est le passage à Richfaces 4 qui demande le plus d’efforts. En effet, cette version majeure n’est pas rétro-compatible avec la précédente. De nombreux changements ont été apportés :

  • Réduction du code d’Ajax4Jsf au profit du support Ajax introduit dans JSF 2
  • Renommage de classes, packages, tags et paramètres
  • Suppression de tags Richfaces

Pour faciliter le travail du développeur, JBoss met à disposition un Guide de migration de Richfaces 3 vers Richfaces 4. Et pour le compléter, voici mes notes de travail :

Migration Richfaces

    1. Retirer le context parameter org.ajax4jsf.VIEW_HANDLERS nécessite de réimplémenter la classe ParameterizedFaceletViewHandler
    2. La classe org.richfaces.renderkit.html.HtmlRichMessageRenderer est renommée en HtmlMessageRenderer
    3. La classe AjaxContext d’A4JSF a disparu. Nécessiter d’utiliser l’API JSF2  context.getPartialViewContext().isAjaxRequest() pour savoir si la requête http est une requête Ajax. Le code ajaxContext.getAjaxSingleClientId() est migré en context.getPartialViewContext().getExecuteIds().size() == 1
    4. Supprimer la déclaration du filtre org.ajax4jsf.Filter dans le web.xml qui n’est plus nécessaire dans RichFaces 4.
    5. Classes InternetResourceBuilder et InternetResource non trouvées : constante DEFAULT_EXPIRE redéfinie à 1 jour.
    6. Les composants <a4jloadScript> et  <a4jloadStyle> ont été supprimés de RichFaces 4. Les tags natifs de JSF 2 doivent être utilisés h:outputStylesheet and h:outputScript
    7. Remplacer le tag <a4j:loadBundle> par <f:loadBundle>
    8. Sous peine d’une javax.faces.FacesException: No enum const class org.richfaces.component.JQueryTiming.onload , la déclaration <rich:jQuery timing= »onload » selector= »document » query= »loadMyCombo() »/> doit être corrigée avec l’attribut timing= »domready ».
    9. Renommer l’objet JavaScript Richfaces en RichFaces.

Migration des composants RichFaces

    1. Prendre connaissance du guide de migration des composants.
    2. Migrer <rich:modalPanel> vers <rich:popupPanel>
    3. Tag <rich:spacer> supprimé. Pas d’équivalent. Création nécessaire d’un custom tag.
    4. Migrer le tag <rich:simpleTogglePanel> en <rich:collapsiblePanel>
    5. Tag <a4j:include> remplacé par <ui:include> + paramètre viewId changé en src
    6. Tag <aj4:form> remplacé par <h:form>
    7. Tag <a4j:support> remplacé par <a4j:ajax>
    8. Tag <rich:toolTip> remplacé par <rich:tooltip>
    9. Tag <rich:suggestionbox> des snippets à migrer vers l’autocomplete.
    10. Noms d’évènements JavaScript à renommer. Exemple de message d’erreur si oubli : <a4j:ajax> onclickevent is not supported for the HtmlSelectOneRadio
      1. HtmlSelectOneRadio : event= »onclick » à changer en « select »
      2. HtmlSelectOneMenu : event= »onchange » à changer en « select »
      3. HtmlInputText : event= »onchange » à changer en « change »
      4. UICalendar : event= »onchanged » à changer en « change »

Migration Facelets

    1. Facelets est désormais inclu dans l’API JSF 2 et l’implémentation JSF RI Mojara 2 (jsf-impl-2.1.19-jbossorg-1.jar et jboss-jsf-api_2.1_spec-2.1.19.1.Final.jar)
    2. Package com.sun.facelets scindé en 2 : javax.faces.view.facelets pour l’API et com.sun.faces.facelets pour l’implémentation
    3. Classe com.sun.facelets.impl.ResourceResolver relocalisée dans javax.faces.view.facelets.ResourceResolver
    4. Supprimer le parameter <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name> du web.xml
    5. Renommer les variables suivantes dans le web.xml :
      1. facelets.SKIP_COMMENTS en javax.faces.FACELETS_SKIP_COMMENTS
      2. facelets.LIBRARIES en javax.faces.FACELETS_LIBRARIES

Migration Apache Tomahawk

    1. Utiliser la version MyFaces Tomahawk 1.1.14 for JSF 2.0.
    2. L’artefact tomahawk change en tomahawk20
      <dependency>
      <groupId>org.apache.myfaces.tomahawk</groupId>
      <artifactId>tomahawk20</artifactId>
      <version>1.1.14</version>
      </dependency>

Migration JSF 2

    1. L’extension .jspx des pages ne fonctionne plus => géré par le moteur JSP de Tomcat. Passage à xhtml + suppression conf JSF
    2. Classe com.sun.faces.application.ConfigNavigationCase de JSF 1 à remplacer :
      1. Utilisation de la classe javax.faces.application.NavigationCase introduite dans JSF 2.
      2. Le constructeur prend 3 nouveaux paramètres : condition, includeViewParams et parameters
      3. La méthode getToViewId prend désormais un FacesContext en paramètre => nécessite de faire appel à FacesContext.getCurrentInstance()
    3. Remplacer <head> du HTML par <h:head> de JSF 2 sous peine d’obtenir l’erreur jSf précisant qu’une ou plusieurs ressources partagent la cible «body», mais qu’aucun composant «body» n’a été défini dans la vue.

Conclusion

En l’espace 2 jours, j’aurais réussi à démarrer mon application puis à migrer partiellement un premier écran, et ceci sans changer de serveur d’application. J’ai chiffré à 30 jours de développement le temps nécessaire pour migrer l’intégralité d’une application comportant une vingtaine d’écrans.
Pour les applications comportant de nombreux écrans, afin de réduire les coûts et les erreurs humaines, l’automatisation des tâches récurrentes de migration pourrait être intéressante. Dommage que JBoss ne propose pas un tel outil.

Références :

Une réflexion au sujet de « Notes de migration vers JSF 2 et Richfaces 4 »

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *