Présentation des JSP Tag Libraries (Taglibs).
Date de publication : 03/01/2005 , Date de mise à jour : 18/02/2007
Par
F. Martini (adiGuba) (mes autres tutoriels)
Les librairies de tags JSP (Taglibs) permettent de définir des tags JSP afin d'éffectuer des actions précises.
Les pages JSP n'en deviennent que plus claires car cela limite l'utilisation de scriptlets Java...
Ce tutoriel décrit le fonctionnement des librairies de tags (taglibs).
Mise à jours
Remerciement
Présentation
Qu'est-ce qu'un tag JSP
Comment utiliser une librairie de tag ?
Les différentes versions des taglibs
1. Les JSP Taglibs 1.1
1.1. Le descripteur de Taglib 1.1
1.1.1. Le Doctype
1.1.2. Description de la librairie
1.1.3. Déclaration de tag
1.1.4. Définir un attribut du tag
1.2. L'interface Tag
1.2.1. Exemple de tag : Hello World
1.2.2. Gestion des attributs du tag
1.2.3. Traitement conditionnel du corps
1.2.4. Création de tags collaboratifs
1.3. L'interface BodyTag
1.3.1. Itérer sur le corps du tag
1.3.2. Modifier le contenu du corps
1.3.3. Interpréter d'autres langages de script
1.4. La classe TagExtraInfo
1.4.1. Création de variable de script
1.4.2. Vérification des attributs
2. Les JSP Taglibs 1.2
2.1. Le descripteur de Taglib 1.2
2.1.1. Le Doctype
2.1.2. Description de la librairie
2.1.3. Le validateur de taglib
2.1.4. Les listeners
2.1.5. Déclaration de tag
2.1.6. Définir un attribut du tag
2.1.7. Déclaration des variables de script
2.2. L'interface IterationTag
2.2.1. Exemple d'itération
2.2.2. Exemple d'itération avec des variables de script
2.3. L'interface TryCatchfinally
3. Les JSP Taglibs 2.0
3.1. Le descripteur de Taglib 2.0
3.1.1. Le Doctype
3.1.2. Description de la librairie
3.1.3. Déclaration de tag
3.1.4. Déclaration de tag-file
3.1.5. Déclaration de fonction EL
3.1.6. Définir un attribut du tag
3.2. L'interface SimpleTag
3.2.1. Itérer sur le corps du tag
3.3. L'interface DynamicAttributes
3.3.1. Exemple d'attributs dynamiques
3.4. Les fichiers *.tag
3.4.1. Déclaration des attributs
3.4.2. Déclaration de variables
3.4.3. Affichage du corps
3.5. Les fonctions EL
3.6. Utilisation de JspFragment
4. Déploiement de taglib
4.1. Spécification des taglibs archivées
4.2. Et la documentation
Conclusion
Mise à jours
Liste des modifications apportées après la première diffusion de ce
tutoriel :
- Correction de la numérotation des titres et de renvois.
- Correction de la taille des colonnes des tableaux (version PDF).
- Une erreur s'était glissée dans la section "Les fichiers *.tag" : uri était indiqué à la place de tagdir.
- Ajout de la section "Déploiement de taglib".
Remerciement
Je tiens à remercier tout particulièrement
Vedaer pour l'aide qu'il m'a apportée dans ce tutoriel,
ainsi que
Ukyuu pour avoir pris le temps de relire ce tutoriel...
Présentation
Les JSP Tag Librairies permettent la création et l'utilisation
de librairies de tags au sein des pages JSP.
Une JSP Taglib est une collection d'actions prédéfinies destinée
à être utilisée dans une page JSP sous forme de tags (balises XML).
Elle se compose d'un descripteur de taglib (Tag Librarie Descriptor)
et d'un ensemble de classes Java implémentant l'interface JspTag.
Le descripteur de taglib (*.tld) est un document XML qui décrit
les associations entre les balises et la classe Java.
Ces actions sont représentées dans le source JSP comme une balise
XML. Lors de la compilation de la JSP, ces balises sont remplacées
par des appels vers la classe Java correspondante.
On peut citer comme exemple les balises standard préfixées avec jsp :
<jsp:useBean id="monBean" scope="session" class="package.MonObject" >
<jsp:setProperty name="monBean" property="monAttribut" value="1.0" />
</jsp:useBean>
<jsp:include page="maPage.jsp"/>
<jsp:redirect page="maPage.jsp"/>
etc.
|
L'utilisation de taglib permet de limiter l'utilisation de code
Java dans une page JSP.
Une Taglib est composée de trois éléments :
- Le Tag Librarie Descriptor (fichier *.tld) qui effectue le mapping entre
les tags et les classes Java.
- Les classes Java implémentant les différents Tag
(implémentant l'interface Tag, ou une de ses interfaces filles
IterationTag et BodyTag.
- Les classes Java implémentant TagExtraInfo afin d'apporter
des informations supplémentaires sur les tags (optionnel).
Qu'est-ce qu'un tag JSP
Un tag JSP est en réalité une simple balise XML a laquelle est
associée une classe Java. A la compilation d'une page JSP, ces
tags sont remplacés par l'utilisation de ces classes Java
qui implémentent une interface particulière.
La structure des tags est la suivante :
<prefixe:nomDuTag attribut="valeur">
Corps du tag
</prefix:nomDuTag>
|
On y retrouve les éléments suivants :
- Un préfixe, qui permet de distinguer les
différentes taglibs utilisées.
- Le nom du tag de la librairie.
- D'un certain nombre de couple d'attribut/valeur.
- D'un corps.
Ces deux derniers éléments sont optionnels et varient selon le
tag lui-même.
Les tags JSP sont des balises XML, elles doivent donc être
correctement fermées :
| Exemples de tags JSP |
<prefixe:nomDuTag attribut="valeur" attribut2="valeur2" attribut3="valeur3"
attribut4="valeur4" attribut5="valeur5" />
<prefixe:nomDuTag attribut="valeur"></prefixe:nomDuTag>
<prefixe:nomDuTag attribut="valeur">
<prefixe:nomDuTag>corps</prefixe:nomDuTag>
</prefixe:nomDuTag>
<prefixe:nomDuTag/>
|
Comment utiliser une librairie de tag ?
Une librairie de tags (taglib) nécessite l'utilisation d'un
descripteur de taglib. Il s'agit d'un fichier XML qui décrit
les différents tags de la librairie.
- Le descripteur de fichier est séparé des classes Java ...
- Le descripteur de fichier est inclut dans le Jar avec
les classes (il possède alors le nom suivant : "META-INF/taglib.tld").
Afin de pouvoir utiliser une taglib dans un fichier JSP,
il faut donc la déclarer avec la directive taglib.
Respectivement avec le code suivant :
<%@ taglib uri="/WEB-INF/taglib.tld" prefix="tag-prefix" %>
<%@ taglib uri="/WEB-INF/lib/taglib.jar" prefix="tag-prefix" %>
|
La première ligne permet d'utiliser un descripteur de taglib
indépendant, tandis que la seconde ligne utilise le descripteur
de taglib /META-INF/taglib.tld du fichier jar.
L'attribut prefix indique le préfixe qui sera utilisé dans
la page JSP pour les tags de cette taglib.
Toutefois, il est préférable de définir la taglib dans le fichier
web.xml de l'application web, avec le code suivant :
<taglib>
<taglib-uri>taglib-URI</taglib-uri>
<taglib-location>/WEB-INF/lib/taglib.jar</taglib-location>
</taglib>
|
Ainsi, dans les pages JSP, la directive taglib devient :
<%@ taglib uri="taglib-URI" prefix="tag-prefix" %>
|
L'attribut uri permet d'identifier la taglib à utiliser et
doit correspondre à la valeur de la balise taglib-uri du
fichier web.xml.
Les différentes versions des taglibs
Il existe actuellement trois versions des taglibs.
Chacune d'entre elle correspond à une version de J2EE, comme
indiqué sur le tableau ci dessous :
| Taglib |
JSP |
J2EE |
| 1.1 |
1.1 |
1.2.1 |
| 1.2 |
1.2 |
1.3 |
| 2.0 |
2.0 |
1.4 |
1. Les JSP Taglibs 1.1
Les JSP Taglibs 1.1 apportent un certain nombre de classes
et d'interface Java permettant de réaliser des tags personnalisés.
Ces derniers étant référencés dans un descripteur de Taglib.
Les principales classes/interfaces des taglibs 1.1 sont :
- Tag qui est l'interface de base pour écrire un tag, et
TagSupport qui correspond à son implémentation par défaut.
- BodyTag, une interface qui étend l'interface Tag
en apportant une meilleure gestion du corps des tags (itérations, écriture bufférisée),
et BodyTagSupport qui correspond à son implémentation par défaut.
- Enfin, la classe TagExtraInfo permet d'apporter des informations
complémentaires sur les tags lors de la compilation des JSP.
1.1. Le descripteur de Taglib 1.1
Le descripteur de taglib (fichier avec l'extension *.tld)
renseigne le serveur d'application sur la librairie de tag.
Cette section décrit son format pour les Taglibs 1.1.
1.1.1. Le Doctype
Le descripteur de taglib est un fichier XML décrit par un
fichier DTD (Document Type Definition) fournit par Sun.
Il prend donc 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>
...
</taglib>
|
1.1.2. Description de la librairie
La balise taglib de base du descripteur de taglibs
accepte les balises suivantes :
| Nom |
Description |
Type |
| tlibversion |
Le numéro de la version de la librairie de tag. |
obligatoire
Format : [0-9]*{ "."[0-9] }0..3 |
| jspversion |
Le numéro de version des JSP requis pour la librairie. |
optionnel (défaut: 1.1) |
| shortname |
Définit un préfixe pour les tags de la librairie.
Attention : Le prefix utilisé sera toujours celui de la directive taglib
de la page JSP. Celui définit ici permet aux éditeurs JSP d'insérer
la directive taglib avec le préfixe indiqué.
|
obligatoire |
| uri |
Définit une URI (Uniform Resource Identifier) qui identifie la taglibrairie.
Il s'agit de l'URI à utiliser pour identifier la taglib dans le web.xml et les pages JSP. |
optionnel |
| info |
Un texte de description de la librairie. |
optionnel |
| tag |
Description d'un tag de la librairie.
(Voir la section Déclaration de tag) |
Au moins une. |
1.1.3. Déclaration de tag
La balise tag permet de faire un mapping entre
un nom de balise et une classe Java (à l'instar de web.xml
qui permet la même chose entre une URL et une Servlet).
Elle accepte les balises suivantes :
| Nom |
Description |
Type |
| name |
Le nom du tag tel qu'il devra être utilisé dans les JSP. |
obligatoire |
| tagclass |
Le nom complet (package compris) de la classe Java
qui représente ce tag (sous-classe de javax.serlvet.jsp.tagext.Tag). |
obligatoire |
| teiclass |
Le nom complet (package compris) de la classe Java qui apporte
des informations supplémentaires sur le tag (sous-classe de javax.serlvet.jsp.tagext.TagExtraInfo). |
optionnel |
| bodycontent |
Indique le type de contenu du corps du tag :
empty : Le tag n'accepte aucun corps
(une exception sera lancée si il est utilisé avec un corps quelconque).
JSP : Le contenu du tag est interprétés comme du JSP.
tagdependent : Le corps du tag ne sera pas interprété. Si il contient du code
il sera affiché comme du simple texte.
|
optionnel (défaut : JSP) |
| info |
Un texte de description du tag. |
optionnel |
| attribute |
Définition des différents attributs du tag |
optionnel une balise par attribut.
Cf : Définir un attribut du tag. |
1.1.4. Définir un attribut du tag
La balise attribute permet de définir les attributs
du tag JSP. Seul les attributs décrits dans le TLD seront
acceptés. L'utilisation de nom d'attribut inéxistant dans le TLD
provoquera une exception à la compilation.
La balise attribute accepte les balises suivantes :
| Nom |
Description |
Type |
| name |
Le nom de l'attribut tel qu'il devra être utilisé dans les JSP. |
obligatoire |
| required |
Indique si l'attribut est requis (valeur possible : true/false ou yes/no). |
optionnel (défaut : false) |
| rtexprvalue |
Indique si l'attribut peut être le résultat d'une scriptlet (valeur possible : true/false ou yes/no). |
optionnel (défaut : false) |
Si un attribut required est absent, une exception sera lancée.
L'utilisation de rtexprvalue permet d'utiliser le résultat d'une
expression de scriptlet comme valeur d'attribut (par exemple <%=monObjet%>).
 |
Tous les attributs utilisés dans le tag de la page JSP seront
passés à la classe qui représente le tag via leurs mutateurs
respectifs (méthode setNomDeL'Attribut()).
Pour chaque attribut, il faut donc que le mutateur correspondant
soit présent dans la classe Java. Par exemple, si on définit un
attribut valeur, son mutateur pourrait être :
|
public void setValeur (String valeur) {
this.valeur = valeur;
}
|
 |
On peut également utiliser des types primaires (int, long,
double, boolean, etc.) en paramètre du mutateur de l'attribut.
Dans ce cas, une exception peut être lancée si la valeur est incorrecte
(par exemple, NumberFormatException).
|
 |
Si rtexprvalue vaut true (ou yes),
l'attribut n'est pas forcement du type String, mais
peut être n'importe quel objet Java ...
Attention aux ClassCastException que cela peut engendrer ...
|
1.2. L'interface Tag
L'interface Tag permet la création de Tag de base.
Elle définit six méthodes à implémenter et quatre constante.
L'évaluation d'un Tag JSP dans une page 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.
1.2.1. Exemple de tag : Hello World
Comme bien souvent, le premier exemple est un simple
"Hello world" ...
Notre tag va donc se contenter d'afficher le texte
"Hello World" dans la page JSP.
Il ne possèdera donc ni contenu, ni attribut.
| La classe HelloTag |
public class HelloTag extends TagSupport {
public int doStartTag() throws JspException {
try {
pageContext.getOut().println ("Hello World !");
} catch (IOException e) {
throw new JspException ("I/O Error", e);
}
return SKIP_BODY;
}
}
|
Explication :
- 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 chaîne "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.
Le mapping de notre tag dans le fichier de descripteur correspond à ceci :
<tag>
<name>hello</name>
<tagclass>tutoriel.taglib.HelloTag</tagclass>
<bodycontent>empty</bodycontent>
</tag>
|
Ainsi, dans une page JSP, le code suivant :
affichera dans le navigateur :
1.2.2. Gestion des attributs du tag
Nous allons améliorer notre tag précédent en lui ajoutant
un attribut
name. Si l'attribut
name est présent,
on devra afficher
"Hello" suivi de la valeur de l'attribut
name, sinon on affiche
"Hello World".
Nous devront pour cela modifier le mapping du tag afin de spécifier
qu'il peut avoir un attribut
name optionnel (voir la section
1.1.4 pour plus de détail) :
<tag>
<name>hello</name>
<tagclass>tutoriel.taglib.HelloTag</tagclass>
<bodycontent>empty</bodycontent>
<attribute>
<name>name</name>
</attribute>
</tag>
|
 |
La balise <attribute> accepte également deux autres balises :
<required> permet d'indiquer que l'attribut est requis (son absence provoquera une erreur de compilation).
<rtexprvalue> indique que la valeur de l'attribut peut être le résultat d'une expression ( <%=expression%> ).
|
Si l'attribut name est présent, sa valeur sera passée
à la classe HelloTag grâce à son mutateur setName().
Il est donc obligatoire d'implémenter cette méthode.
Au final, notre classe HelloTag pourrait ressembler à ceci :
public class HelloTag extends TagSupport {
private String name = null;
public void setName (String string) {
this.name = string;
}
public int doStartTag() throws JspException {
if (this.name==null)
this.name = "World";
try {
pageContext.getOut().println ("Hello " + this.name + " !");
} catch (IOException e) {
throw new JspException ("I/O Error", e);
}
return SKIP_BODY;
}
}
|
Ainsi, dans la page JSP :
<tag11:hello/><br/>
<tag11:hello name="Fred"/>
|
donnera l'affichage suivant :
Hello World !
Hello Fred !
|
 |
Si le nom de l'attribut n'est pas spécifié dans le TLD
ou qu'aucun mutateur (setXXX()) n'est implémenté,
une exception sera lancée lors de la compilation de la
page JSP ...
|
1.2.3. Traitement conditionnel du corps
Un tag peut également posséder un corps. Étant donné que
l'évaluation du corps dépend du retour de la méthode
doStartTag(), on peut facilement évaluer ou non le
corps du tag selon une condition quelconque ...
Nous allons écrire un tag qui affichera son contenu seulement
si le paramètre désigné par l'attribut name est présent
dans le request de la page courante.
Le code de ce tag pourrait être :
| ParamPresentTag |
public class ParamPresentTag extends TagSupport {
private String name = null;
public void setName (String string) {
this.name = string;
}
public int doStartTag() throws JspException {
String value = pageContext.getRequest().getParameter(this.name);
if (value!=null)
return EVAL_BODY_INCLUDE;
return SKIP_BODY;
}
}
|
Nous utiliserons le mapping suivant dans notre fichier
de description :
| taglib.tld |
<tag>
<name>paramPresent</name>
<tagclass>tutoriel.taglib.ParamPresentTag</tagclass>
<bodycontent>JSP</bodycontent>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
</tag>
|
L'attribut name est requis, ainsi un oubli de l'attribut
et une exception sera levée à la compilation de la page JSP.
Enfin, le corps du tag possède un contenu JSP, c'est à dire
qu'il sera interprété comme du code JSP (il s'agit du fonctionnement
par défaut).
Le code suivant :
<tag11:paramPresent name="page">
Le paramètre page vaut : <%=request.getParameter("page")%>.
</tag11:paramPresent>
|
N'affichera rien si la page JSP est appelée sans paramètre
"page". Mais si elle est appelée avec page=valeur, on obtiendra
le résultat suivant :
Le paramètre page vaut : valeur.
|
1.2.4. Création de tags collaboratifs
L'interface Tag définit les méthodes setParent()
et getParent() afin de renseigner le tag sur son tag parent
(si il existe).
Cela permet une collaboration entre différents tags. Nous allons voir
comment utiliser la méthode getParent() afin de simuler
un switch/case grâce à des simples tags.
Ainsi, la classe qui représente le tag 'switch' pourrait correspondre à :
| SwitchTag |
public class SwitchTag extends TagSupport {
private Object test = null;
public void setTest (Object obj) {
this.test = obj;
}
public int doStartTag() throws JspException {
return EVAL_BODY_INCLUDE;
}
public boolean isValid (Object caseValue) {
if (this.test==caseValue) return true;
if (this.test!=null && this.test.equals(caseValue)) return true;
return false;
}
}
|
Explication :
La classe SwitchTag se contente d'évaluer son corps.
Par contre, elle possède une méthode isValid() qui
permettra de savoir si un objet est égal à son attribut
test. Cette méthode sera utilisée par les tags 'case' :
| CaseTag |
public class CaseTag extends TagSupport {
public Object value = null;
public void setValue (Object object) {
this.value = object;
}
public int doStartTag() throws JspException {
if ( getParent() instanceof SwitchTag ) {
SwitchTag parent = (SwitchTag) getParent();
if (parent.isValid(this.value))
return EVAL_BODY_INCLUDE;
return SKIP_BODY;
}
throw new JspException ("Le tag case doit être à l'intérieur du tag swith.");
}
}
|
Explication :
La classe CaseTag récupère son tag parent et vérifie
qu'il est bien du type de SwitchTag (sinon, cela signifie
que le tag est mal utilisé, donc on peut lancer une exception).
Il utilise ensuite la méthode isValid() de son parent afin
de déterminer si il doit afficher son contenu ou pas.
Ainsi, le code suivant :
<tag11:switch test="2">
<tag11:case value="0">Zéro</tag11:case>
<tag11:case value="1">Un</tag11:case>
<tag11:case value="2">Deux</tag11:case>
<tag11:case value="3">Trois</tag11:case>
</tag11:switch>
|
affichera le résultat suivant :
 |
La classe TagSupport propose la méthode statique
findAncestorWithClass() qui permet de retrouver un
tag parent de plus haut niveau selon son type.
|
1.3. L'interface BodyTag
L'interface BodyTag étend l'interface Tag.
Elle hérite donc de toutes ses propriétés, mais permet un traitement
bufférisé du corps de la balise ainsi que
des itérations sur le corps du tag.
L'interface BodyTag définit trois nouvelles méthodes :
- setBodyContent() afin d'affecter un objet de type
BodyContent qui correspond au buffer.
- doInitBody() qui est appelée avant la première
évaluation du corps du tag.
- doAfterBody() qui est appelée après chaque évaluation
du corps.
La méthode doAfterBody() permet d'effectuer des itérations sur le
corps du tag, en fonction de son retour :
- BodyTag.EVAL_BODY_TAG permet de réévaluer le corps du tag une nouvelle fois.
- Tag.SKIP_BODY permet de passer à la fin du tag (méthode doEndTag()).
Les méthodes setBodyContent() et doInitBody() sont
appelées une seule fois si et seulement si la méthode doStartTag()
renvoie BodyTag.EVAL_BODY_TAG.
Le contenu du corps du tag sera alors écrit dans le buffer représenté
par l'objet BodyContent passé à setBodyContent().
il faudra recopier manuellement ce buffer dans la sortie de la page
JSP (avec getEnclosingWriter() de l'objet BodyContent,
ou getPreviousOut() de la classe BodyTagSupport).
 |
Dans la version 1.2 des Taglibs, la valeur BodyTag.EVAL_BODY_TAG
est dépréciée (voir Les JSP Taglibs 1.2).
Elle est remplacée par BodyTag.EVAL_BODY_BUFFERED et
IterationTag.EVAL_BODY_AGAIN, respectivement lorsque
BodyTag.EVAL_BODY_TAG devait être retourné par les
méthodes doStartTag() et doAfterBody ...
|
La classe javax.servlet.jsp.tagext.BodyTagSupport propose
une implémentation par défaut de l'interface BodyTag.
1.3.1. Itérer sur le corps du tag
Afin de comprendre le fonctionnement d'un tag itératif, nous
allons écrire un tag qui effectuera un certain nombre de boucles
selon un paramètre count.
Son code pourrait ressembler à cela :
| IterateTag |
public class IterateTag extends BodyTagSupport {
private int count = 0;
private int current;
public void setCount(int i) {
count = i;
}
public int doStartTag() throws JspException {
current = 0;
if (current < count)
return Tag.EVAL_BODY_TAG;
return Tag.SKIP_BODY;
}
public int doAfterBody() throws JspException {
current++;
if (current < count)
return IterationTag.EVAL_BODY_TAG;
return Tag.SKIP_BODY;
}
}
|
Explication :
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 la méthode setCount() prend un int
en paramètre. En cas de valeur incorrecte, une exception sera lancée ...
Dans doStartTag(), on initialise current qui contiendra le nombre
de lignes déjà affichées. Il est important de l'initialiser dans
doStartTag() car la même instance de Tag peut être utilisée plusieurs fois.
Enfin, à chaque passage dans doAfterBody(), on évalue la condition
de fin afin de retourner le code correspondant ...
Ainsi, le code suivant :
<tag11:iterate count="3">
Cette ligne sera affichée trois fois<br/>
</tag11:iterate>
|
Donnera bien le résultat suivant :
Cette ligne sera affichée trois fois
Cette ligne sera affichée trois fois
Cette ligne sera affichée trois fois
|
 |
Le fonctionnement des itérations avec la classe BodyTag
est similaire à celui de la classe IterationTag des
taglibs 1.2.
Je vous invite donc à consulter le passage sur
l'interface IterationTag.
|
1.3.2. Modifier le contenu du corps
En utilisant BodyTag.EVAL_BODY_TAG, le corps
des tags n'est pas écrit directement dans la sortie de la page
JSP mais dans un buffer. Le premier intérêt de cette méthode
est de pouvoir modifier dynamiquement ce corps avant de
l'envoyer vers la sortie de la page.
Par exemple, le tag suivant permet de mettre le texte du corps
du tag en majuscule :
| UpperCaseTag |
public class UpperCaseTag extends BodyTagSupport {
public int doStartTag() throws JspException {
return EVAL_BODY_TAG;
}
public int doAfterBody() throws JspException {
try {
if ( getBodyContent()!=null ) {
String bodyString = getBodyContent().getString();
bodyString = bodyString.toUpperCase();
getBodyContent().getEnclosingWriter().println( bodyString );
}
} catch (IOException e) {
throw new JspException (e);
}
return EVAL_PAGE;
}
}
|
Explication :
On se contente dans doAfterbody() de récupérer l'objet
BodyContent sous forme de String que l'on passe
en majuscule.
getBodyContent().getEnclosingWriter() permettant d'accéder
au JspWriter de la page JSP.
Ainsi, le code suivant :
<tag11:upperCase>Ce texte sera mis en majuscule, ainsi que le résultat de ce <%="scriptlet"%>.</tag11:upperCase>
|
affichera le texte en majuscule :
CE TEXTE SERA MIS EN MAJUSCULE, AINSI QUE LE RÉSULTAT DE CE SCRIPTLET.
|
1.3.3. Interpréter d'autres langages de script
L'interface BodyTag nous permet également d'utiliser
d'autres langages de script au sein d'une page JSP. En effet, le
corps du tag n'étant plus directement écrit dans la page JSP,
on peut très bien l'envoyer à un interpréteur et n'afficher
que le résultat ...
Ainsi, ce tag envoie le contenu de son corps à l'interpréteur PERL :
| PerlTag |
public class PerlTag extends BodyTagSupport {
private Process process = null;
protected void startProcess () throws JspException {
try {
process = Runtime.getRuntime().exec("perl");
} catch (IOException e) {
throw new JspException ("Erreur de creation du process 'perl'.", e);
}
}
protected void writeBody () throws JspException {
OutputStream stdin = process.getOutputStream();
OutputStreamWriter stdinWriter = new OutputStreamWriter (stdin);
try {
getBodyContent().writeOut( stdinWriter );
} catch (IOException e) {
throw new JspException ("Erreur d'ecriture du body.", e);
} finally {
try { stdinWriter.close(); } catch (IOException e) {}
try { stdin.close(); } catch (IOException e) {}
}
}
protected void readErrors () throws JspException {
InputStream stdout = process.getErrorStream();
InputStreamReader stdoutReader = new InputStreamReader (stdout);
BufferedReader stdoutBuffer = new BufferedReader (stdoutReader);
StringBuffer errorBuffer = null;
try {
String line = null;
while ( ( line = stdoutBuffer.readLine() ) != null ) {
if (errorBuffer==null)
errorBuffer = new StringBuffer ();
errorBuffer.append(line);
}
} catch (IOException e) {
throw new JspException ("Erreur de lecture du résultat.", e);
} finally {
try { stdoutBuffer.close(); } catch (IOException e) {}
try { stdoutReader.close(); } catch (IOException e) {}
try { stdout.close(); } catch (IOException e) {}
}
if (errorBuffer!=null)
throw new JspException ("Perl Script Error : " + errorBuffer);
}
protected void readResult () throws JspException {
InputStream stdout = process.getInputStream();
InputStreamReader stdoutReader = new InputStreamReader (stdout);
BufferedReader stdoutBuffer = new BufferedReader (stdoutReader);
try {
String line = null;
while ( ( line = stdoutBuffer.readLine() ) != null ) {
getPreviousOut().print(line);
}
} catch (IOException e) {
throw new JspException ("Erreur de lecture du résultat.", e);
} finally {
try { stdoutBuffer.close(); } catch (IOException e) {}
try { stdoutReader.close(); } catch (IOException e) {}
try { stdout.close(); } catch (IOException e) {}
}
}
public int doStartTag() throws JspException {
return EVAL_BODY_TAG;
}
public int doAfterBody() throws JspException {
startProcess();
writeBody ();
readErrors ();
readResult ();
return SKIP_BODY;
}
}
|
Explication :
doStartTag() renvoie EVAL_BODY_TAG afin
que le contenu du corps soit écrit dans un buffer.
Une fois dans doAfterBody() :
- startProcess() lance le processus "perl" (l'interpréteur perl installé sur le système).
- writeBody() écrit le contenu du corps dans le flux stdin du processus "perl".
- readErrors() récupère le flux stderr du processus "perl" afin de lancer une exception en cas d'erreur dans le script.
- readResult() récupère le flux stdout du processus "perl" et le copie dans la page JSP.
Et le code suivant :
<tag11:perl>
$lang = "perl";
printf ( "Utilisation du langage %s dans une page JSP", $lang );
</tag11:perl>
|
Affiche correctement le résultat du script PERL :
Utilisation du langage perl dans une page JSP
|
 |
Pour cet exemple, il est préférable d'utiliser dans le
descripteur de taglib la valeur tagdependent pour
le <bodycontent>.
Ceci afin d'éviter que des éléments du script PERL soient
interprétés par le JSP ...
|
1.4. La classe TagExtraInfo
La classe javax.servlet.jsp.tagext.TagExtraInfo permet
de fournir des informations supplémentaires sur le tag au moment
de la compilation de la page JSP.
Cette classe définit les méthodes suivantes :
- setTagInfo() et getTagInfo() qui permettent
d'accéder aux informations sur le tag contenu dans le descripteur
de taglib (TLD). setTagInfo() est renseigné automatiquement
par le serveur d'application.
- getVariableInfo() qui permet de mapper des éléments
des scopes vers des variables de script dans la page JSP.
- isValid() qui permet de valider le tag (et ses attributs)
avant même que la classe du tag soit exécutée ...
En plus de l'objet TagInfo renseigné par setTagInfo()
et qui permet d'accéder aux informations du fichier TLD, les méthodes
getVariableInfo() et isValid() possèdent en paramètre
un objet de type TagData permettant d'accéder aux différents
attributs du tag et à leurs valeurs.
 |
Attention, si la valeur d'un attribut est le résultat d'une scriptlet
(<%=object%> avec rtexprvalue=true), on
ne peut évidemment pas accéder à cette valeur ...
|
1.4.1. Création de variable de script
Il est possible de créer une variable de script dans la page JSP
depuis un tag. Nous allons donc modifier le tag
paramPresent
(voir la section
Traitement conditionnel du corps)
afin qu'il déclare une variable de script nommée "value" dans la page JSP.
Cette variable devra contenir la valeur du paramètre.
Nous allons commencer par modifier le code du tag afin qu'il place
la valeur du paramètre dans le scope "page" de la JSP :
public class ParamPresentTag extends TagSupport {
private String name = null;
public void setName (String string) {
this.name = string;
}
public int doStartTag() throws JspException {
String value = pageContext.getRequest().getParameter(this.name);
if (value!=null) {
pageContext.setAttribute("value", value);
return EVAL_BODY_INCLUDE;
}
return SKIP_BODY;
}
public int doEndTag() throws JspException {
pageContext.removeAttribute("value");
return EVAL_PAGE;
}
}
|
L'attribut "value" est donc placé dans le scope "page" au début
du tag et il est effacé à la fin du tag. On peut désormais utiliser
le code suivant pour afficher la valeur du paramètre :
<tag11:paramPresent name="page">
Le paramètre page vaut : <%=pageContext.getAttribute("value")%>.
</tag11:paramPresent>
|