3.5. JSTL

JSTL (JavaServer Pages Standard Tag Library) es una librería de tags estándar que encapsula, en forma de tags, muchas funcionalidades comunes en aplicaciones JSP, de forma que, en lugar que tener que recurrir a varias librerías de tags de distintos distribuidores, sólo necesitaremos tener presente esta librería que, además, por el hecho de ser estándar, funciona de la misma forma en cualquier parte, y los contenedores pueden reconocerla y optimizar sus implementaciones.

JSTL permite realizar tareas como iteraciones, estructuras condicionales, tags de manipulación de documentos XML, tags SQL, etc. También introduce un lenguaje de expresiones que simplifica el desarrollo de las páginas, y proporciona un API para simplificar la configuración de los tags JSTL y el desarrollo de tags personalizados que sean conformes a las convenciones de JSTL.

Se puede emplear JSTL a partir de la versión 2.3 de servlets, y 1.2 de JSP. Podéis encontrar más información sobre JSTL en:

http://java.sun.com/products/jsp/jstl

3.5.1. Uso de JSTL

JSTL contiene una gran variedad de tags que permiten hacer distintos tipos de tareas, subdivididas en áreas. Así, JSTL proporciona varios ficheros TLD, para describir cada una de las áreas que abarca, y dar así a cada área su propio espacio de nombres. 

En la siguientes tablas se muestran las áreas cubiertas por JSTL (cada una con una librería):

Librería EL:

AREA URI PREFIJO
Core http://java.sun.com/jstl/ea/core c
XML http://java.sun.com/jstl/ea/xml x
Internacionalización (I18N) http://java.sun.com/jstl/ea/fmt fmt
SQL http://java.sun.com/jstl/ea/sql sql

Librería RT:

AREA URI PREFIJO
Core http://java.sun.com/jstl/ea/core_rt c_rt
XML http://java.sun.com/jstl/ea/xml_rt x_rt
Internacionalización (I18N) http://java.sun.com/jstl/ea/fmt_rt fmt_rt
SQL http://java.sun.com/jstl/ea/sql_rt sql_rt

Se tienen dos versiones de JSTL (la razón de ello se explica más adelante), aunque la utilidad de las librerías es la misma para las dos:

Las URIs y prefijos que se indican en la tabla pueden emplearse (aunque no es obligatorio) para utilizar las librerías en nuestras aplicaciones.

EJEMPLO

Para utilizar, por ejemplo, los tags del área core en una página JSP, seguimos pasos similares a los realizados para utilizar las librerías de tags vistas anteriormente:

<%@ taglib uri="/jstl-core" prefix="c" %>

donde la uri y el prefix tendrán los valores que queramos darles.

<taglib> 
	<taglib-uri>/jstl-c</taglib-uri> 
	<taglib-location>
	/WEB-INF/c.tld
	</taglib-location> 
</taglib>

Si utilizamos las uris absolutas que se han indicado anteriormente, no tenemos que añadir este elemento <taglib> en el descriptor de despliegue. El contenedor JSP localiza automáticamente el TLD.

3.5.2. Lenguaje de expresiones

Una característica muy importante de JSTL es el soporte que ofrece para un lenguaje de expresiones propio. Hasta ahora, en una página JSP teníamos que utilizar bloques del tipo <%= valor %> para acceder al valor de un determinado componente. Con el lenguaje de expresiones podemos acceder a estos elementos de forma más sencilla. Por ejemplo: 

<c:if test="${persona.edad} > 18">
	...
</c:if>

Comprobamos si en los datos de una persona consta que es mayor de edad. 

3.5.2.1. Versiones de JSTL

Por motivos de compatibilidad con JSP 1.2, las librerías de JSTL vienen en dos versiones, que se diferencian en la forma en que dan soporte a expresiones en tiempo de ejecución para valores de atributos:

