Le conteneur JSF 2.0 est composé de 6 phases différentes. Ces phases sont exécutées dans un ordre bien précis lors du requêtage du serveur:
- La phase Restore View
- La phase Apply Request Value
- La phase Process Validations
- La phase Update Model Values
- La phase Invoke Application
- La phase Render Response
Je ne reviendrai pas sur le rôle de chacune de ces phases, pour plus de détails jetez un œil sur cet article.
Mais qu’est-ce donc ?
Le Phase Listener est une entité qui sera appelée avant et après une phase précise. Cette fonctionnalité était déjà présente dans les précédentes version de JSF. Puisque les barbus préfèrent généralement un bon bout de code plutôt que de longues phrases rhétoriques ennuyeuses à mourir comme celle que vous lisez en ce moment même, je vous propose cet exemple:
public class myPhaseListener implements PhaseListener {
/**
*
*/
private static final long serialVersionUID = 7252665497770558239L;
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
@Override
public void beforePhase(PhaseEvent event) {
System.out.println("I am called before phase: " + event.getPhaseId());
}
@Override
public void afterPhase(PhaseEvent event) {
System.out.println("I am called after phase: " + event.getPhaseId());
}
}
Trois méthodes à implémenter donc:
- getPhaseId qui doit retourner l’identifiant de la phase que vous souhaitez écouter. Dans l’exemple ci-dessus l’identifiant
ANY_PHASEindique que nous souhaitons faire appel auPhaseListeneravant et après chacune des 6 phases JSF. - beforePhase qui prend en paramètre un
PhaseEventqui vous permettra de retrouver leFacesContext(et donc la requête Http) ou encore l’identifiant de la phase. La méthodephaseEvent.getSource()vous permettra éventuellement de gérer vos exceptions (même s’il est plutôt recommandé de se servir de l’API ExceptionHandler). - afterPhase qui sera donc invoquée entre la fin de la phase surveillée et le démarrage de la suivante.
La configuration, elle, se fait dans le fichier faces-config.xml de la manière suivante:
<lifecycle> <phase-listener>package.to.myPhaseListener</phase-listener> </lifecycle>
Et ça sert à quoi ?
On peut trouver divers intérêts dans cette approche qui ressemble de loin à de l’AOP. Certains y voient un excellent moyen de débuger leur application, d’autres s’en servent pour la restriction d’accès en fonction de l’authentification.
Permettez-moi de citer un cas concret que j’ai rencontré lors du développement d’une application en entreprise: un moteur de recherche. La problématique était de réinitialiser la liste des résultats de recherche présents dans le ManagedBean lors du clic du bouton Rechercher. Dans l’idée, il semble suffisant d’ajouter un simple listeResultats = null au début de ma méthode d’action, mais dans la pratique il n’en est rien.
En effet, si nous admettons que le formulaire de recherche contient maintes validations en tous genres, de nouvelles problématiques s’ajoutent à notre développement. Lorsque l’utilisateur poste un formulaire non valide, l’action n’est pas appelée car si la phase de validation rencontre une erreur, le cycle de vie est reconduit et la phase Invoke Application n’est pas exécutée.
Le PhaseListener s’inscrit ici parfaitement. Reste encore une question qui me brûle les lèvres: suis-je forcé d’accepter que ces 3 méthodes soient invoquées à chaque requête ? Malheureusement, oui.
Et en admettant (dans un cas extrême) que vous ayez besoin d’écouter chacune des 6 phases et effectuer un traitement lourd qui ne serait utile que dans 5% de votre application, ça peut faire mal au cœur.
Heureusement tout n’est pas perdu !
Si vous désirez faire appel à votre PhaseListener uniquement dans le cadre de certaines de vos vues, la configuration xml (voir plus haut) n’est plus nécessaire. Maintenant, dans les xhtml, on peut faire ça:
<f:phaseListener type="package.to.myPhaseListener" />
Du coup, celui-ci ne sera invoqué que lors du requêtage des vues dans lesquelles vous aurez inscrit ce tag.
Ouai, cool. On s’approche du monde des bisounours où il serait possible de faire appel à un PhaseListener au clic d’un bouton.