JSP Custom Tags, etiquetas a medida

La tecnología JSP nos permite introducir código java en páginas HTML. Este documento supone que esta familiarizado con la creación de páginas JSP y que tiene instalado apache-tomcat (en realidad valdria cualquier servidor de JSP que cumpla la especificacion JSP 1.1, pero los ejemplos se han probado en tomcat).

Las JSP estan compuestas por varios tags o, como las denominaremos en adelante, etiquetas. Ejemplos de etiquetas JSP son setProperty, include, forward, usebean. Todas ellas encapsulan comportamientos de una manera sencilla. Nos detendremos en la etiqueta <jsp:useBean>, esta etiqueta crea una instancia de una clase definida como un JavaBean. El uso de la etiqueta <jsp:useBean> en conjunción con los JavaBeans hace que podamos separar la lógica de presentación de la de proceso y consigue unos archivos JSP mas claros, y lo que es mas importante, el que escribe la JSP, no tiene por que tener unos conocimientos avanzados de programación para crear páginas JSP de funcionalidades avanzadas. Pues bien, las JSP Custom Tags son un paso mas allá, con ellas se pueden añadir nuevas etiquetas a las ya existentes en JSP (eso si a partir de la especificacion JSP 1.1) que encapsulen comportamientos mas o menos complicados. Antes de seguir, vamos a revisar algunos conceptos:

Hay dos tipos principales de etiquetas, etiquetas que no tienen cuerpo y etiquetas que si lo tienen, ejemplos son la etiqueta <img src="asdfa.gif"> que no tiene cuerpo y no tiene etiqueta de final, y <title>manolo</title> que tiene cuerpo y etiqueta de final. Estos son también los dos tipos de etiquetas JSP que nos podemos crear a medida.

La primera etiqueta

Las JSP Custom tags no son mas que clases java que implementan o heredan de determinadas clases. Dependiendo de si la etiqueta tendrá cuerpo o no, se debe implementar una interfaz u otra, o como veremos mas adelante se debe heredar de una u otra clase.

Vamos a definir nuestra primera etiqueta personalizada, que como se ha hecho desde el principio de los tiempos será un Hola mundo, muy original :). Nuestra etiqueta va a ser una etiqueta sin cuerpo de la forma <ejemplo:hola/>. Atención a la barra invertida del final, recordemos que JSP sigue una sintáxis XML y las etiquetas que no tienen cierre deben llevar esta barra. Como es una etiqueta sin cuerpo, se debe implementar la interfaz Tag:


public inteface Tag {
    public final static int SKIP_BODY = 0;
    public final static int EVAL_BODY_INCLUDE = 1;
    public final static int SKIP_PAGE = 5;
    public final static int EVAL_PAGE = 6;
	
    public int doEndTag() throws JspException;
    public int doStartTag() throws JspException;
    public Tag getParent();
    public void release();
    public void setPageContext(PageContext pc);
    public Tag setParent(Tag t);
}

setPageContext: Este método lo invoca el motor de JSP para definir el contexto.

setParent: Lo invoca el motor de JSP para pasarle un manejador de etiqueta a la etiqueta padre.

getParent: Devuelve una instancia de la etiqueta padre.

doStartTag: Se procesa la etiqueta de apertura.

doEndTag: Se ejecuta despues de doStartTag.

release: Lo invoca el motor de JSP para hacer limpieza

Bueno, nuestra etiqueta deberá implementar todos los métodos y ejecutará la complicada y novedosa tarea de mostrarnos un Hola mundo. Crearemos ahora lo que se llama un Tag Handler. Primero el código y luego comentamos:

// HolaMundoTag.java
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class HolaMundoTag implements Tag {
    private PageContext pc;
    private Tag parent;
	
    public HolaMundoTag() {
        super();
    }
	
    public int doStartTag() throws JspException {
        try {
            pc.getOut().print("Hola mundo");
        } catch (IOException e){
            throw new JspException("Error al enviar al cliente" + 
                                            e.getMessage());
        }
        return SKIP_BODY;
    }
	
    public int doEndTag() throws JspException {
        return SKIP_PAGE;
    }
	
    public void release(){
    }
	
    public void setPageContext(PageContext pc){
        this.pc = pc;
    }
	
    public void setParent(Tag parent){
        this.parent = parent;
    }
	
    public Tag getParent(){
        return parent;
    }
}
HolaMundoTag.java

