JSTL responde a la demanda de los desarrolladores de un conjunto de acciones JSP personalizadas para manejar las tareas que necesitan casi todas las p�ginas JSP, incluyendo procesamiento condicional, internacionalizaci�n, acceso a bases de datos y procesamiento XML. Esto acelarar� el desarrollo de JSPs m�s o menos eliminando la necesidad de elementos de scripting y los inevitables y dif�ciles de encontrar errores de sintaxis, y liberando el tiempo anteriormente gastado en desarrollar y aprender trillones de acciones personalizadas espec�ficas del proyecto para estas tareas comunes.
Este tutorial nos ense�a c�mo JSTL puede simplificar nuestra vida cuando usamos JSP, en grandes y peque�as aplicaciones. En este tutorial se ofrecer� una introducci�n a JSTL y mostrar� como usar las acctiones JSTL m�s comunes.
�Introducci�n a las Librer�as JSTL
JSTL 1.0 especifica un conjunto de librer�as de etiquetas basadas en el API JSP 1.2. Hay cuatro librer�as de etiquetas independientes, cada una contiene acciones personalizadas dirigidas a un �rea funcional espec�fica. Esta tabla lista cada librer�a con su prefijo de etiqueta recomendado y la URI por defecto:
Descripci�n | Prefijo | URI por Defecto |
---|---|---|
Core | c | http://java.sun.com/jstl/core |
XML Processing | x | http://java.sun.com/jstl/xml |
I18N & Formatting | fmt | http://java.sun.com/jstl/fmt |
Database Access | sql | http://java.sun.com/jstl/sql |
La Librer�a Core contiene acciones para las tareas rutinarias, como incluir o excluir una parte de una p�gina dependiendo de una condici�n en tiempo de ejecuci�n, hacer un bucle sobre una colecci�n de �tems, manipular URLs para seguimiento de sesi�n, y la correcta interpretaci�n del recurso objetivo, as� como acciones para importar contenido de otros recursos y redireccionar la respuesta a una URL diferente.
La Librer�a XML contiene acciones -- como puedes imaginar -- para procesamiento XML, inclu�do validar un documento XML y transformarlo usando XSLT. Tambi�n proporciona acciones para extraer parte de un documento XML validado, hacer bucles sobre un conjunto de nodos, y procesamiendo condicional basado en valores de nodos.
La Internationalizaci�n (i18n) y el formateo general est�n soportados por las acciones de la Librer�a I18N & Formatting. Podemos leer y modificar informaci�n almacenada en una base de datos con las acciones proporcionadas por la Librer�a Database Access.
M�s adelante, podemos esperar que todos los contenedores Web incluyan una implementaci�n de las librer�as JSTL, as� no tendremos que instalar ning�n c�digo adicional. Hasta que esto suceda, podemos descargar e instalar la implementaci�n de referencia (RI) de JSTL. Se ha desarrollado dentro del proyecto Apache Taglibs como una librer�a llamada Standard. Puedes bajarla desde aqu�:The JSTL RI: the Standard Library at Apache Taglibs.
Instalar RI es f�cil, s�lo hay que copiar los ficheros JAR del directorio lib de la distribuci�n al directorio WEB-INF/lib de nuestra aplicaci�n. Observa que JSTL 1.0 requiere un contenedor JSP 1.2, debemos asegurarnos de tener un contenedor compatible con JSP 1.2 antes de probar esto.
Para usar una librer�a JSTL, tanto si la implementaci�n est� incluida con el contenedor o con el RI, debemos declarar la librer�a usando una directiva taglib, como lo har�amos con una librer�a de etiquetas personalizadas normal:
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
Observa que siempre deber�amos usar la URI por defecto, incluso aunque la especificaci�n JSP nos permita sobreescribirla. Un contenedor puede generar c�digo optimizado para la acci�n JSTL en la clase correspondiente a una p�gina JSP. Esto puede resultar en un mejor rendimiento que cuando el c�digo generado llama a los manejadores de etiquetas a trav�s del API est�ndar. Sin embargo, s�lo cuando se usa la URI por defecto, es cuando el contenedor puede utilizar una implementaci�n optimizada.
�El Lenguaje de Expresi�n JSTL
Adem�s de las librer�as de etiquetas, JSTL 1.0 define un llamado Lenguaje de Expresiones (EL). EL es un luenguaje para acceder a datos de varias fuentes en tiempo de ejecuci�n. Su sintaxis es considerablemente m�s amigable que la de Java, que es el �nico lenguaje soportado directamente por la especificaci�n JSP 1.2. Todas las acciones JSTL reconocen expresiones EL en sus valores de atributos, y se podr�an desarrollar acciones personalizadas para que hicieran lo mismo. Se espera que EL sea incorporado dentro de la pr�xima versi�n de la especificaci�n JSP para mejorar su uso para acceder a datos sobre el lenguaje Java. Si es as�, podremos usar expresiones EL en un valor de atributo de una acci�n, o incluso en una plantilla de texto.
Si has usado JavaScript deber�as sentirte como en casa con EL. EL toma prestada de JavaScript la sintaxis para acceder a estructuras de datos tanto como propiedades de un objeto (con el operador .) como con elementos con nombres de un array (con el operador ["nombre"]). Las propiedades de los componentes JavaBeans y las entradas java.util.Map, que usan la clave como nombre de propiedad, pueden ser accedidas de esta forma. Aqu� tenemos algunos ejemplos:
${myObj.myProperty}$ ${myObj["myProperty"]}$ ${myObj[varWithTheName]}$
Como se ve aqu�, una exprei�n EL siempre debe estar encerrada entre los caracteres ${ y }$. Las dos primera expresiones acceden a una propiedad llamada myProperty en un objeto representado por una variable llamada myObj. La tercera expresi�n acceder a una propiedad con un nombre contenido en un variable, esta sintaxis se puede utilizar con cualquier expresi�n que eval�e el nombre de la propiedad.
El operador de acceso a array tambi�n se usa para datos representados como una colecci�n de elementos indexados, como un array Java o una java.util.List:
${myList[2]}$ ${myList[aVar + 1]}$
Adem�s de los operadores de propiedad y elemento array y los operadores aritm�ticos, relacionales, y l�gicos, hay un operador especial para comprobar si un objeto est� "vac�o" o no puede ser usado en una expresi�n EL. La siguiente tabla lista todos los operadores:
Operador | Description |
---|---|
. | Accede a una propiedad |
[] | Accede a un elemento de un array/lista |
() | Agrupa una subexpression |
+ | Suma |
- | Resta o negaci�n de un n�mero |
/ o div | Divisi�n |
% o mod | M�dulo (resto) |
== o eq | Comprueba Igualdad |
!= o ne | Comprueba desigualdad |
< o lt | Comprueba menor que |
> o gt | Comprueba mayor que |
<= o le | Comprueba menor o igual que |
>= o gt | Comprueba mayor o igual que |
&& o and | Comrpueba AND l�gico |
|| o or | Comprueba OR l�gico |
! o not | Complemento binario booleano |
empty | Comprueba un valor vac�o (null, string vac�o, o una collecci�n vac�a) |
Lo que no encontraremos en EL son sentencias como asignaciones, if/else, o while. Para este tipo de funcionalidades en JSP se usan los elementos Action, y EL no est� pensado para utilizarse como un lenguaje de programaci�n de prop�sito generar, s�lo un lenguaje de acceso a datos.
Por supuesto, los literales y las variables, tambi�n son parte del lenguaje. EL proporciona los siguientes literales, similares a los que proporcionan JavaScript, Java, y otros lenguajes similares:
Tipo de Literal | Descripci�n |
---|---|
String | Encerrado con comillas simples o dobles. Una comilla del mismo tipo dentro del string puede ser escapada con una barra invertida: (\' en un string encerrado con comillas simples; \" en un string encerrado con comillas dobles). El caracter de barra invertida debe se escapado como \\ en ambos casos. |
Integer | Un signo opcional (+ o -) seguido por digitos entre 0 y 9. |
Coma Floating | Lo mismo que un literal entero, excepto que usa un punto como separador de la parte fraccional y que se puede especificar un exponente con e o E, seguido por un literal entero. |
Boolean | true o false. |
Null | null. |
Cualquier objeto en uno de los �mbitos de JSP (p�gina, solicitud, sesi�n o aplicaci�n) se puede utilizar como una variable en una expresi�n EL. Por ejemplo, si tenemos un bean con una propiedad firstName en el �mbito de la solicitud bajo el nombre customer, esta expresi�n EL representa el valor de la propiedad firstName del bean.
${customer.firstName}
Pero no se para aqu�. EL tambi�n hace que la informaci�n de la solicitud y la informaci�n general del contenedor est� disponible como un conjunto de variables impl�citas:
Variable | Descripci�n |
---|---|
param | Una collection de todos de los par�metros de la solicitud como un s�lo valor string para cada par�metro. |
paramValues | Una collection de todos los valores de los par�metros de la solicitud como un array de valores string por cada par�metro. |
header | Una collection de todas las cabeceras de solicitud como un s�lo valor string por cada cabecera. |
headerValues | Una collection de todos los valores de cabecera de la solicitud como un array de valores string por cada cabecera. |
cookie | Una collection con todas las cookies de la solicitud en un s�lo ejemplar de javax.servlet.http.Cookie por cada cokkie. |
initParams | Una collection de todos los par�metros de inicializaci�n de la aplicaci�n en un s�lo valor string por cada par�metro. |
pageContext | Un ejemplar de la clase javax.servlet.jspPageContext. |
pageScope | Una collection de todos los objetos en el �mbito de la p�gina. |
requestScope | Una collection de todos los objetos en el �mbito de la solicitud. |
sessionScope | Una collection de todos los objetos en el �mbito de la sesi�n. |
applicationScope | Una collection de todos los objetos en el �mbito de la aplicaci�n. |
Las cinco primeras variables impl�citas de la tabla nos ofrecen acceso a los valores de par�metros, cabeceras y cookies de la solicitud actual. Aqu� hay un ejemplo de c�mo acceder a un par�metro de solicitud llamado listType y a la cabecera User-Agent:
${param.listType} ${header['User-Agent']}
Observa como debemos usar la sintaxis de array para la cabecera, porque el nombre incluye un gui�n; con la sintaxis de propiedad, ser�a interpretado como la expresi�n vairable header.User menos el valor de una variable llamada Agent.
La variable initParameter proporciona acceso a los par�metros de inicializaci�n que se definen en el fichero web.xml de la aplicaci�n. La variable pageContext tiene varias propiedades que proporcionan acceso al objeto servlet que representa la solicitud, la respuesta, la sesi�n y la aplicaci�n, etc.
Las cuatro �ltimas variables son colecciones que contienen todos los objetos de cada �mbito espec�fico. Podemos usarlas para limitar la b�squeda de un objeto en s�lo un �mbito en lugar de buscar en todos ellos, lo que est� por defecto si no se especifica ning�n �mbito. En otras palabras, si hay un objeto llamado customer en el �mbito de sesi�n, estas dos primeras expresiones encuentran el mismo objeto, pero la tercera vuelve vac�a:
${customer} ${sessionScope.customer} ${requestScope.customer}
Todas las acciones JSTL aceptan expresiones EL como valores de atributo, para todos los atributos excepto para var y scope, porque estos valores de atributo podr�an usarse para chequear el tipo en el momento de la traducci�n en una futura versi�n. Hay un atributo de una acci�n JSTL adicional que no toma un valor de expresi�n EL, pero s�lo se usa en la librer�a XML, por eso lo ignoraremos por ahora. Se pueden usan una o m�s expresiones EL en el mismo valor de atributo, y el texto fijo y las expresiones EL se pueden mezclar en el mismo valor de atributo:
First name: <c:out value="${customer.firstName}" /> <c:out value="First name: ${customer.firstName}" />
Antes de saltar a ver ejemplos de utilizaci�n de las acciones Core, dej�me cualificar algo que dijimos anteriormente: todas las acciones JSTL de la librer�a EL aceptan expresiones EL. Realmente hay un conjunto parelelo de librer�as JSTL, llamado conjunto de librer�a RT. que s�lo acepta expresiones Java del viejo estilo:
First name: <c_rt:out value="<%= customer.getFirstName() %>" />
�Procesamiento Condicional y Bucles
Veamos algunos ejemplos de c�mo podemos usar el JSTL condicional y las acciones de iteracci�n: <c:if>; <c:choose>, <c:when>, y un triple <c:otherwise>; y <c:forEach>. Por el camino, tambi�n usaremos acciones de salida b�sica y de selecci�n de variables: <c:out> y <c:set>.
<c:if> nos permite incluir, o procesar, condicionalmente una parte de una p�gina, dependiendo de la informaci�n durante la ejecuci�n. Este ejemplo incluye un saludo personal si el usuario es un visitante repetitivo, seg�n lo indica la presencia de una cokkie con el nombre del usuario:
<c:if test="${!empty cookie.userName}"> Welcome back <c:out value="${cookie.userName.value}" /> </c:if>
El valor del atributo test es una expresi�n EL que chequea si la cookie existe. El operador empty combinado con el operador "not" (!) significa que eval�a a true si el cookie no existe, haciendo que el cuerpo del elemento sea procesado. Dentro del cuerpo, la acci�n <c:out> a�ade el valor de la cookie a la respuesta. As� de sencillo.
Pasar a trav�s de una colecci�n de datos es casi tan sencillo. Este fragmento itera sobre una colecci�n de filas de una base de datos con informaci�n del tiempo de diferentes ciudades:
<c:forEach items="${forecasts.rows}" var="${city}"> City: <c:out value="${city.name}" /> Tomorrow's high: <c:out value="${city.high}" /> Tomorrow's low: <c:out value="${city.low}" /> </c:forEach>
La expresi�n EL para el valor items obtiene el valor de la propiedad rows desde un objeto representado por la variable forecasts. Como aprenderemos m�s adelante, la acciones de bases de datos de JSTL representan un resultado de consulta como un ejemplar de una clase llamada javax.servlet.jsp.jstl.sql.Result. Esta clase se puede utilizar como un bean con varias propiedades. La propiedad rows contiene un array de ejemplares java.util.SortedMap, donde cada uno representa una fila con valores de columnas. La acci�n <c:forEach> procesa su cuerpo una vez por cada elemento de la colecci�n especificado por el atributo items. Adem�s de con arrays, la acci�n funciona con cualquier otro tipo de dato que represente una colecci�n, como ejemplares de las clases java.util.Collection o java.util.Map.
Si se especifica el atributo var, el elemento actual de la colecci�n se hace disponible para las acciones del cuerpo como una variable con el nombre especificado. Aqu� se llamaba city y, como la colecci�n es un array de maps, esta variable contiene un nuevo map con valores de columnas cada vez que se procesa el cuerpo. Los valores de las columnas se a�aden a la respuesta por el mismo tipo de acciones <c:out> que hemos visto en ejemplos anteriores.
Para ilustrar el uso del resto de las acciones condicionales, extendamos el ejemplo de iteracci�n para procesar s�lo un conjunto fijo de filas por cada p�gina solicitada, a�adamos enlaces "Previous" y "Next" a la misma p�gina. El usuario puede entonces pasar sobre los resultados de la base de datos, mirando unas pocas fila cada vez, asumiendo que el objeto Result se ha grabado en el �mbito de la sesi�n. Aqu� est� c�mo procesar s�lo algunas filas:
<c:set var="noOfRows" value="10" /> <c:forEach items="${forecasts.rows}" var="${city}" begin="${param.first}" end="${param.first + noOfRows - 1}"> City: <c:out value="${city.name}" /> Tomorrow's high: <c:out value="${city.high}" /> Tomorrow's low: <c:out value="${city.low}" /> </c:forEach>
La acci�n <c:set> selecciona una variable con el valor especificado por el atributo value; que puede ser un valor est�tico, como en este ejemplo, o una expresi�n EL. Tambi�n podemos especificar el �mbito de la variable con el atributo scope (page, request, session o application). En este ejemplo, hemos seleccionado una variable llamada noOfRows a 10 en el �mbito de la p�gina (por defecto). Este es el n�mero de filas que mostraremos en cada solicitud.
El <c:forEach> en este ejemplo toma los mismos valores para los atributos items y var como antes, pero hemos a�adido dos nuevos atributos:
- El atributo begin toma el �ndice (base 0) del primer elemento de la colecci�n a procesar. Aqu� se selecciona al valor de un par�metro de solicitud llamado first. Para la primera solicitud, este par�metro no est� disponible, por eso la expresi�n se eval�a a 0; en otras palabtas, la primea fila.
- El atributo end especifica el �ndice del �ltimo elemento de la colecci�n a procesar. Aqu� lo hemos seleccionado al valor del par�metro first m�s noOfRows menos uno. Para la primera solicitud, cuando no existe el par�metro de la solicitud, este resultado es 9, por eso la acci�n itera sobre los �ndices del 0 al 9.
Luego a�adimos los enlaces "Previous" y "Next":
<c:choose> <c:when test="${param.first > 0}"> <a href="foreach.jsp?first=<c:out value="${param.first - noOfRows}"/>"> Previous Page</a> </c:when> <c:otherwise> Previous Page </c:otherwise> </c:choose> <c:choose> <c:when test="${param.first + noOfRows < forecasts.rowsCount}"> <a href="foreach.jsp?first=<c:out value="${param.first + noOfRows}"/>"> Next Page</a> </c:when> <c:otherwise> Next Page </c:otherwise> </c:choose>
El <c:choose> agrupa una o m�s acciones <c:when>, cada una especificando una condici�n booleana diferente. La acci�n <c:choose> chequea cada condici�n en orden y s�lo permite la primera acci�n <c:choose> con una condici�n que se eval�e a true para procesar su cuerpo. El cuerpo <c:choose> tambi�n podr�a contener un <c:otherwise>. Su cuerpo s�lo se procesa si ninguna de las condiciones de los <c:when> es true.
En este ejemplo, la primera acci�n <c:when> comprueba si el par�metro first es mayor que cero, es decir, si la p�gina muestra un subconjunto de filas distinto del primero. Si esto es cierto, el cuerpo de la acci�n <c:when> a�ade un enlace a la misma p�gina con un par�metro first seleccionado al �ndice del subconjunto anterior. Si no es true, se procesa el cuerpo de la acci�n <c:otherwise>, a�adiendo s�lo el texto "Previous Page". El segundo bloque <c:choose> proporciona l�gica similar para a�adir el enlace "Next Page".
�Procesar URLs
Los ejemplos anteriores funcionan bien mientras se utilicen las cookies para seguimiento de sesi�n. No est� todo dado; el navegador podr�a tener desactivadas las cookies, o no soportarlas. Por lo tanto es una buena idea activar el contenedor para usar la reescritura de URL como backup de los cookies. La reescritura de URL, como podr�as conocer, significa poner el ID de la sesi�n en todas las URLs usadas en los enlaces y formularios de la p�gina. Una URL reescrita se parece a algo como esto:
myPage.jsp;jspsessionid=ah3bf5e317xmw5
Cuando el usuario pulsa en una l�nea como esta, el identificador de sesi�n ID se env�a al contenedor como parte de la URL. La librer�a coraz�n de JSTL incluye la acci�n <c:url>, que tiene cuidado de la reescritura de URL por nosotros. Aqu� est� como podemos usarla para mejorar la generaci�n del enlace "Previous Page" del ejemplo anterior:
<c:url var="previous" value="foreach.jsp"> <c:param name="first" value="${param.first - noOfRows}" /> </c:url> <a href="<c:out value="${previous}"/>">Previous Page</a>
<c:url> soporta un atributo var, usado para especificar una variable para contener la URL codificada, y un atributo value para contener la URL a codificar. Se pueden especificar par�metros string para solicitar la URL usando acciones <c:param>. Los caracteres especiales en los par�metros especificados por elementos anidados son codificados (si es necesario) y luego a�adidos a la URL como par�metros string de la consulta. El resultado final se pasa a trav�s del proceso de reescritura de URL, a�adiendo un ID de sesi�n si est� desactivado el seguimiento de sesi�n usando cookies. En este ejemplo, se utiliza la URL codificada como un valor de atributo href en un elemento de enlace HTML.
<c:url> tambi�n realiza otro buen servicio. Como podr�a preocuparte, las URLs relativas en elementos HTML tambi�n deben ser relativas a la p�gina que los contiene o al directorio ra�z del servidor (si empiezan con una barra inclinada). La primera parte del path de una URL de una p�gina JSP se llama path de contexto, y podr�a variar de instalaci�n a instalaci�n. Por lo tanto deber�amos evitar codificar el path de contexto en las p�ginas JSP. Pero algunas veces realmente querremos utilizar un path de URL relativo al servidor en elemento HTML; por ejemplo cuando necesitamos referirnos a un fichero de imagen localizado en el directorio /images compartido por todas las p�ginas JSP. Las buenas noticias es que si especificamos una URL con un barra inclinada como el valor <c:url>, lo convierte en un path relativo al servidor. Por ejemplo, en una aplicaci�n con el path de contexto /myApp, la acci�n <c:url> convierte el path a /myApp/images/logo.gif:
<c:url value="/images/logo.gif" />
Hay unas cuantas acciones m�s relacionadas con las URLs en la librer�a coraz�n. La acci�n <c:import> es una acci�n m�s flexible que la acci�n est�ndar <jsp:include>. Podemos usarla para incluir contenido desde los recursos dentro de la misma aplicaci�n Web, desde otras aplicaciones Web en el mismo contenedor, o desde otros servidores, usando protocolos como HTTP y FTP. La accion <c:redirect> nos permite redirigir a otro recurso en la misma aplicaci�n Web, en otra aplicaci�n Web o en un servidor diferente. Ambas acciones son f�ciles de usar, por eso te dejamos como ejercicio que las pr�ctiques.