Développement avancé de composant composite avec JSF2
Introduction
Je vous en ai parlé dans un précédent article, le composant composite est l’une des nouveautés principales de JSF2. Celui-ci permet en quelques minutes de créer un composant JSF.
Au minimum, un composant JSF2 a besoin d’un fichier xhtml pour son rendu. Cepdendant, un simple fichier de markup est insuffisant pour y ajouter de la logique métier. JSF2 permet d’associer au fichier de markup, un backing bean dans lequel nous allons ajouter les traitements.
J’illustre ce concept en créant un composant JSF2 qui va lister les tweets d’un utilisateur.
Rendu du composant
Créer un répertoire resources dans le répertoire /src/main/webapp/
Créer un répertoire mycomponents (nom de la librairie) dans le répertoire resources, pour l’exemple ici cela sera mycomponents.
Créer le fichier de markup Twilist.xhtml (la casse est importante).
Ce qui nous donne l’arborescence suivante :

Le namespace http://java.sun.com/jsf/composite déclaré en entête de notre fichier nous permet d’utiliser les composants de configuration du composant composite :
- cc:interface : permet de définir la signature de notre composant (ses attributs),
- cc:implementation : l’implémentation de notre composant,
Pour récupérer la valeur d’un attribut passé à notre composant, il faut utiliser l’expression language #{cc.attrs.nomDeLattribut}
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite" xmlns:f="http://java.sun.com/jsf/core">
<h:body>
<cc:interface>
<cc:attribute name="qui" required="true"/>
</cc:interface>
<cc:implementation>
<b>Twit de @#{cc.attrs.qui} : </b><br/>
<h:dataTable id="listTwit" binding="#{cc.data}" var="twit">
<h:column>
<h:outputText value="@#{twit.fromUser} : #{twit.text}"/>
</h:column>
</h:dataTable>
</cc:implementation>
</h:body>
</html>
Dans l’implémentation, nous allons afficher un texte indiquant le pseudo de l’utilisateur via l’expression language #{cc.attrs.qui}. Pour afficher la liste des twits de cet utilisateur, nous allons utiliser ajouter un composant taillé pour, la DataTable.
Voyons maintenant comment nous allons lier le tout
Logique du composant
Cette facilité est assez méconnue, car peu documentée, cependant, il est assez facile de lier le rendu d’un composant composite (fichier xhtml) à du traitement métier via l’utilisation d’un backing bean. Celui-ci peut être écrit en groovy (si celui-ci est activé sur l’application), ou bien en java.
Il suffit de créer un classe java portant exactement le même nom que le fichier xhtml, dans notre cas Twilist.java (la casse est importante). Celui-ci doit être positionné dans un package correspondant à la librairie dans lequel ce composant est positionné.
Ce qui donne l’arborescence suivante :
+src
+main
+java
+mycomponents
+Twitlist.java
+webapp
+resources
+mycomponents
+Twitlist.xhtml
La classe Twitlist.java doit obligatoirement implémenter l’interface NamingContainer soit directement, soit en héritant d’un composant JSF l’implémentant lui aussi.
Celle-ci doit également posséder la méthode suivante :
public String getFamily() {return "javax.faces.NamingContainer";}
Ci dessous l’implementation (simplifiée) de notre composant :
public class Twitlistjava extends UIComponentBase implements NamingContainer {
//Methode devant être surchargée par le composant
public String getFamily() {return "javax.faces.NamingContainer";}
//
private HtmlOutputText pseudo;
//valeur bindée au composant
private UIData data;
@Override
public void encodeBegin(FacesContext context) throws IOException {
DataModel dataModel = new ListDataModel();
//On recupère la valeur de l'attribut via la méthode getAttributes (retourne une map)
try {
QueryResult result = new TwitterFactory().getInstance().search(new Query("from:" + getAttributes().get("qui")));
//on associe les données récupérées au datamodel bindée au composant DataTable du fichier de rendu
dataModel.setWrappedData(result.getTweets());
data.setValue(dataModel);
} catch (TwitterException e) {
LOGGER.error("erreur lors de l'appel a twitter {}", e.getLocalizedMessage());
}
super.encodeBegin(context);
}
...
}
Il est intéressant de noter que pour récupérer la valeurs des paramètres passés à notre composant il faut utiliser la méthode getAttributes()
Conclusion
Cet exemple vous aura présenté un exemple simple de composant composite associé à du code métier.
Un simple fichier de markup html n’aurait pas beaucoup de valeur si l’on ne pouvait y associéer de traitement.
Le fait d’associer par convention de nommage un backing bean à un composant composite est assez élégant. Dommage que cette fonctionnalité ne soit pas mieux documentée...
A vous maintenant d’imaginer de nouveaux champs d’application.