Podemos utilizar las dos librerías conjuntamente, como en este ejemplo, que utiliza las dos librerías de internacionalización (prefijo fmt):

<fmt:message key="clave">
	<fmt:param value="${miParametro}"/>
	<fmt_rt:param value="<%= miParametro %>"/>
</fmt:message>

3.5.2.2. Definición del lenguaje

Se describe a continuación, y a grandes rasgos, el lenguaje de expresiones incluido en JSTL 1.0. Se espera que dicho lenguaje, salvo algunas modificaciones que puedan surgir, sea completamente compatible con JSP 1.3, cuando salga dicha versión.

El lenguaje está inspirado en los lenguajes ECMAScript y XPath, y está basado en espacios de nombres (atributos PageContext),  propiedades de elementos, operadores relacionales, lógicos y aritméticos, y un conjunto de objetos implícitos. 

3.5.2.2.1. Atributos y expresiones

En JSTL, podremos utilizar este lenguaje de expresiones sólo en atributos de etiquetas (en JSP 1.3 también se puede utilizar fuera de ellas). Se invoca a este lenguaje mediante el elemento ${...}:

${expresion}

Hay 3 formas de utilizar expresiones en atributos:

<pref:etiq value="${expresion}"/>

En este caso, se evalúa la expresión y el resultado se convierte al tipo de dato del atributo, siguiendo las reglas de conversión internas del lenguaje.

<pref:etiq 
 value="texto${e1}y ${e2}texto"/>

Aquí, las expresiones se evalúan de izquierda a derecha, y se intercalan entre el texto, convirtiéndolas a String (siguiendo reglas de conversión internas). Luego, la cadena resultante se convierte al tipo del atributo.

<pref:etiq value="Un texto"/>

En este caso, la cadena se convierte al tipo de dato del atributo.

Para cadenas que contengan la secuencia '${' sin que sea propiamente una expresión, se encapsula esa secuencia así: ${'${'}. Por ejemplo:

<pref:etiq value="Cadena con ${'${'}expr}"/>

Mostraría: "Cadena con ${expr}"

EJEMPLOS

<c:if test="${bean1.a > 10}"/>
	...
</c:if>

Comprueba si una propiedad a de un bean es menor que 10.

<c:if test="true"/>
	...
</c:if>

Asigna directamente el valor verdadero a la condición.

3.5.2.2.2. Operadores

expr.campo
expr["campo"]
${empty A}

PRECEDENCIA

3.5.2.2.3. Nombres de variables

El lenguaje de expresiones evalúa un identificador o nombre de elemento mirando su valor como un atributo, según el comportamiento del método PageContext.findAttribute(String). Por ejemplo, si ponemos:

${valor}

Se buscará el atributo valor en los ámbitos de página (page), petición (request), sesión (session) y aplicación (application), y si lo encuentra devuelve su valor. Si no, se devuelve null.

Objetos implícitos

Cuando como nombre de atributo se utiliza alguno de los que el lenguaje de expresiones considera como implícitos, se devolverá el objeto asociado. Dichos objetos implícitos son:

EJEMPLOS

${sessionScope.profile}

Se obtiene el atributo profile de la sesión

${param.id}

Se obtiene el valor del parámetro id, o null si no se encuentra.

Palabras reservadas

Se tienen algunos identificadores que no podemos utilizar como nombres de atributos, como son and, eq, gt, true, instanceof, or, ne, le, false, empty, not, lt, ge, null, div y mod.

3.5.3. La librería Core

Los tags core incluyen tags de propósito general. En esta librería se tienen etiquetas para:

Los tags de esta librería se presentan con el prefijo "c".

1. Tags de propósito general

out

El tag out evalúa el resultado de una expresión y lo pone en el objeto JspWriter actual. Es equivalente a la sintaxis <%= ... %> de JSP.

SINTAXIS:

Dar el valor por defecto mediante un atributo default:

