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 :

<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 :
<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 :

<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/

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 :

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

    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 :

1 thoughts on “Notes de migration vers JSF 2 et Richfaces 4

Laisser un commentaire

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

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.