Toutefois, il est préférrable de ne pas utiliser ces notations sous cette forme.
En effet, étant donné que la directive taglib doit être présente sur toutes les pages JSP qui l'utilisent, les modifications du nom du descripteur de taglib pourrait être longue et pénible.
Afin de faciliter la maintenance, il faut déclarer la taglib dans le fichier web.xml :
L'évaluation d'un Tag JSP aboutit aux appels suivants :
Les méthodes setParent(Tag) et setPageContext(PageContext) sont renseignées, ainsi que d'éventuels attributs présents dans le tag.
La méthode doStartTag() est appelée. Son code de retour détermine l'affichage du contenu de la balise. Si le retour vaut Tag.EVAL_BODY_INCLUDE, le corps est évalué et écrit dans le JspWriter de la page, mais il est ignoré si il vaut Tag.SKIP_BODY. Si Tag.EVAL_BODY_INCLUDE est retourné alors que la balise n'a pas de corps, il est ignoré.
La méthode doEndTag() est appelée. Son code de retour détermine si le reste de la page doit être évalué ou pas. Si le retour vaut Tag.EVAL_PAGE, le reste de la page est évalué, mais il est ignoré si le retour vaut Tag.SKIP_PAGE.
Enfin, la méthode release() est appelée avant que l'objet ne soit rendu au garbage collector.
Attention toutefois, afin d'éviter trop d'allocation, les tags sont conservés en cache et réutilisés (tout comme les Servlet/JSP)...
La classe javax.servlet.jsp.tagext.TagSupport propose une implémentation par défaut de l'interface Tag (et de son interface fille IterationTag).
L'interface IterationTag étend l'interface Tag. Elle hérite donc de toutes ses propriétés et permet d'effectuer des itérations sur le contenu de la balise :
La méthode doAfterBody() est appelée après chaque affichage du corps du tag. Son code de retour determine si le corps doit être réévalué ou pas. Si le retour vaut IterationTag.EVAL_BODY_AGAIN, le corps sera réévalué, mais si le retour vaut Tag.SKIP_BODY, on passe à la fin du tag (doEndTag()) sans réévaluer le corps. La classe javax.servlet.jsp.tagext.TagSupport propose une implémentation par défaut de l'interface IterationTag.
L'interface BodyTag étend l'interface IterationTag. Elle hérite donc de toutes ses propriétés, mais permet plus de traitement sur le corps de la balise :
La méthode doStartTag() peut désormais retourner BodyTag.EVAL_BODY_BUFFERED.
Dans ce cas (et dans ce cas seulement), les méthodes setbodyContent() puis doInitBody() sont appelées avant la première évaluation du corps de la balise.
La méthode setBodyContent() permet au serveur d'application d'indiqué un buffer qui sera utilisé pour écrire le contenu du corps.
La méthode doInitBody() est appelée avant la première évaluation du corps du message.
La classe javax.servlet.jsp.tagext.BodyTagSupport propose une implémentation par défaut de l'interface BodyTag.
Le Tag Library Descriptor décrit les différents tag de la librairie.
Il s'agit d'un fichier XML qui prend la forme suivante :
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN""http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>name</shortname>
<uri></uri>
<info>Description de la taglib</info>
<tag>...</tag>*
</taglib>
Après l'entête XML et la déclaration du doctype, on a les éléments suivants :
tlibversion : Numero de version de la librairie.
jspversion : Version minimum des JSP requise pour pouvoir utilisé la librarie (optionnel, défaut: 1.1).
shortname : Indique le prefix par défaut de la librairie pour les EDI compatible (ceci n'est qu'a titre indicatif, le prefix de la directive <%@ taglib %> est utilisé).
uri : Une URI unique qui identifie cette version de la taglib (optionnel).
info : Un texte de description de la taglib (optionnel).
tag : Une ou plusieurs mapping de balise avec les classes Java.
Dans un Jar, ce fichier doit se situer dans le répertoire META-INF avec le nom taglib.tld afin de pouvoir référencer directement le fichier Jar.
name : Nom de la balise (Utilisation: <taglib-prefix:nom/>).
tagclass : Nom de la classe Java qui représente cette balise (hérite de Tag).
teiclass : Nom de la classe Java qui fournit des informations complémentaire sur la balise (optionnel).
bodycontent : Type de contenu que peut accepter la balise, peut prendre les valeurs empty (pas de corps), JSP (le contenu est interprété comme du JSP), ou tagdependent (le contenu n'est pas interprété) (optionnel, défaut: JSP).
info : Un texte de description du tag (optionnel).
attribute : Zéro, une, ou plusieurs déclarations d'attribut du tag, décomposé de la manière suivante :
name : Nom de l'attribut. La classe tagclass doit posséder un mutateur pour cet attribut.
required : true/false, indique si l'attribut est obligatoire ou pas (optionnel, défaut: false)
rtexprvalue : true/false, indique si l'attribut peut être le résultat d'une expression (${bean} ou <%=bean%>) ou si il doit être une chaine statique (optionnel, défaut: false/valeur statique)
On étend TagSupport afin de bénéficier des implémentations par défaut des méthodes de Tag.
On surcharge doStartTag(), dans lequel on se contente d'écrire la chaine "Hello World" dans la sortie de la page courante (pageContext est initialisé par l'implémentation par défaut de setPageContext()).
On retourne Tag.SKIP_BODY car on ne veut pas traiter le corps de la balise.
Il est inutile de surcharger doEndTag() car son implémentation par défaut retourne Tag.EVAL_PAGE.
Notre descripteur de fichier complet ressemble à ceci :
Cet exemple se base sur l'exemple précédent.
Nous allons améliorer notre tag précédent en lui ajoutant un attribut name.
Si name est présent, on devra afficher "Hello " suivi de la valeur de l'attribut name, sinon on affiche "Hello World".
On ajoute une variable name ainsi que son mutateur setName().
Le mutateur setName() est obligatoire car il sera utilisé afin d'initialiser l'attribut de classe name avec la valeur de l'attribut du tag, avant d'appeller doStartTag().
Dans doStartTag(), on affiche "Hello " + name + " !"...
On peut avoir besoin d'exécuter une portion de code seulement si une condition est vérifié.
Par exemple, on va exécuter le corps d'un tag seulement si l'attribut indiqué en paramètre est présent dans la session :
publicclass IsPresentTag extends TagSupport {
privateString name = "World";
publicvoid setName(String string) {
name = string;
}
publicint doStartTag() throws JspException {
if (name==null)
thrownew JspException ("name est null !");
if ( pageContext.getAttribute(name,PageContext.SESSION_SCOPE) != null )
return EVAL_BODY_INCLUDE;
return SKIP_BODY;
}
}
ainsi le code suivant :
<p:isPresent name="infoConnection">
[ Vous êtes en ligne ]
</p:isPresent>
remplace le scriplet suivant :
<% if ( session.getAttribute("infoConnection") ) { %>
[ Vous êtes en ligne ]
<% } %>
Afin de créer un tag itératif, il faut implémenter l'interface IterationTag.
Toutefois, TagSupport implémente déjà cette interface, on pourra donc étendre cette classe afin de bénéficier des méthodes par défaut...
Notre tag itératif effectuera un certain nombre de boucle selon un paramètre count.
Son mapping serait :
L'attribut count contiendra la valeur de l'attribut de la balise.
Il n'y a pas de conversion String/int à effectuer car elle est automatique puisque le mutateur prend un int en paramètre.
En cas de valeur incorrecte, une exception est lancée...
Dans doStartTag(), on initialise current qui contiendra le nombre de ligne déjà affiché. Il est important de l'initialisé dans doStartTag() car la même instance de Tag peut être utilisé plusieurs fois...
Utilisation :</paragapah>
<p:iterate count="3">Cette ligne sera affichée trois fois<br/></p:iterate>
Résultat:
Cette ligne sera affichée trois fois
Cette ligne sera affichée trois fois
Cette ligne sera affichée trois fois
Il peut être intéressant d'interagir avec le corps de la balise.
Par exemple, dans le cas d'un tag itératif, on peut avoir besoin du numéro de ligne courant dans le body.
Pour cela, il suffit de stocker des éléments dans un des scopes (page, request, session, application)...
On reprend donc le tag précédent et on met le numéro de la ligne dans le scope "page".
Notre code devient alors :
Avant chaque évaluation du corps, on place dans le scope "page" un attribut "line" contenant un Integer contenant le numéro de ligne courante.
Dans doEndTag(), on supprime cet attribut afin de ne pas 'polluer' inutilement le reste de la page.
Note : On peut également proposer un attribut supplémentaire afin de changer le nom de l'attribut stocké dans le scope "page", ainsi que le scope à utiliser...