Accueil
Rechercher:
sur developpez.com sur les forums
Forums | Tutoriels | F.A.Q's | Participez | Hébergement | Contacts
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi MS-Office SQL & SGBD Oracle  4D  Business Intelligence
FORUMS JAVA FAQs TUTORIELS JAVASEARCH SOURCES LIVRES OUTILS, EDI & API ECLIPSE NETBEANS BLOG DISCUSSIONS TV

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" %> 
	// ou si le fichier tld est dans le Jar :
	<%@ 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%>).

warning 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;
}
info 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).
warning 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 :

<tag11:hello/>
affichera dans le navigateur :

Hello World !

1.2.2. Gestion des attributs du tag


<tag>
	<name>hello</name>
	<tagclass>tutoriel.taglib.HelloTag</tagclass>
	<bodycontent>empty</bodycontent>
	<attribute>
		<name>name</name>
	</attribute>
</tag>
info 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 !
warning 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 :

Deux
info 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).

warning 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
info 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 {
		// On recupere les flux STDIN du process 'perl'
		OutputStream stdin = process.getOutputStream();
		OutputStreamWriter stdinWriter = new OutputStreamWriter (stdin);
		
		try {
			// On écrit dans le STDIN du process le contenu du tag :
			getBodyContent().writeOut( stdinWriter );
		} catch (IOException e) { 
			throw new JspException ("Erreur d'ecriture du body.", e);
		} finally {
			// Fermeture des flux ...
			try { stdinWriter.close(); } catch (IOException e) {}
			try { stdin.close(); } catch (IOException e) {}
		}
	}
	
	protected void readErrors () throws JspException {
		// On recupere les flux STDERR du process 'perl'
		InputStream stdout = process.getErrorStream();
		InputStreamReader stdoutReader = new InputStreamReader (stdout);
		BufferedReader stdoutBuffer = new BufferedReader (stdoutReader);
		StringBuffer errorBuffer = null;
	
		try {
			// On récupère chaque ligne du STDERR dans un buffer :
			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 {
			// Fermeture des flux ...
			try { stdoutBuffer.close(); } catch (IOException e) {}
			try { stdoutReader.close(); } catch (IOException e) {}
			try { stdout.close(); } catch (IOException e) {}
		}
		
		// Si le buffer n'est pas vide, on envoi une exception
		if (errorBuffer!=null)
			throw new JspException ("Perl Script Error : " + errorBuffer);
	}
	
	protected void readResult () throws JspException {
		// On recupere les flux STDOUT du process 'perl'
		InputStream stdout = process.getInputStream();
		InputStreamReader stdoutReader = new InputStreamReader (stdout);
		BufferedReader stdoutBuffer = new BufferedReader (stdoutReader);
		
		try {
			// On récupère chaque ligne du STDOUT et on l'affiche :
			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 {
			// Fermeture des flux ...
			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
idea 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.

warning 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>