Montée de version JDK8 : un RESTeasy récalcitrant

java8

Dans le cadre d'une montée de version d'un projet Spring en JDK6 vers JDK8, nous avons dû faire le ménage dans nos librairies.

Pour la plupart des librairies, la migration se fait en douceur. Les choses se compliquent lorsque l'on arrive à Spring et sa myriade de modules. En effet La version 3.0.5.RELEASE de Spring que nous utilisions jusqu'à présent n'était pas compatible avec les nouvelles fonctionnalités JDK8. La compatibilité JDK8 ne commençant qu'à partir de la 4.0, nous sommes donc passés à la dernière version de l'époque : la 4.1.7.RELEASE.

Heureusement pour nous Spring nous propose un super guide de migration, qui nous permet d'éviter la majorité des embûches : https://github.com/spring-projects/spring-framework/wiki/Migrating-from-earlier-versions-of-the-spring-framework.

Deux briques dépendantes de Spring ont nécessité une attention toute particulière.

Après m'être arraché les cheveux pendant de longues heures, voici en résumé 3 éléments particulièrement impactant :

L'intégration du contexte Spring

Après un premier essai de montée de version de RESTeasy en 3.0.6.Final, notre application se lance mais la reconnaissance url REST se plante. Cela est dû à un mauvais séquençage entre le chargement du contexte Spring et l'action du BeanPostProcessor, chargé de parcourir les classes annotées JAX-RS pour créer les façades REST. Heureusement un workaround que vous pourrez trouver ici (https://issues.jboss.org/browse/RESTEASY-1012) a été intégré à partir de la version 3.0.9.Final. Nous sommes donc montés directement à la 3.0.11.Final.

Mais second problème, l'intégration n'est toujours pas complète. Dans notre application nous utilisions la facilité proposée par Spring de pouvoir paramétrer les beans afin que la classe soit filtrée par une propriété gérée par le PropertyPlaceholderConfigurer (cf. http://docs.spring.io/spring/docs/4.1.7.RELEASE/spring-framework-reference/htmlsingle/#beans-factory-placeholderconfigurer). Malheureusement l'intégration du contexte Spring à partir du listener org.jboss.resteasy.plugins.spring.SpringContextLoaderListener modifie l'ordre de passage des BeanPostProcessor, empêchant cette fonctionnalité de fonctionner. Un ticket JIRA chez RESTeasy non traité à ce jour traite de ce point (https://issues.jboss.org/browse/RESTEASY-1221).

Le cache serveur

RESTeasy propose une multitude de moyens pour réaliser un cache (cf. http://docs.jboss.org/resteasy/docs/3.0.13.Final/userguide/html_single/index.html#Cache_NoCache_CacheControl).

Gestion des headers par annotation

Cette partie n'a pas réellement changé au cours du temps et permet tout simplement d'indiquer au client HTTP la configuration de cache qu'il peut utiliser.

Client "Browser" Cache

Là ça devient intéressant car la documentation officielle indique que cette gestion du cache type navigateur à implémenter par les applications clientes n'a pas changé. Cependant le code d'implémentation déclenche des warnings car toutes les classes du package sont @deprecated avec comme seul commentaire Javadoc : "Caching in the Resteasy client framework in resteasy-jaxrs is replaced by caching in the JAX-RS 2.0 compliant resteasy-client module". Mais où est donc le guide de migration? Bref, après plusieurs heures de recherche sur internet, je tombe sur ce test unitaire qui explique la version officielle non officialisée d'implémenter ce cache : https://github.com/resteasy/Resteasy/blob/3.0.11.Final/jaxrs/resteasy-jaxrs-testsuite/src/test/java/org/jboss/resteasy/test/nextgen/client/cache/ClientCacheTest.java

Cache server side

Cette fois-ci la documentation est à jour, mais il n'y a pas de guide de migration. Ce n'est donc qu'au runtime que j'ai compris qu'ils avaient complètement changé la façon de mettre en place ce cache. Cette nouvelle méthode implique la mise en place d'un cache infinispan. Notre application ayant déjà comme librairie principale de cache ehcache et n'ayant pas besoin de gérer ce type de cache en doublon du 'Client "Browser" Cache' dans notre cas, j'ai préféré ne pas l'implémenter.

La gestion des exceptions

Dans notre application nous avons développé une implémentation du javax.ws.rs.ext.ExceptionMapper permettant de transformer nos exceptions métier en une javax.ws.rs.core.Response ré-interprétable côté client. En effet en 1.2.1.GA, la documentation officielle indique qu’en cas d'erreur interne côté serveur, le client REST reçoit une exception org.jboss.resteasy.client.ClientResponseFailure (http://docs.jboss.org/resteasy/docs/1.2.GA/userguide/html_single/#Client_error_handling). Nous avions donc côté client réalisé un aspect AOP catchant ces exceptions et transformant le message transmis en une exception métier compréhensible.
Là encore, suite à la montée de version, il m'a fallu retrousser mes manches pour comprendre ce qui ne fonctionnait pas.

En 3.0.11.Final, l'exception devient deprecated avec comme unique indice la Javadoc du package : The Resteasy client framework in resteasy-jaxrs is replaced by the JAX-RS 2.0 compliant resteasy-client module. De plus dans la documentation officielle de la 3.0.11.Final, la section '42.3. Client error handling' disparaît, nous laissant deviner par nous-même comment catcher ce type d'exception. Cette fois-ci il a fallu que je rentre en debug dans le code pour connaitre le nouveau remplaçant : javax.ws.rs.InternalServerErrorException. Une fois arrivé à ce point, la mise à jour du code fut un jeu d'enfant.

Et voilà, j’espère que ce retour d'expérience pourra vous servir un petit peu et vous épargner quelques heures de recherche et de debuggage.

En conclusion je dirais que cette expérience ne fait que confirmer l'importance d'une documentation claire. La mise en place d'un @deprecated ne sert à rien, si il n'est pas accompagné d'un lien vers les classes de substitution ou de la présence d'un guide de migration expliquant comment adapter son code.

Laisser un commentaire

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

Captcha *

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