Vemos que la funcionalidad de la etiqueta es mínima, la cantidad de métodos a implementar es bastante grande, y la mayoría de ellos triviales. La especificación JSP 1.1 nos provee de una clase que implementa una custom Tag con la funcionalidad mínima y de la que podemos heredar para sobreescribir los metodos que necesitemos. Esta clase es TagSupport, el código anterior se puede reescribir de la siguiente manera:

// HolaMundoTag.java
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class HolaMundoTag extends TagSupport{
    public int doStartTag() throws JspException {
    try{
            pageContext.getOut().print("Hola Mundo");
        } catch (IOException e) {
            throw new JspException ("Error: IOException" + 
			                        e.getMessage());
        }
        return SKIP_BODY;
    }
	
    public int doEndTag() throws JspException {
        return EVAL_PAGE;
    }
}
HolaMundoTag.java

Se necesita importar java.io.* para escribir en el cliente, los otros dos import son los típicos para una JSP Custom Tag. El método doStartTag se invoca cuando se encuentra la etiqueta de apertura, el código tiene que ir incluído en un try-catch por que el metodo print() puede generar una IOException, al final se devuelve el valor SKIP_BODY que le indica al motor de JSP que la etiqueta no tiene cuerpo (en realidad que no queremos evaluar el cuerpo). El método doEndtag se invoca cuando se encuentra la etiqueta de cierre, en este ejemplo se devuelve EVAL_PAGE para que siga evaluando el resto de la página, si no queremos evaluar el resto de la pagina el valor a devolver será SKIP_PAGE.

Para compilar debemos incluir en el classpath los paquetes de JSP. Suponiendo que tomcat esta instalado en c: omcat:

javac -classpath c:	omcatlibcommonservlet.jar HolaMundoTag.java

Ya tenemos la etiqueta compilada, ahora tenemos que decirle a tomcat como y cuando utilizarla. Para esto tenemos que crear un archivo que describa a esta etiqueta, en concreto que describa una libreria de etiquetas. Este archivo se llama Tag Library Descriptor y sigue una sintáxis xml:

<!-- Ejemplos.tld -->
<?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>ejemplos</shortname>
    <uri></uri>
    <info>Etiquetas de ejemplo</info>
	
    <tag>
    <name>holamundo</name>
        <tagclass>HolaMundoTag</tagclass>
        <bodycontent>empty</bodycontent>
        <info>Saludo</info>
    </tag>
</taglib>
Ejemplos.tld

El documento empieza con la etiqueta que declara que es un documento xml y la declaración DOCTYPE, luego el resto estará encerrado entre <taglib> y </taglib>. Las etiquetas <tlibversion> y <jspversion> definen las versiones de la librería de etiquetas y de la especificación JSP respectivamente. Vemos que cada etiqueta va encerrada entre <tag> y </tag> y lleva, el nombre de la etiqueta (<name>), la clase que implementa la etiqueta (<tagclass>), si tiene cuerpo (<bodycontent>, en este caso empty para indicar que no lleva) y la etiqueta <info> que almacena una descripción de la etiqueta.

Bueno, ya tenemos la clase que implementa la etiqueta (que por cierto para probarla, la guardaremos en c: omcatwebappsejemplosWEB-INFclassesHolaMundoTag.class, suponiendo que el tomcat esta en c: omcat), el tag library descriptor (que guardaremos en c: omcatwebappsejemplosEjemplos.tld) y necesitamos una pagina JSP para probar nuestra flamante primera etiqueta personalizada, por ejemplo esta:

<!-- HolaMundo.jsp -->
<%@ taglib uri="Ejemplos.tld" prefix="ejemplos" %>
<HTML>
<HEAD>
    <TITLE>Tag Hola Mundo</TITLE>
</HEAD>
<ejemplos:holamundo/>
</BODY>
</HTML>
HolaMundo.jsp

Vemos que es una JSP normal a excepción de nuestra nueva etiqueta, observad que no tiene etiqueta de cierre porque no tiene cuerpo. En la directiva taglib definimos donde se encuentra la librería de etiquetas y el nombre por el que nos referiremos a esta librería durante todo el documento.

