Tutoriel sur une migration vers JSF2 et Richfaces 4

Antoine Rey

Cet article s'intéresse à la faisabilité technique d'une migration de JSF 1.2 + Richfaces 3.3 vers JSF 2.1 et Richfaces 4.3 sans changer de serveur d'application.

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum Commentez .

- Titre : Tutoriel sur une migration vers JSF2 et Richfaces 4

- Auteur : Antoine Rey

- Parution : 9 septembre 2015

- Licence : Copyright ® 2014 Antoine Rey. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Désinstaller JSF 1.2

- III. Déployer une application JSF 2

- IV. Migrer vers JSF 2 et Richfaces 4

- IV-A. Migration Richfaces

- IV-B. Migration des composants RichFaces

- IV-C. Migration Facelets

- IV-D. Migration Apache Tomahawk

- IV-E. Migration JSF 2

- V. Conclusion et remerciements

I - Introduction

Dé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.

II - 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 ne s'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.

2 - Supprimer la configuration JSF de JBoss

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

1. <context-param>

2. <param-name>com.sun.faces.injectionProvider</param-name>

3. <param-value>org.jboss.web.JSF.integration.injection.JBossDelegatingInjectionProvider</param-value>

4. </context-param>

1. <listener>

2. <listener-class>org.jboss.web.JSF.integration.config.JBossJSFConfigureListener</listener-class>

3. </listener>

1. <listener>

2. <listener-class>com.sun.faces.application.WebappLifecycleListener</listener-class>

3. </listener>

1. <init-param>

2. <description>JSF standard tlds</description>

3. <param-name>tagLibJar0</param-name>

4. <param-value>JSF-libs/JSF-impl.jar</param-value>

5. </init-param>

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.

III - 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 profils 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 quatre dépendances suivantes peuvent être supprimées, car JBoss embarque ses propres versions d'Hibernate 3.3. À noter que le showcase est rétrocompatible 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 :

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="richfaces-showcase"  version="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 :

1. <listener>

2. <listener-class>com.sun.faces.config.ConfigureListener</listener-class>

3. </listener>

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/

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

1. 20:51:53,781 INFO  [ServerImpl] Starting JBoss (Microcontainer)...

2. 20:51:53,782 INFO  [ServerImpl] Release ID: JBoss [EAP] 5.1.1 (build: SVNTag=JBPAPP_5_1_1 date=201105171607)

3. 20:51:53,782 INFO  [ServerImpl] Home Dir: D:\jboss-eap-5.1\jboss-as

4. 20:51:56,080 INFO  [ServerInfo] Java version: 1.6.0_31,Sun Microsystems Inc.

5. 20:51:56,081 INFO  [ServerInfo] Java Runtime: Java(TM) SE Runtime Environment (build 1.6.0_31-b05)

6. 20:51:56,081 INFO  [ServerInfo] Java VM: Java HotSpot(TM) 64-Bit Server VM 20.6-b01,Sun Microsystems Inc.

7. 20:51:56,081 INFO  [ServerInfo] VM arguments: -Dprogram.name=JBossTools: JBoss EAP 5.x Runtime -Xms256m -Xmx768m -XX:MaxPermSize=256m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -Djava.endorsed.dirs=D:\jboss-eap-5.1\jboss-as\lib\endorsed -Dfile.encoding=ISO-8859-1

8. 20:52:15,315 INFO  [AjpProtocol] Initializing Coyote AJP/1.3 on ajp-localhost%2F127.0.0.1-8009

9. 20:52:15,332 INFO  [StandardService] Démarrage du service jboss.web

10. 20:52:15,334 INFO  [StandardEngine] Starting Servlet Engine: JBoss Web/2.1.11.GA

11. 20:52:15,377 INFO  [Catalina] Server startup in 62 ms

12. 20:52:18,375 INFO  [PersistenceUnitDeployment] Starting persistence unit persistence.unit:unitName=#richfaces-showcase

13. 20:52:18,455 INFO  [Version] Hibernate Annotations 3.4.0.GA_CP01

14. 20:52:18,466 INFO  [Environment] Hibernate 3.3.2.GA_CP04

15. 20:52:18,593 INFO  [Version] Hibernate EntityManager [WORKING]

16. 20:52:18,620 INFO  [Ejb3Configuration] Processing PersistenceUnitInfo [name: richfaces-showcase ...]

17. 20:52:18,688 WARN  [Ejb3Configuration] Persistence provider caller does not implement the EJB3 spec correctly. PersistenceUnitInfo.getNewTempClassLoader() is null.

18. 20:52:18,755 INFO  [AnnotationBinder] Binding entity from annotated class: org.richfaces.demo.arrangeablemodel.Person

19. 20:52:18,950 INFO  [SettingsFactory] JDBC driver: HSQL Database Engine Driver, version: 1.8.0

20. 20:52:18,979 INFO  [Dialect] Using dialect: org.hibernate.dialect.HSQLDialect

21. 20:52:19,030 INFO  [Version] Hibernate Validator 3.1.0.GA

