JSP no sólo nos permite el uso de etiquetas estándar, también admite la creación de etiquetas personalizadas, también denominadas "extensiones de etiquetas". Empezamos con algunos ejemplos sencillos:
<ejemplos:notificarPedido nombre="Patricia Escobar" />Podría mostrar el mensaje (ver ejemplo):
Estimado/a Patricia Escobar:
Su pedido se ha procesado a partir de
Wed Feb 01 11:10:52 CET 2006. En el plazo de tres días recibirá la mercancía.
La etiqueta redirige la salida a una clase que se encarga de definir el resultado, a esta clase se la denomina interprete o manejador de etiquetas.
En resumen, la idea es sencilla: invocar mediante etiquetas a componentes comunes y reusables, que impliquen tanto presentación como lógica de negocio. En la actualidad muchos vendedores de software ofertan dentro de sus productos no sólo JavaBeans, sino además etiquetas "extendidas" para facilitar el trabajo de integración y desarrollo; esto es un ejemplo típico de reusabilidad.
Los elementos necesarios para llevar a cabo la tarea son los siguientes:
lo que haremos a continuación es examinar cada uno de estos elementos.
Cuando el motor JSP se encuentre con la etiqueta, realiza una serie de llamadas al objeto que implementa el comportamiento de la etiqueta, con la finalidad de que dicha clase escriba en la salida del JSP (le cede su salida).
El caso más común, en el que la clase hereda de TagSupport (que implementa el interfaz javax.servlet.jsp.tagext.Tag), los mensajes que el contenedor envía son (en el orden indicado):
Buenas noticias: normalmente el programador sólo tiene que trabajar sobre un método (doStartTag() o doEndTag() para definir la salida. El resto de métodos son heredados de la clase madre. El método heredado doStartTag() devuelve EVAL_BODY_INCLUDE. El objeto pageContext también es heredado.
En nuestro ejemplo se implementa el comportamiento de la etiqueta en doEndTag(). Pero sea cual sea el método elegido (Start/End), se trabaja en último término contra la salida, que es obtenida por medio del contexto de página, pageContext.getOut().
El código de ejemplo:
package paq_08_taglib_atrib;
import java.io.IOException;
import java.util.Date;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
/************************************************************************
* Clase que interpreta etiqueta.
* Notifica al usuario cuyo nombre es el atributo 'nombre' la recepción del pedido.
************************************************************************/
public class Notificacion extends TagSupport {
private String nombre; // Nombre del usuario
/************************************************************************
* Aunque no se use explicitamente por parte del programador,
* resulta necesario internamente por parte de esta clase para
* obtener el valor que pasa la página JSP
************************************************************************/
public String getNombre() { return nombre; }
/************************************************************************
* El motor JSP invoca a este método antes de invocar a doStartTag()
************************************************************************/
public void setNombre( String nombre ) {
this.nombre = nombre;
}
/************************************************************************
* El motor JSP invoca a este método al inicio de la etiqueta.
* No es necesario: si se omite, se hereda
************************************************************************/
public int doStartTag() throws JspTagException {
return EVAL_BODY_INCLUDE;
}
/************************************************************************
* El motor JSP invoca a este método al final de la etiqueta.
* Es el responsable de la escritura del resultado.
************************************************************************/
public int doEndTag() throws JspTagException {
String fecha = new Date().toString();
try {
pageContext.getOut().write("Estimado/a <b>" + getNombre() + "</b>:");
pageContext.getOut().write("<p>Su pedido se ha procesado a partir de " +
fecha + ". En el plazo de tres días recibirá la mercancía.");
} catch (IOException ex) {
throw new JspTagException("Error al escribir etiqueta en la salida JSP");
}
return EVAL_PAGE;
}
}
Observamos que la IOException debe lanzar una JspTagException con la finalidad de poder gestionar la excepción en la página.
El descriptor tiene como finalidad especificar la etiqueta, con sus atributos, cuerpo, etc. En nuestro ejemplo vemos que requiere un archivo DTD (definición de tipos):
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>ejemplos</short-name>
<description>Ejemplos de etiquetas personalizadas</description>
<tag>
<name>notificarPedido</name>
<tag-class>paq_08_taglib_atrib.Notificacion</tag-class>
<body-content>JSP</body-content>
<description>Ejemplo sencillo con atributos</description>
<attribute>
<name>nombre</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
Algunos comentarios al respecto de la librería:
Respecto a la etiqueta:
Respecto al atributo:
<ejemplos:notificarPedido nombre="<%=cadena%>" />
Habitualmente se pone el TLD en un subdirectorio de WEB-INF (en lib o en tlds), con la finalidad de que quede protegido.
El código JSP no hace más que importar la librería donde se encuentra nuestra etiqueta e invocar dicha etiqueta:
<ejemplos:notificarPedido nombre="Patricia Escobar" />
<%@ taglib uri="/ejemplos" prefix="ejemplos" %>
Evidentemente 'prefix' debe coincidir con el prefijo de la etiqueta, es decir, es el nombre de la librería en nuestra página.
Én web.xml hay que añadir las siguientes líneas para nuestro ejemplo:
<taglib>
<taglib-uri>/ejemplos</taglib-uri>
<taglib-location>/WEB-INF/tlds/ejemplos.tld</taglib-location>
</taglib>
Si se usa Tomcat aconsejo que se situe taglib entre servlet-mapping y resource-ref. El orden importa y dependiendo de la versión y la configuración del servidor puede ocurrir que nos de una excepción a la hora de interpretar el web.xml.
taglib-uri hace referencia a la dirección que aparece en la página JSP y taglib-location hace referencia a la ruta física (dentro de nuestro contexto de aplicación).
Si no se pone taglib o no coincide su taglib-uri con la uri de la JSP nos puede aparecer el típico ejemplo de "Archivo "XXX" no encontrado".
Hasta ahora hemos usado atributos que son tipos "simples" (String por ejemplo), supongamos que necesitamos tipos más complejos, por ejemplo un Vector.
En el siguiente ejemplo tenemos un nuevo atributo, denominado 'amigos', que es un vector de nombres de amigos del usuario que en este momento están en línea dentro del chat. La página sería:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page language="java" contentType="text/html; charset=iso-8859-1" %>
<%@ page errorPage="07_errorPage.jsp"%>
<%@ taglib uri="/ejemplos" prefix="ejemplos" %>
<%@ page import="java.util.Vector"%>
<%
Vector v = new Vector();
v.add( new String("Ana") );
v.add( new String("Susana") );
v.add( new String("Juan") );
%>
<html>
<head>
<title>Etiquetas personalizadas</title>
</head>
<body>
<h1>Etiquetas personalizadas</h1>
<p>Te indicamos tus amigo en linea:</p>
<ejemplos:notificarAmigos nombre="Patricia Escobar" amigos="<%=v%>"/>
</body>
</html>
Enlace al ejemplo. Se deja al lector el ejercicio de modificar el resto de elementos para que este ejemplo tenga éxito.
¿Dónde se imprime el cuerpo de la etiqueta. La respuesta es sencilla, dependiendo del método utilizado para definir la salida. Si hemos escogido doEndtag primero aparece el cuerpo y luego el valor de la etiqueta. Si hemos escogido doStartTag aparecerá inmediatamente después del contenido de la etiqueta. Un ejemplo de JSP con cuerpo:
<% int numPedidos = 3;%> <ejemplos:notificarPedido nombre="Manolo" > Esto es el 'cuerpo' de Manolo. Tiene <%=numPedidos%> pedidos pendientes.<br><br> </ejemplos:notificarPedido>