<c:out value="valor" 
 [escapeXML="true|false"] 
 [default="valor"]/>

Dar el valor por defecto mediante el cuerpo del tag:

<c:out value="valor" 
 [escapeXML="true|false"]>
	Valor por defecto
</c:out>
ATRIBUTOS:

EJEMPLO:

<c:out value="${datos.ciudad}" 
 default="desconocida"/>

Sacaría el valor del campo ciudad del objeto datos, o mostraría "desconocida" si dicho valor es nulo.

set

El tag set establece el valor de un atributo en cualquier campo JSP (page, request, session, application). Si el atributo no existe, se crea. 

SINTAXIS:

Dar valor a una variable utilizando el atributo value:

<c:set value="valor" var="variable" 
 [scope="page|request|session|application"]/>

Dar valor a una variable utilizando el cuerpo del tag:

<c:set var="variable" 
 [scope="page|request|session|application"]>
	Valor
</c:set>

Dar valor a una propiedad de un objeto utilizando el atributo value:

<c:set value="valor" target="objeto" 
 property="propiedad"/>

Dar valor a una propiedad de un objeto utilizando el cuerpo del tag:

<c:set target="objeto" property="propiedad">
	Valor
</c:set>

ATRIBUTOS:

EJEMPLO:

<c:set var="foo" value="2"/>

Asignaría a la variable foo el valor "2".

Otras Etiquetas

Existen otras etiquetas, como remove o catch, que no se comentan aquí.

2. Tags de control de flujo

if

El tag if permite ejecutar su código si se cumple la condición que contiene su atributo test.

SINTAXIS:

Sin cuerpo:

<c:if test="condicion" var="variable" 
 [scope="page|request|session|application"]/>

Con cuerpo:

<c:if test="condicion" [var="variable"] 
 [scope="page|request|session|application"]>
	Cuerpo
</c:if>

ATRIBUTOS:

EJEMPLO:

<c:if test="${visitas} > 1000">
<h1>¡Mas de 1000 visitas!</h1>
</c:if>

Sacaría el mensaje "¡Mas de 1000 visitas!" si el contador visitas fuese mayor que 1000.

choose

El tag choose permite definir varios bloques de código y ejecutar uno de ellos en función de una condición. Dentro del choose puede haber espacios en blanco, una o varias etiquetas when y cero o una etiquetas otherwise.

El funcionamiento es el siguiente: se ejecutará el código de la primera etiqueta when que cumpla la condición de su atributo test. Si ninguna etiqueta when cumple su condición, se ejecutará el código de la etiqueta otherwise (esta etiqueta, si aparece, debe ser la última hija de choose).

SINTAXIS:

<c:choose>
	<c:when test="condicion1">
		codigo1
	</c:when>
	<c:when test="condicion2">
		codigo2
	</c:when>
	...
	<c:when test="condicionN">
		codigoN
	</c:when>
	<c:otherwise>
		codigo
	</c:otherwhise>
</c:choose>

EJEMPLO:

<c:choose>
	<c:when test="${a} < 0">
	<h1>a menor que 0</h1>
	</c:when>
	<c:when test="${a} > 10">
	<h1>a mayor que 10</h1>
	</c:when>
	<c:otherwise>
	<h1>a entre 1 y 10</h1>
	</c:otherwhise>
</c:choose>

Sacaría el mensaje "a es menor que 0" si la variable a es menor que 0, el mensaje "a es mayor que 10" si es mayor que 10, y el mensaje "a esta entre 1 y 10" si no se cumple ninguna de las dos anteriores.

forEach

El tag forEach permite repetir su código recorriendo un conjunto de objetos, o durante un número determinado de iteraciones.

SINTAXIS:

Para iterar sobre un conjunto de objetos:

<c:forEach [var="variable"] items="conjunto" 
[varStatus="variableEstado"] [begin="comienzo"] 
[end="final"] [step="incremento"]>
	codigo