22. 20:52:19,270 INFO  [TomcatDeployment] deploy, ctxPath=/richfaces-showcase

23. 20:52:19,440 INFO  [Version] WELD-000900 1.1.4 (Final)

24. 20:52:19,534 INFO  [config] Initialisation de Mojarra 2.1.19 ( 20130213-1512 https://svn.java.net/svn/mojarra~svn/tags/2.1.19@11614) pour le contexte "/richfaces-showcase"

25. 20:52:24,347 INFO  [application] JSF1048 : Présence d?annotations PostConstruct/PreDestroy  Les méthodes de beans gérés marquées avec ces annotations auront des annotations dites traitées.

26. 20:52:30,174 INFO  [Application] RichFaces Core Implementation by JBoss by Red Hat, version 4.3.5.Final

27. 20:52:30,217 INFO  [Application] Startup initialization of PushContext

28. 20:52:30,232 WARNING [Application] JMS API was found on the classpath; if you want to enable RichFaces Push JMS integration, set context-param 'org.richfaces.push.jms.enabled' in web.xml

29. 20:52:30,292 INFO  [AnnotationBinder] Binding entity from annotated class: org.richfaces.demo.arrangeablemodel.Person

30. 20:52:30,292 INFO  [EntityBinder] Bind entity org.richfaces.demo.arrangeablemodel.Person on table Person

31. 20:52:30,298 INFO  [HibernateSearchEventListenerRegister] Unable to find org.hibernate.search.event.FullTextIndexEventListener on the classpath. Hibernate Search is not enabled.

32. 20:52:30,299 INFO  [NamingHelper] JNDI InitialContext properties:{}

33. 20:52:30,517 INFO  [Bootstrap] WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.

34. 20:52:30,855 INFO  [Tomcat6Container] Tomcat 6 detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported

35. 20:52:31,392 WARNING [Webapp] PushFilter has been deprecated, you should use PushServlet instead

36. 20:52:31,446 INFO  [ProfileServiceBootstrap] Loading profile: ProfileKey@1b83ee9a[domain=default, server=default, name=JSF2]

37. 20:52:31,454 INFO  [Http11Protocol] D?marrage de Coyote HTTP/1.1 sur http-localhost%2F127.0.0.1-8080

38. 20:52:31,478 INFO  [AjpProtocol] Starting Coyote AJP/1.3 on ajp-localhost%2F127.0.0.1-8009

39. 20:52:31,487 INFO  [ServerImpl] JBoss (Microcontainer) [5.1.1 (build: SVNTag=JBPAPP_5_1_1 date=201105171607)] Started in 37s:701ms

IV - 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étrocompatible 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 :

IV-A - 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écessité 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 à un 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 et h:outputScript.

7. Remplacer le tag <a4j:loadBundle> par <f:loadBundle>.

8. Sous peine d'une
, la déclaration
doit être corrigée avec l'attribut
.

9. Renommer l'objet JavaScript Richfaces en RichFaces.

IV-B - 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 :
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".

IV-C - Migration Facelets

- Facelets est désormais inclus 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).

- Package com.sun.facelets scindé en deux : javax.faces.view.facelets pour l'API et com.sun.faces.facelets pour l'implémentation.

- Classe com.sun.facelets.impl.ResourceResolver relocalisée dans javax.faces.view.facelets.ResourceResolver.

- Supprimer le parameter <param-name>org.ajax4JSF.VIEW_HANDLERS</param-name> du web.xml.

- Renommer les variables suivantes dans le web.xml :

- facelets.SKIP_COMMENTS en javax.faces.FACELETS_SKIP_COMMENTS,

- facelets.LIBRARIES en javax.faces.FACELETS_LIBRARIES.

IV-D - Migration Apache Tomahawk

- Utiliser la version MyFaces Tomahawk 1.1.14 for JSF 2.0.

- L'artefact tomahawk change en tomahawk20.

<dependency>
<groupId>org.apache.myfaces.tomahawk</groupId>
<artifactId>tomahawk20</artifactId>
<version>1.1.14</version>
</dependency>

IV-E - Migration JSF 2

- L'extension .jspx des pages ne fonctionne plus => géré par le moteur JSP de Tomcat. Passage à XHTMLl + suppression conf JSF.

- Classe com.sun.faces.application.ConfigNavigationCase de JSF 1 à remplacer :

- utilisation de la classe javax.faces.application.NavigationCase introduite dans JSF 2,

- le constructeur prend trois nouveaux paramètres : condition, includeViewParams et parameters,

- La méthode getToViewId prend désormais un FacesContext en paramètre => nécessite de faire appel à FacesContext.getCurrentInstance().

- Remplacer
du HTML par
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.

V - Conclusion et remerciements

En l'espace de deux jours, j'aurai 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.

Cet article a été publié avec l'aimable autorisation d'Antoine Rey.

Nous tenons à remercier ced pour sa relecture orthographique attentive de cet article et Mickaël Baron pour la mise au gabarit.