Si situamos el archivo HolaMundo.jsp en c: omcatwebappsejemplosHolaMundo.jsp, arrancamos el tomcat y visitando la dirección http://localhost:8080/ejemplos/HolaMundo.jsp (sustituyendo el host y el puerto de acuerdo a vuestra configuración) vereis nuestra primera etiqueta en acción.

Etiquetas con parámetros

La etiqueta anterior no tenía ningún parámetro. Hay situaciones en las que nos interesa llamar a una etiqueta con parámetros, lo mismo que haríamos con una función. Ahora vamos a hacer una etiqueta que sume los dos numeros que le pasemos como parámetros, por ejemplo para sumar 2+3, la etiqueta será <ejemplos:suma num1=2 num2=3/>.

Por cada parámetro que queremos que soporte la etiqueta, debemos añadir al código de la etiqueta anterior un método set del mismo modo que los métodos set de los javabeans, set + el nombre del parámetro, para esta etiqueta el código es el siguiente:

// SumaTag.java
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class SumaTag extends TagSupport{
    private int num1,num2;
    public void setNum1(int num1){
        this.num1 = num1;
    }
    public void setNum2(int num2){
        this.num2 = num2;
    }
    public int doStartTag() throws JspException {
        try{
            pageContext.getOut().print("Suma: " + (num1+num2));
        } catch (IOException e) {
            throw new JspException ("Error: IOException" + 
			                                e.getMessage());
        }
        return SKIP_BODY;
    }
	
    public int doEndTag() throws JspException {
        return SKIP_PAGE;
    }
}
SumaTag.java

Tenemos que añadir la nueva etiqueta a la librería de ejemplos, el archivo Ejemplos.tld. El esqueleto es el mismo que el anterior, solo que ahora tenemos que añadir una serie de etiquetas para cada atributo. Por cada atributo le diremos su nombre, con la etiqueta <name>, si es necesaria o no, con la etiqueta <required> y si tiene que ser averiguada en tiempo de ejecución con la etiqueta <rtexprvalue>. En nuestro caso tenemos que añadir esto a Ejemplos.tld entre <taglib> y </taglib>:

<tag>
    <name>suma</name>
    <tagclass>SumaTag</tagclass>
    <bodycontent>empty</bodycontent>
    <info>Saludo</info>
    <attribute>
        <name>num1</name>
        <required>true</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
    <name>num2</name>
        <required>true</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
</tag>
Ejemplos.tld

El codigo de la jsp es igual de complicado que el de la etiqueta anterior:

<%@ taglib uri="Ejemplos.tld" prefix="ejemplos" %>
<HTML>
<HEAD>
    <TITLE>Tag Suma</TITLE>
</HEAD>
<ejemplos:suma num1="2" num2="3"/>
</BODY>
</HTML>
Ejemplos.tld

Si colocamos los archivos de la misma forma que antes (c: omcatwebappsejemplosEjemplos.tld, c: omcatwebappsejemplosSuma.jsp, c: omcatwebappsejemplosclassesSumaTag.class) y visitamos http://localhost:8080/ejemplos/Suma.jsp veremos como funciona nuestra segunda etiqueta personalizada.

Etiquetas con cuerpo

Las etiquetas con cuerpo tienen la misma estructura que etiquetas de html del estilo de <h1>titulo</h1>. Son etiquetas con etiqueta de apertura y de cierre con algo dentro, que llamamos cuerpo. Para implementar una etiqueta personalizada con cuerpo debemos heredar de la clase BodyTagSupport (o implementar la interfaz BodyTag) y sobreescribir los métodos necesarios. Vamos a hacer una etiqueta que nos diga el tamaño de la cadena que se le pasa en el cuerpo.

El código sobreescribe el método doAfterBody() que se ejecuta despues de comprobar el contenido del cuerpo de la etiqueta:

// LongitudTag.java
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class LongitudTag extends BodyTagSupport{
    public int doAfterBody() throws JspException {
        try {
            BodyContent bc = getBodyContent();
            String cuerpo = bc.getString();
            JspWriter out = bc.getEnclosingWriter();
            if (cuerpo != null){
                out.print("la longitud de " + cuerpo + "es: " + 
				              cuerpo.length());
            }
        } catch (IOException e){
            throw new JspException("Error: IOEXception" + 
			                                e.getMessage());
        }
        return SKIP_BODY;
    }
}
LongitudTag.java