</c:forEach>

Para iterar un determinado número de veces:

<c:forEach [var="variable"] 
 [varStatus="variableEstado"] begin="comienzo" 
 end="final" [step="incremento"]>
	codigo
</c:forEach>

ATRIBUTOS:

EJEMPLO:

<c:forEach var="item" 
 items="${cart.items}"> 
	<tr> 
		<td>
		<c:out value="${item.valor}"/>
		</td> 
	</tr>
</c:forEach>

Muestra el valor de todos los items.

forTokens

El tag forTokens es similar al tag foreach, pero permite recorrer una serie de tokens (cadenas de caracteres), separadas por el/los delimitador(es) que se indique(n).

SINTAXIS

La sintaxis es la misma que foreach, salvo que se tiene un atributo delims, obligatorio.

ATRIBUTOS

EJEMPLO:

<c:forTokens var="item"
 items="un#token otro#otromas" delims="# "> 
	<tr> 
		<td>
		<c:out value="${item}"/>
		</td> 
	</tr>
</c:forEach>

Definimos dos separadores: el '#' y el espacio ' '. Así habrá 4 iteraciones, recorriendo los tokens "un", "token", "otro" y "otromas".

3. Tags de manejo de URLs

import

El tag import permite importar el contenido de una URL.

SINTAXIS:

Para copiar el contenido de la URL en una cadena:

<c:import url="url" [context="contexto"] 
 [var="variable"] 
 [scope="page|request|session|application"] 
 [charEncoding="codificacion"]>
	cuerpo para tags "param" opcionales
</c:import>

Para copiar el contenido de la URL en un Reader:

<c:import url="url" [context="contexto"] 
 varReader="variableReader" 
 [charEncoding="codificacion"]>
	codigo para leer del Reader
</c:import>

ATRIBUTOS:

EJEMPLO:

<c:import url="http://www.ua.es" 
 var="universidad">
	<c:out value="${universidad}"/>
</c:import>

Obtiene y muestra el contenido de la URL indicada.

param

El tag param se utiliza dentro del tag import y de otros tags (redirect, url) para indicar parámetros de la URL solicitada. Dentro del tag import sólo se utiliza si la URL se guarda en una cadena. Para los Readers no se emplean parámetros.

SINTAXIS:

Sin cuerpo:

<c:param name="nombre" value="valor"/>

Con cuerpo:

<c:param name="nombre">
	Valor
</c:param>

ATRIBUTOS:

EJEMPLO:

<c:import url="http://localhost/mipagina.jsp" 
 var="universidad"> 
	<c:param name="id" value="12"/>
</c:import>

Obtiene la página mipagina.jsp?id=12 (le pasa como parámetro id el valor 12).

Otras Etiquetas

Existen otras etiquetas, como url o redirect, que no se comentan aquí.

EJEMPLO

Vemos cómo quedaría el ejemplo visto en la sesión anterior para la librería request adaptado a la librería core. Partiendo del mismo formulario inicial:

<html>
<body>
	<form action="request.jsp">
		Nombre: 
		<input type="text" name="nombre">
		<br>
		Descripcion: 
		<input type="text" name="descripcion">
		<br>
		<input type="submit" value="Enviar">
	</form>
</body>
</html>

Para obtener los parámetros podríamos tener una página como esta:

<%@ taglib uri="core" prefix="c" %>
<%@ taglib uri="corert" prefix="c_rt" %>

<html>
<body>
	Nombre: <c:out value="${param.nombre}"/>
	<br>
	<% String descr = request.getParameter("descripcion"); %>
	Descripcion: <c_rt:out value="<%= descr %>"/>
	<br>
</body>
</html>

Hemos utilizado en este caso, como ejemplo, tanto los tags de la librería EL como los de la librería RT, para ver que se pueden emplear juntos. Para probar el ejemplo, copiad este fichero WAR en el directorio webapps de Tomcat, reiniciar el servidor y ejecutad:

