Notes de migration vers JSF 2 et Richfaces 4


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.
- 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.
- 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 :
<context-param>
<param-name>com.sun.faces.injectionProvider</param-name>
<param-value>org.jboss.web.jsf.integration.injection.JBossDelegatingInjectionProvider</param-value>
</context-param>
<listener>
<listener-class>org.jboss.web.jsf.integration.config.JBossJSFConfigureListener</listener-class>
</listener>
<listener>
<listener-class>com.sun.faces.application.WebappLifecycleListener</listener-class>
</listener>
<init-param>
<description>JSF standard tlds</description>
<param-name>tagLibJar0</param-name>
<param-value>jsf-libs/jsf-impl.jar</param-value>
</init-param>
La dernière manipulation consiste à supprimer la ligne suivante dans le fichier deploy/jbossweb.sar/META-INF/jboss-structure.xml :
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 :
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 :
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</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 :
20:51:53,781 INFO [ServerImpl] Starting JBoss (Microcontainer)...
20:51:53,782 INFO [ServerImpl] Release ID: JBoss [EAP] 5.1.1 (build: SVNTag=JBPAPP_5_1_1 date=201105171607)
20:51:53,782 INFO [ServerImpl] Home Dir: D:\jboss-eap-5.1\jboss-as
20:51:56,080 INFO [ServerInfo] Java version: 1.6.0_31,Sun Microsystems Inc.
20:51:56,081 INFO [ServerInfo] Java Runtime: Java(TM) SE Runtime Environment (build 1.6.0_31-b05)
20:51:56,081 INFO [ServerInfo] Java VM: Java HotSpot(TM) 64-Bit Server VM 20.6-b01,Sun Microsystems Inc.
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
20:52:15,315 INFO [AjpProtocol] Initializing Coyote AJP/1.3 on ajp-localhost%2F127.0.0.1-8009
20:52:15,332 INFO [StandardService] Démarrage du service jboss.web
20:52:15,334 INFO [StandardEngine] Starting Servlet Engine: JBoss Web/2.1.11.GA
20:52:15,377 INFO [Catalina] Server startup in 62 ms
20:52:18,375 INFO [PersistenceUnitDeployment] Starting persistence unit persistence.unit:unitName=#richfaces-showcase
20:52:18,455 INFO [Version] Hibernate Annotations 3.4.0.GA_CP01
20:52:18,466 INFO [Environment] Hibernate 3.3.2.GA_CP04
20:52:18,593 INFO [Version] Hibernate EntityManager [WORKING]
20:52:18,620 INFO [Ejb3Configuration] Processing PersistenceUnitInfo [name: richfaces-showcase ...]
20:52:18,688 WARN [Ejb3Configuration] Persistence provider caller does not implement the EJB3 spec correctly. PersistenceUnitInfo.getNewTempClassLoader() is null.
20:52:18,755 INFO [AnnotationBinder] Binding entity from annotated class: org.richfaces.demo.arrangeablemodel.Person
20:52:18,950 INFO [SettingsFactory] JDBC driver: HSQL Database Engine Driver, version: 1.8.0
20:52:18,979 INFO [Dialect] Using dialect: org.hibernate.dialect.HSQLDialect
20:52:19,030 INFO [Version] Hibernate Validator 3.1.0.GA
20:52:19,270 INFO [TomcatDeployment] deploy, ctxPath=/richfaces-showcase
20:52:19,440 INFO [Version] WELD-000900 1.1.4 (Final)
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»
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.
20:52:30,174 INFO [Application] RichFaces Core Implementation by JBoss by Red Hat, version 4.3.5.Final
20:52:30,217 INFO [Application] Startup initialization of PushContext
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
20:52:30,292 INFO [AnnotationBinder] Binding entity from annotated class: org.richfaces.demo.arrangeablemodel.Person
20:52:30,292 INFO [EntityBinder] Bind entity org.richfaces.demo.arrangeablemodel.Person on table Person
20:52:30,298 INFO [HibernateSearchEventListenerRegister] Unable to find org.hibernate.search.event.FullTextIndexEventListener on the classpath. Hibernate Search is not enabled.
20:52:30,299 INFO [NamingHelper] JNDI InitialContext properties:{}
20:52:30,517 INFO [Bootstrap] WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
20:52:30,855 INFO [Tomcat6Container] Tomcat 6 detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported
20:52:31,392 WARNING [Webapp] PushFilter has been deprecated, you should use PushServlet instead
20:52:31,446 INFO [ProfileServiceBootstrap] Loading profile: ProfileKey@1b83ee9a[domain=default, server=default, name=jsf2]
20:52:31,454 INFO [Http11Protocol] D?marrage de Coyote HTTP/1.1 sur http-localhost%2F127.0.0.1-8080
20:52:31,478 INFO [AjpProtocol] Starting Coyote AJP/1.3 on ajp-localhost%2F127.0.0.1-8009
20:52:31,487 INFO [ServerImpl] JBoss (Microcontainer) [5.1.1 (build: SVNTag=JBPAPP_5_1_1 date=201105171607)] Started in 37s:701ms
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
- Retirer le context parameter _org.ajax4jsf.VIEW`HANDLERS
nécessite de réimplémenter la classeParameterizedFaceletViewHandler` - La classe org.richfaces.renderkit.html.HtmlRichMessageRenderer est renommée en
HtmlMessageRenderer - La classe
AjaxContextd’A4JSF a disparu. Nécessiter d’utiliser l’API JSF2context.getPartialViewContext().isAjaxRequest()pour savoir si la requête http est une requête Ajax. Le codeajaxContext.getAjaxSingleClientId()est migré encontext.getPartialViewContext().getExecuteIds().size() == 1 - Supprimer la déclaration du filtre org.ajax4jsf.Filter dans le web.xml qui n’est plus nécessaire dans RichFaces 4.
- Classes InternetResourceBuilder et InternetResource non trouvées : constante DEFAULT_EXPIRE redéfinie à 1 jour.
- Les composants
et . Les tags natifs de JSF 2 doivent être utilisés h:outputStylesheet and h:outputScriptont été supprimés de RichFaces 4 - Remplacer le tag a4j:loadBundle par <f:loadBundle>
- 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”.
- Renommer l’objet JavaScript Richfaces en RichFaces.
Migration des composants RichFaces
- Prendre connaissance du guide de migration des composants.
- Migrer rich:modalPanel vers <rich: popupPanel>
- Tag rich:spacer supprimé. Pas d’équivalent. Création nécessaire d’un custom tag.
- Migrer le tag rich:simpleTogglePanel en rich:collapsiblePanel
- Tag
<a4j:include>remplacé par<ui:include>+ paramètreviewIdchangé ensrc - Tag aj4:form remplacé par <h:form>
- Tag a4j:support remplacé par a4j:ajax
- Tag rich:toolTip remplacé par rich:tooltip
- Tag rich:suggestionbox des snippets à migrer vers l’autocomplete.
- Noms d’évènements JavaScript à renommer. Exemple de message d’erreur si oubli : a4j:ajax onclickevent is not supported for the HtmlSelectOneRadio
- HtmlSelectOneRadio : event=“onclick” à changer en “select”
HtmlSelectOneMenu: event=“onchange” à changer en “select”HtmlInputText: event=“onchange” à changer en “change”UICalendar: event=“onchanged” à changer en “change”
Migration Facelets
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)
Package com.sun.facelets scindé en 2 : 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
org.ajax4jsf.VIEW_HANDLERS du web.xmlRenommer les variables suivantes dans le web.xml:
- facelets.SKIP_COMMENTS en javax.faces.FACELETS_SKIP_COMMENTS
- facelets.LIBRARIES en javax.faces.FACELETS_LIBRARIES
Migration Apache Tomahawk
- Utiliser la version MyFaces Tomahawk 1.1.14 for JSF 2.0.
- L’artefact tomahawk change en tomahawk20
org.apache.myfaces.tomahawk tomahawk20 1.1.14
Migration JSF 2
L’extension .jspx des pages ne fonctionne plus => géré par le moteur JSP de Tomcat. Passage à xhtml + 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 3 nouveaux paramètres :
condition,includeViewParamsetparameters - La méthode
getToViewIdprend désormais unFacesContexten paramètre => nécessite de faire appel àFacesContext.getCurrentInstance()
Remplacerdu 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 :