Primero tomamos el contenido del cuerpo y lo almacenamos en bc, lo tratamos como cadena y lo guardamos en cuerpo y obtenemos un canal de salida para escribir en el cliente con getEnclosingWriter(). Compilamos LogitudTag.

Ahora tenemos que incluir la nueva etiqueta en nuestra librería:

<tag>
    <name>longitud</name>
    <tagclass>LongitudTag</tagclass>
    <bodycontent>JSP</bodycontent>
    <info>calcula longitud de una cadena</info>
</tag>
Ejemplos.tld

La nueva etiqueta <bodycontent> le indica al motor de jsp cual va a ser el contenido del cuerpo.Si el contenido es texto, html u otras etiquetas, se indica JSP, otro tipo de contenidos son tagdependent. Esto no afecta a la interpretación de la etiqueta, solamente es informativo.

La jsp de prueba es tanto o mas complicada que las anteriores:

<%@ taglib uri="Ejemplos.tld" prefix="ejemplos" %>
<HTML>
<HEAD>
    <TITLE>Tag Longitud</TITLE>
</HEAD>
<ejemplos:longitud>
Hola, soy una cadena
</ejemplos:longitud>
</BODY>
</HTML>
Longitud.jsp

Como siempre, se colocan los archivos en su sitio, se arranca el tomcat y se visita http://localhost:8080/ejemplos/Longitud.jsp

Hay un caso concreto de etiquetas con cuerpo, cuando se necesita que se evalúe el cuerpo mas de una vez. Esto se consigue devolviendo EVAL_BODY_TAG en lugar de SKIP_BODY en el método doAfterBody().

Como ejemplo vamos a programar una etiqueta personalizada que realice un bucle, que escriba un número determinado de veces el cuerpo de la etiqueta.

El codigo de la clase de la etiqueta bucle, es el siguiente:

// BucleTag.java
import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class BucleTag extends BodyTagSupport{
    int veces = 0;
    BodyContent bc;

    public void setVeces(int veces) {
        this.veces = veces;
    }
    public void setBodyContent(BodyContent bc){
        this.bc = bc;
    }
    public int doAfterBody() throws JspException {
        if (veces-->0){
            try {
                JspWriter out = bc.getEnclosingWriter();
                out.println(bc.getString());
                bc.clearBody();
            } catch (IOException e) {
                system.out.println("Error en Tag Bucle" + 
				                        e.getMessage());
            }
            return EVAL_BODY_TAG;
        } else {
            return SKIP_BODY;
        }
    }
}
BucleTag.java

Debemos añadir la descripción de esta nueva etiqueta a la librería:

<tag>
    <name>bucle</name>
    <tagclass>BucleTag</tagclass>
    <bodycontent>JSP</bodycontent>
    <info>realiza un bucle</info>
    <attribute>
        <name>veces</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
        </attribute>
</tag>
Ejemplos.tld

Como es la última etiqueta de esta introducción, la JSP va a ser la mas complicada XD.

<!-- Bucle.jsp-->
<%@ taglib uri="Ejemplos.tld" prefix="ejemplos" %>
<HTML>
<HEAD>
    <TITLE>Tag Bucle</TITLE>
</HEAD>
<ejemplos:bucle veces="5">
Dentro del bucle<br>
</ejemplos:bucle>
</BODY>
</HTML>
Bucle.jsp

Despues de colocar los archivos en sus lugares habituales, lanzamos el tomcat y visitamos http://localhost:8080/ejemplos/Bucle.jsp.

Conclusión

La utilización de etiquetas personalizadas en el código de nuestras páginas JSP nos permite separar aún mas la lógica de presentación de la lógica de procesamiento, permite que personas con pocos conocimientos de programación utilicen características avanzadas (accesos a bases de datos, llamadas a procedimientos remotos, etc...). Y además conseguimos uno de los fines de todo buen programador, la reutilización del software.

Enlaces

Tutorial J2EE de sun: http://java.sun.com/j2ee/download.html#tutorial

Jakarta Tag Libs (librerias de etiquetas de jakarta): http://jakarta.apache.org/taglibs

Pagina de JSP de sun: http://java.sun.com/products/jsp/index.html

Ejemplos del articulo: ejemplos.zip

COMPARTE ESTE ARTÍCULO

ENVIAR A UN AMIGO
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN GOOGLE +
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO

¡SÉ EL PRIMERO EN COMENTAR!
Conéctate o Regístrate para dejar tu comentario.