http://localhost:8080/ejemplojstlcore/index.html 

3.5.4. La librería SQL

Los tags de la librería SQL permiten acceder y manipular información de bases de datos relacionales. Vienen definidos con el prefijo "sql".

Con esta librería podremos:

Estas acciones se realizan sobre objetos de tipo javax.sql.DataSource, que proporciona conexiones a la fuente de datos que representa. Así, se obtiene un objeto Connection de dicho DataSource, y con él podremos ejecutar sentencias y obtener resultados. Podemos definir el DataSource mediante la etiqueta setDataSource y luego acceder a ese DataSource con los atributos dataSource de las etiquetas de la librería.

setDataSource

El tag setDataSource permite definir el objeto DataSource con el que trabajar, y dejarlo asignado en una variable.

SINTAXIS:

<sql:setDataSource {dataSource="DataSource" | 
 url="url" [driver="driver"] [user="usuario"] 
 [password="password"]} [var="variable"]
 [scope="page|request|session|application"]/>

ATRIBUTOS:

Notar que se puede obtener el DataSource tanto indicándolo directamente en el atributo dataSource como indicando url, driver, user y password (los tres últimos opcionales).

query

El tag query permite definir una consulta (select) en una base de datos.

SINTAXIS:

Sin cuerpo:

<sql:query sql="consulta" var="variable" 
 [scope="page|request|session|application"] 
 [dataSource="DataSource"] [maxRows="max"] 
 [startRow="inicio"]/>

Con cuerpo donde indicar parámetros de la consulta:

<sql:query sql="consulta" var="variable" 
 [scope="page|request|session|application"] 
 [dataSource="DataSource"] [maxRows="max"] 
 [startRow="inicio"]>
	Campos <sql:param>
</sql:query>

Con cuerpo donde indicar la propia consulta y los parámetros de la consulta:

<sql:query var="variable" 
 [scope="page|request|session|application"] 
 [dataSource="DataSource"] [maxRows="max"] 
 [startRow="inicio"]>
	Consulta
	Campos <sql:param>
</sql:query>

ATRIBUTOS:

update

El tag update permite definir una actualización (insert, update, delete) en una base de datos.

SINTAXIS:

Sin cuerpo:

<sql:update sql="actualizacion" 
 [dataSource="DataSource"] [var="variable"] 
 [scope="page|request|session|application"]/>

Con cuerpo donde indicar parámetros de la actualización:

<sql:update sql="actualizacion" 
 [dataSource="DataSource"] [var="variable"] 
 [scope="page|request|session|application"]>
	Campos <sql:param>
</sql:update>

Con cuerpo donde indicar la propia actualización y los parámetros de la misma:

<sql:update [dataSource="DataSource"] [var="variable"] 
 [scope="page|request|session|application"]>
	Actualización
	Campos <sql:param>
</sql:update>

ATRIBUTOS:

transaction

El tag transaction permite agrupar dentro de él varios tags query y/o update, de forma que se define una transacción con ellos.

SINTAXIS:

<sql:transaction [dataSource="DataSource"] 
 [isolation="nivel"]>
	Conjunto de etiquetas query y/o update
</sql:transaction>

ATRIBUTOS:

param

El tag param permite definir parámetros a la hora de ejecutar consultas (query) o actualizaciones (update). Así, se sustituyen las marcas "?" que pueda haber en consultas o actualizaciones, por los valores de los parámetros que se indiquen.

SINTAXIS:

Sin cuerpo:

<sql:param value="valor"/>

Con cuerpo:

<sql:param>
	Valor
</sql:param>

ATRIBUTOS:

Los valores de los parámetros se sustituyen en las etiquetas en el orden en que se van definiendo. Veremos un ejemplo más adelante.

Otras Etiquetas

Se tienen otras etiquetas, como dateParam, que es igual que param pero para parámetros de tipo Date (fechas). No la veremos con más detalle aquí.

EJEMPLO

Vemos un ejemplo de uso de las etiquetas explicadas.

<sql:setDataSource 
	url="jdbc:mysql//localhost/miBD" 
	driver="org.gjt.mm.mysql.Driver"
	user="root" password="mysql"
	var="miDataSource"
/>
<sql:query var="miConsulta" 
 dataSource="${miDataSource}">
	SELECT nombre FROM nombres
</sql:query>

<c:foreach var="fila" items="${miConsulta.rows}">
	<c:out value="${fila.nombre}"/><br>
</c:foreach>
<sql:update dataSource="${miDataSource}">
	UPDATE nombres SET nombre='pepe' 
	WHERE id=1 OR id=2
</sql:update>
<sql:update dataSource="${miDataSource}">
	UPDATE nombres SET nombre='pepe' 
	WHERE id=? OR id=?
	<sql:param value="${param.id1}"/>
	<sql:param value="${param.id2}"/>
</sql:update>

Se sustituiría la primera ? por el primer param y la segunda ? por el segundo param. Los dos parámetros se toman en este ejemplo de la petición request. Pueden tomarse de cualquier otro sitio (constantes, variables, etc).

<sql:transaction dataSource="${miDataSource}">

	<sql:query var="miConsulta">
		SELECT nombre FROM nombres
	</sql:query>

	<c:foreach var="fila" 
	 items="${miConsulta.rows}">
		<c:out value="${fila.nombre}"/><br>
	</c:foreach>

	<sql:update dataSource="${miDataSource}">
		UPDATE nombres SET nombre='pepe' 
		WHERE id=? OR id=?
		<sql:param value="${param.id1}"/>
		<sql:param value="${param.id2}"/>
	</sql:update>

</sql:transaction>

EJEMPLO

Vemos cómo quedaría el ejemplo visto en la sesión anterior para la librería dbtags adaptado a la librería sql

<%@ taglib uri="sql" prefix="sql" %>
<%@ taglib uri="core" prefix="c" %>

<html>
<body>
	<sql:setDataSource url="jdbc:mysql://localhost/prueba" 
	 driver="org.gjt.mm.mysql.Driver" 
	 user="root" password="mysql" var="miDataSource"/>

	<sql:query var="miConsulta" dataSource="${miDataSource}">
		SELECT * FROM datos
	</sql:query>

	<c:forEach var="fila" items="${miConsulta.rows}">
	Nombre: <c:out value="${fila.nombre}"/><br>
	Descripcion: <c:out value="${fila.descripcion}"/><br>
	</c:forEach>
</body>
</html>

Utilizamos la librería sql y la core para recorrer todos los registros encontrados. Para probar el ejemplo, copiad este fichero WAR en el directorio webapps de Tomcat, reiniciar el servidor y ejecutad:

http://localhost:8080/ejemplojstlsql/dbtags.jsp 

3.5.5. La librería XML

Los tags de la librería de XML permiten tener acceso y procesar el contenido de documentos XML, utilizando la recomendación XPath del W3C como un lenguaje de expresiones local. Esta librería se utiliza con el prefijo "x".

Dentro de la librería, distinguimos entre los tags principales (que nos permiten explorar el contenido de un fichero XML), tags de control de flujo (para realizar código en función de dicho contenido), y tags de transformación (que permite transformar ficheros XML aplicando hojas de estilo XSLT).

3.5.6. La librería de internacionalización

Los tags de la librería de internacionalización (o I18n)  permiten adaptar aplicaciones Web a las convenciones de idioma y formato de los clientes para un determinado origen (locale). Los tags de esta librería se presentan con el prefijo "fmt".

En la librería tenemos tags para la internacionalización propiamente dicha, y para el formato de determinados elementos según la región (podremos formatear números, fechas, monedas, etc, dependiendo del idioma o región que se trate).