8. Facelets, JSTL y lenguajes de expresiones

8.1. JavaServer Faces y Facelets

JavaServer Faces (JSF) es un framework para la creación de aplicaciones web en el lado del servidor. Dentro de este framework encontramos:

  • Una API que nos proporciona componentes que podemos utilizar en la interfaz de nuestra aplicación y nos permite gestionar sus eventos y su validación en el lado del servidor. Ejemplos de componentes son los botones, cuadros de texto, imágenes, mensajes de texto, etc.

  • Librerías de etiquetas con las que podemos añadir los componentes anteriores a una página web sin necesidad de introducir código Java. Por ejemplo, encontramos etiquetas como <h:form>, <h:inputText>, <h:graphicImage>, <h:selectOneMenu>, etc.

Una página JSF se representa mediante un árbol de componentes, al que se llama vista.

Los componentes que constituyen la vista son elementos reutilizables y configurables como por ejemplo botones, campos de texto, tablas, etc. Todos los componentes se implementan en clases que heredan de UIComponentBase. Estas clases implementan la funcionalidad del componente, pero no la forma de mostrarlo. Esta separación permite crear de forma sencilla varias formas de mostrar un componente reutilizando su funcionalidad. La forma de mostrar un componente se implementa mediante un objeto de tipo Renderer.

Podemos tener diferentes etiquetas para un mismo componente, de manera que cada una de ellas lo muestre de forma distinta. Por ejemplo, el componente UISelectOne tiene la funcionalidad de permitir seleccionar un único elemento de una lista, pero puede presentarse mediante tres renderers alternativos, por lo que tenemos disponibles tres etiquetas para este mismo componente:

  • <h:selectOneListbox>: Se muestra como un cuadro de lista donde aparecen todas las opciones.

  • <h:selectOneMenu>: Se muestra como un menú desplegable.

  • <h:selectOneRadio>: Se muestra como botones de radio.

Cada etiqueta de la librería HTML de JSF relaciona un componente con un renderer determinado para mostrarlo.

Una ventaja de JSF es que separa claramente la presentación de la lógica. Existen diferentes opciones para crear la vista en JSF. Se podría utilizar cualquier tipo de componente en el que podamos incluir las etiquetas que proporciona, como son por ejemplo los JSPs, pero la opción recomendada es la tecnología de Facelets. Vamos a centrarnos en estudiar los Facelets y los distintos elementos que podemos incluir en ellos.

8.2. Introducción a los Facelets

Los Facelets consisten en páginas creadas en XHTML, dentro de las cuales podemos utilizar los siguientes elementos:

  • Librerías de tags de Facelets, JSF y JSTL: Se utilizan fundamentalmente para crear los diferentes componentes de la interfaz de la página, además de especificar la forma de validarlos o de presentar los datos.

  • Lenguaje de expresiones (EL): Nos permite relacionar los componentes anteriores con datos de nuestra aplicación, como por ejemplo aquellos contenidos en managed beans, o aquellos que recibimos como parámetros de la petición. Podremos utilizar este lenguaje en el cuerpo de la página o en los atributos de las etiquetas anteriores para consultar o guardar datos en managed beans, realizar operaciones con ellos, etc.

  • Plantillas de componentes y páginas: Podemos definir la estructura de contenido de nuestras páginas mediante una plantilla, y aplicar esta plantilla a todas ellas. Por ejemplo podremos definir en la plantilla bloques para la cabecera, pie, menú lateral, etc.

En los siguientes apartados estudiaremos con detalle cada uno de los componentes anteriores.

Para crear una aplicación que utilice Facelets deberemos realizar lo siguiente:

  • Mapear el servet de JSF (FacesServlet) a un determinado patrón de URL (por ejemplo *.xhtml).

  • Crear managed beans. Desde los Facelets accederemos a los datos de nuestra aplicación a través de ellos.

  • Crear páginas utilizando las etiquetas de componentes. Los Facelets serán estas página XHTML que utilizarán dichas etiquetas y accederán a los datos mediante managed beans.

8.2.1. Mapeo del servlet de JSF

Para poder usar Facelets deberemos mapear el servlet de JSF a un determinado patrón de URL para que se encargue de procesar todas las páginas que se ajusten a dicho patrón. Normalmente quedará mapeado al patrón *.xhtml, para que todas las páginas que tengan dicha extensión sean procesadas como Facelets. De no añadir este mapeo se tratarían como páginas estáticas.

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
Esto puede hacerse de forma automática desde IntelliJ. Bastará con situarse sobre el directorio WEB-INF, pulsar con el botón derecho y seleccionar New > XML Configuration File > Faces Config. Con esto se creará en WEB-INF en fichero de configuración de JSF (faces-config.xml), y añadirá al descriptor de despliegue la configuración del Faces Servlet que se encargará de procesar los Facelets.
Creación de la configuración de JSF

8.2.2. Estructura básica de un Facelet

A continuación mostramos la estructura básica que tiene un Facelet:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"> (1)

    <h:head> (2)
        <title>Mi primer Facelet</title>
    </h:head>
    <h:body> (3)
        <!-- Contenido de la página -->
    </h:body>
</html>
1 Declaración de las librerías de etiquetas a utilizar. En este caso declaramos únicamente la librería HTML de JSF asignándole el prefijo h.
2 En lugar de <head> se suele utilizar una etiqueta equivalente de la librería HTML de JSP. Al haber sido declarada con prefijo h, dicha etiqueta será <h:head>.
3 Al igual que <head>, para la etiqueta <body> también utilizamos la etiqueta equivalente de JSF.
Podemos crear un Facelet desde IntelliJ pulsando con el botón derecho sobre el directorio donde lo queramos añadir (por ejemplo webapp) y seleccionando New > JSF/Facelets.
Creación de un Facelet con IntelliJ

8.2.3. Declaración de librerías de etiquetas

En el ejemplo anterior hemos declarado únicamente la librería HTML de JSF. Podríamos añadir a la declaración otras librerías de etiquetas como por ejemplo las que se muestran a continución:

Librería URI Prefijo Descripción

JSF HTML Tag Library

http://xmlns.jcp.org/jsf/html

h

Componentes de la UI de la página

JSF Core Tag Library

http://xmlns.jcp.org/jsf/core

f

Funciones y acciones

JSTL Core Tag Library

http://xmlns.jcp.org/jsp/jstl/core

c

Etiquetas de propósito general de JSTL

JSTL Functions Tag Library

http://xmlns.jcp.org/jsp/jstl/functions

fn

Funciones de JSTL

Cada librería tiene un prefijo definido por convención que utilizaremos normalmente al declararla, pero podríamos cambiarlo. Por ejemplo

<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">

En este caso hemos declarado la librería HTML de JSF con prefijo h y la librería Core de JSTL con prefijo c. Podríamos también declararlas de la siguiente forma:

<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:html="http://xmlns.jcp.org/jsf/html"
      xmlns:core="http://xmlns.jcp.org/jsp/jstl/core">

En este último caso las etiquetas de la librería HTML de JSF se escribirían con prefijo html, como por ejemplo <html:head> y <html:body>. De la misma forma, las etiquetas de la librería Core de JSTL se escribirían con prefijo core.

8.3. Lenguaje de expresiones

Para poder conectar las etiquetas de un Facelet con los datos de nuestra aplicación necesitaremos introducir en ellas expresiones que hagan referencia a los managed beans que hayamos definido. Estas expresiones se definen mediante el llamado lenguaje de expresiones (EL).

8.3.1. Introducción al lenguaje de expresiones

A partir de la versión 2.0 de JSP se introdujo en las páginas un lenguaje de expresiones que permite hacer referencia a objetos de la aplicación sin necesidad de introducir código Java. En realidad, dicho lenguaje se implantó con las primeras versiones de JSTL, una librería de etiquetas JSP que se considera estándar, y que permitía utilizar este lenguaje de expresiones en los atributos de dichas etiquetas, constituyendo una característica muy importante de JSTL.

Cualquier elemento que pertenezca al lenguaje de expresiones irá englobado dentro de la marca ${…​}. En ella podremos colocar nombres de managed beans, parámetros de petición HTTP, elementos de una sesión…​ etc.

Por ejemplo, si previamente hemos creado un managed bean miBean, podríamos acceder a él desde la página (JSP o Facelet) con algo como:

<h3>El valor del bean es ${miBean}</h3>

Si lo que queremos es mostrar el parámetro password que hemos tomado de un formulario, para sacarlo por pantalla o guardarlo en alguna base de datos, podríamos acceder a él con algo como:

Accediendo al parámetro ${param.password}

También se pueden utilizar, como veremos, expresiones más complejas, que se evalúan desde el contenedor de Facelets o JSP. Por ejemplo, si tenemos un bean edad para una persona y queremos comprobar si dicha persona es mayor de edad, podríamos poner:

${edad > 18}

Y luego utilizar el resultado de esta expresión en otras zonas (por ejemplo, etiquetas condicionales de JSTL) para realizar la acción correspondiente.

Se describe a continuación, y a grandes rasgos, el lenguaje de expresiones incluido en JSTL 1.0, y en general a partir de JSP 2.0. 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.

8.3.2. Atributos y expresiones

Como hemos comentado anteriormente, podremos invocar a este lenguaje desde cualquier lugar de nuestra página (en JSP 2.0 o Facelets), o dentro de un atributo de una etiqueta, mediante el elemento ${…​}:

${expresion}

Esta expresión podrá estar:

  • Por sí sola dentro de un atributo de una etiqueta (por ejemplo de JSTL):

    <c:set var="miVariable" 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.

  • Combinada con texto dentro de un atributo de una etiqueta (por ejemplo de JSTL):

    <c:set var="miVariable" 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 en el que esté (si está dentro de algún atributo).

  • Fuera de atributos, dentro del contenido HTML de la página JSP:

    <h3>Hola, esto es una página</h3>
    <p>Y aquí ponemos una expresión ${expresion}, para mostrar su valor</p>

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

Cadena con ${'${'}expr}

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

8.3.3. Operadores

  • Operadores [ ] y .: se unifican los operadores [ ] y . de forma que son equivalentes:

${expr.campo}
${expr["campo"]}
  • Operadores aritméticos:

  • +, -, *, /: suma, resta, multiplicación y división

  • div: división entera

  • %, mod: resto (se mantienen los dos por compatibilidad con XPath y ECMAScript)

  • -: cambio de signo

  • Operadores relacionales:

  • >, gt: mayor que

  • <, lt: menor que

  • >=, ge: mayor o igual que

  • , le: menor o igual que

  • ==, eq: igual que

  • !=, ne: distinto que

  • Operadores lógicos:

  • &&, and: Y lógica

  • ||, or: O lógica

  • !, not: NO lógica

  • Operador empty: utilizado delante de un elemento, para indicar si el elemento es nulo o vacío (devolvería true) o no (devolvería false). Por ejemplo:

${empty A}

PRECEDENCIA

  • [ ], .

  • ( )

  • - (cambio de signo), not, !, empty

  • *, /, div, %, mod

  • +, -

  • <, >, , >=, lt, gt, le, ge

  • ==, !=, eq, ne

  • &&, and

  • ||, or

EJEMPLOS

// Daría true si el parametro nombre no se ha enviado
${empty param.nombre}

// Devolvería el resultado de la suma de ambas variables
${num1 + num2}

// Devolvería true si valor1 es mayor o igual que valor2
${valor1 >= valor2}

// Daría true si v1 fuese distinto a v2, y v3 menor que v4
${v1 ne v2 and v3 < v4}

8.3.4. 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:

  • pageContext: el objeto PageContext actual

  • pageScope, requestScope, sessionScope, applicationScope: para obtener valores de atributos de página / petición / sesión / aplicación, respectivamente.

  • param: para obtener el valor de un parámetro de petición. Se obtiene un tipo String (utilizando el método ServletRequest.getParameter(String))

  • paramValues: para obtener los valores de un parámetro de petición. Se obtiene un tipo String[] (utilizando el método ServletRequest.getParameterValues(String)).

  • header: para obtener el valor de un parámetro de cabecera. Se obtiene un tipo String (utilizando el método ServletRequest.getHeader(String))

  • headerValues: para obtener los valores de un parámetro de cabecera. Se obtiene un tipo String[] (utilizando el método ServletRequest.getHeaderValues(String)).

  • cookie: para obtener el valor de una cookie. Se obtiene un objeto Cookie. Las cookies se buscan con HttpServletRequest.getCookies()

  • initParam: para obtener el valor de un parámetro de inicialización. Se obtiene un tipo String (utilizando el método ServletContext.getInitParameter(String))

EJEMPLOS

${sessionScope.profile}

Se obtiene el atributo profile de la sesión

${param.id}

Se obtiene el valor del parámetro id de la petición, 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.

8.3.5. Lenguaje de expresiones y CDI

Podemos hacer que los beans CDI sean accesibles desde el lenguaje de expresiones. Por defecto no serán accesibles, para poder acceder a ellos deberemos darles un nombre. Esto lo haremos anotando el bean con la etiqueta @Named:

@Named("hola")
public class HolaMundo {
    public String getSaludo() {
        return "Hola mundo!";
    }
}

Podremos utilizar este bean desde EL de la siguiente forma:

${hola.saludo}

8.4. Librerías de etiquetas

Las librerías de etiquetas (tag libs) son conjuntos de etiquetas HTML personalizadas que permiten encapsular determinadas acciones, mediante un código Java subyacente. Es decir, se define lo que va a ejecutar la etiqueta mediante código Java, y luego se le da un nombre a la etiqueta para llamarla desde los Facelets o páginas JSP, estableciendo la relación entre el nombre de la etiqueta y el código Java que la implementa. Por ejemplo, una página JSP que hace uso de librerías de tags podría tener este aspecto:

<%@ taglib uri="ejemplo" prefix="ej" %>
<html>
<body>
<h1>Ejemplo de librerias de tags</h1>
<ej:mitag>Hola a todos</ej:mitag>
<br>
<ej:otrotag/>
</body>
</html>

donde se utiliza una librería llamada ejemplo, que se simplifica con el prefijo ej, de forma que todos los tags de dicha librería se referencian con dicho prefijo y dos puntos, teniendo la forma ej:tag. Se utilizan así los tags mitag y otrotag.

Podríamos incluir dicha librería en un Facelet de la siguiente forma:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ej="ejemplo">

    <h:body>
      <h1>Ejemplo de librerias de tags</h1>
      <ej:mitag>Hola a todos</ej:mitag>
      <br/>
      <ej:otrotag/>
    </h:body>
</html>

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.

8.4.1. Librería de etiquetas JSTL

JSTL contiene una gran variedad de tags que permiten hacer distintos tipos de tareas, subdivididas en áreas. Así, JSTL proporciona varias sublibrerías, 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):

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

Functions

http://java.sun.com/jstl/ea/functions

fn

Las URIs mostradas en esta tabla pueden ser utilizadas en páginas JSP. En Facelets/JSF sólo se han incorporado las librerías Core y Functions, siendo sus URIs las siguientes:

AREA URI PREFIJO

Core

http://java.sun.com/jstl/ea/core

c

Functions

http://xmlns.jcp.org/jsp/jstl/functions

fn

Cada una de estar librerías se encarga de las siguientes funcionalidades:

  • Core se utiliza para funciones de propósito general (manejo de expresiones, sentencias de control de flujo, etc).

  • XML se emplea para procesamiento de ficheros XML.

  • La librería de internationalización se usa para dar soporte a páginas multilenguaje, y a multiformatos de números, monedas, etc, en función de la región en que se tenga la aplicación.

  • SQL sirve para acceder y manipular bases de datos relacionales.

  • Functions contiene una serie de funciones de propósito general, como por ejemplo funciones para la manipulación de cadenas.

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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:c="http://java.sun.com/jstl/ea/core"
      xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions">

    <h:body>
        ...
    </h:body>
</html>
La librería Core

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

  • Funciones de propósito general: evaluar expresiones, establecer valores de parámetros, etc.

  • Funciones de control de flujo: condiciones para ejecutar unos bloques de código u otro, iteradores, etc.

  • Funciones de acceso a URLs: para importar URLS en la página actual, etc.

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

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, y también a poner directamente una expresión ${…​} en su lugar, aunque admite algunos atributos adicionales.

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:

  • value: expresión que se tiene que evaluar.

  • escapeXML: a true (valor por defecto) indica que los caracteres <, >, &, ', " que haya en la cadena resultado se deben convertir a sus códigos correspondientes (<, >, &, ', ", respectivamente).

  • default: valor por defecto si el resultado es null. Se puede indicar por el atributo o por el cuerpo del tag.

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 ámbito (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:

  • value: valor que se asigna. Podemos dar el valor con este atributo o con el cuerpo del tag.

  • var: variable a la que se asigna el valor.

  • scope: ámbito de la variable a la que se asigna el valor.

  • target: objeto al que se le modifica una propiedad. Debe ser un objeto JavaBeans con una propiedad propiedad que pueda establecerse, o un objeto java.util.Map.

  • property: propiedad a la que se le asigna valor en el objeto target.

EJEMPLO:

<c:set var="foo" value="2"/>
...
<c:out value="${foo}"/>

Asignaría a la variable foo el valor "2", y luego mostraría el valor por pantalla.

Otras Etiquetas

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

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:

  • test: condicion que debe cumplirse para ejecutar el if.

  • var: variable donde se guarda el resultado de evaluar la expresión. El tipo de esta variable debe ser Boolean.

  • scope: ámbito de la variable a la que se asigna el valor de la condición.

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:

  • var: variable donde guardar el elemento actual que se está explorando en la iteración. El tipo de este objeto depende del tipo de conjunto que se esté recorriendo.

  • items: conjunto de elementos que recorre la iteración. Pueden recorrerse varios tipos:

    • Array: tanto de tipos primitivos como de tipos complejos. Para los tipos primitivos, cada dato se convierte en su correspondiente wrapper (Integer para int, Float para float, etc)

    • java.util.Collection: mediante el método iterator() se obtiene el conjunto, que se procesa en el orden que devuelve dicho método.

    • java.util.Iterator

    • java.util.Enumeration

    • java.util.Map:el objeto del atributo var es entonces de tipo Map.Entry, y se obtiene un Set con los mapeos. Llamando al método iterator() del mismo se obtiene el conjunto a recorrer.

    • String: la cadena representa un conjunto de valores separados por comas, que se van recorriendo en el orden en que están.

  • varStatus: variable donde guardar el estado actual de la iteración. Es del tipo javax.servlet.jsp.jstl.core.LoopTagStatus.

  • begin: indica el valor a partir del cual comenzar la iteración. Si se está recorriendo un conjunto de objetos, indica el índice del primer objeto a explorar (el primero es el 0), y si no, indica el valor inicial del contador. Si se indica este atributo, debe ser mayor o igual que 0.

  • end: indica el valor donde terminar la iteración. Si se está recorriendo un conjunto de objetos, indica el índice del último objeto a explorar (inclusive), y si no, indica el valor final del contador. Si se indica este atributo, debe ser mayor o igual que begin.

  • step: indica cuántas unidades incrementar el contador cada iteración, para ir de begin a end. Por defecto es 1 unidad. Si se indica este atributo, debe ser mayor o igual que 1.

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:

  • var: igual que para foreach

  • items: cadena que contiene los tokens a recorrer

  • delims:conjunto de delimitadores que se utilizan para separar los tokens de la cadena de entrada (colocados igual que los utiliza un StringTokenizer).

  • varStatus: igual que para foreach

  • begin: indica el índice del token a partir del cual comenzar la iteración.

  • end: indica el índice del token donde terminar la iteración.

  • step: igual que para foreach.

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".

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:

  • url: URL de la que importar datos

  • context: contexto para URLs que pertenecen a contextos distintos al actual.

  • var: variable (String) donde guardar el contenido de la URL

  • varReader: variable (Reader) donde guardar el contenido de la URL

  • scope: ámbito para la variable var

  • charEncoding: codificación de caracteres de la URL

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:

  • name: nombre del parámetro

  • value: valor del parámetro. Puede indicarse bien mediante este atributo, bien en el cuerpo del tag.

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="http://java.sun.com/jstl/ea/core" prefix="c" %>

<html>
<body>
  Nombre: <c:out value="${param.nombre}"/>
  <br>
  <c:if test="${not empty param.descripcion}">
    Descripcion: <c:out value="${param.descripcion}"/>
  </c:if>
</body>
</html>

Hemos utilizado en este caso, como ejemplo, los tags out e if (para comprobar si hay parámetro descripcion). En este último caso, utilizamos el operador empty en el lenguaje de expresiones, para ver si hay o no valor.

La librería de funciones

JSTL dispone también de un conjunto de funciones que pueden emplearse desde dentro del lenguaje de expresiones, y permiten sobre todo manipular cadenas, para sustituir caracteres, concatenar, etc.

Para utilizar estas funciones, cargaríamos la directiva taglib correspondiente:

<%@ taglib uri="http://java.sun.com/jstl/ea/functions" prefix="fn" %>

Y luego utilizaríamos las funciones que haya disponibles, dentro de una expresión del lenguaje:

La cadena tiene <c:out value="${fn:length(miCadena)}"/> caracteres

8.4.2. Librerías de JSF/Facelets

Hemos visto hasta el momento librerías de JSTL que podemos utilizar dentro de los Facelets, pero que también encontraremos dentro de otros frameworks. Vamos a ver ahora algunas librerías propias de Facelets/JSF, como la librería HTML de JSF y la librería UI de Facelets que nos permitirá crear plantillas para las páginas.

Librería HTML de JSF

Una de las principales librerías de etiquetas utilizada en Facelets es la librería HTML de JSF. Esta librería contiene una serie de componentes de la interfaz que se renderizarán en la página como HTML. Gran parte de los componentes de esta librería son campos de formularios, cuyo valor puede vincularse con managed beans y podemos establecer la forma mediante etiquetas de la librería de funciones de JSF. Por ejemplo podríamos tener un campo de texto como el siguiente:

<h:inputText id="precio"
             size="4"
             value="#{articulo.precio}"
             title="Cantidad">
    <f:validateLongRange minimum="0"/>
</h:inputText>

En el ejemplo anterior se crea un campo de texto que se renderizará como una etiqueta <input> en el HTML resultante, y quedará vinculado en el servidor con el precio del managed bean articulo. Además, se especifica que el campo debe ser validado de forma que nunca tenga un valor inferior a 0.

Encontramos otras etiquetas como las siguientes dentro de esta librería:

Etiqueta Función

<h:commandLink>

Representa un enlace a otra página, se renderiza como <a>

<h:dataTable>

Tabla que se puede actualizar de forma dinámica, se renderiza como <table>

<h:column>

Representa una columna en una tabla

<h:panelGroup>

Representa una fila en una tabla, se renderiza como <div>

<h:graphicImage>

Muestra una imagen, se renderiza como <img>

<h:outputText>

Muestra texto plano

<h:outputStylesheet>

Permite especificar una hoja de estilo

<h:head>

Equivalente a <head>, permite reubicar bloques de script

<h:body>

Equivalente a <body>, permite reubicar bloques de script

En todas estas etiquetas encontramos una serie de atributos comunes:

Atributo Función

id

Identifica al componente de forma única.

rendered

Podemos especificar una condición mediante EL que indica si el componente se debe mostrar o no.

style

Especifica el estilo CSS.

styleClass

Especifica una clase de estilo.

Recursos

Hablamos de recursos para referirnos a cualquier artefacto que una página web necesita para mostrarse correctamente, como son las imágenes, ficheros JavaScript, hojas de estilo, etc. En los Facelets podremos hacer referencia a estos recursos simplemente indicando el nombre del recurso, siempre que éstos se encuentren en determinadas ubicaciones estándar. Estas ubicaciones son:

  • Directorio resources en la raíz del contexto.

  • Dentro del classpath, en META-INF/resources.

Si el recurso se encuentra en una de las localizaciones anteriores podemos hacer referencia a él únicamente mediante su nombre en los atributos de las etiquetas de JSF/Facelets, como por ejemplo:

<h:outputStylesheet library="css" name="estilo.css"/>

En este caso podríamos tener la hoja de estilo en /resources/css/estilo.css.

Podemos también hacer referencia a la ubicación mediante lenguaje de expresiones:

<h:graphicImage value="#{resource['imagenes:logo.gif']}"/>

En el caso anterior buscará la imagen en /resources/imagenes/logo.gif.

Plantillas

La tecnología de Facelets nos permite crear plantillas para las páginas. Estas plantillas definen una determinada disposición de los elementos en el documento, y podrán ser aplicadas a diferentes páginas para así tener una estructura homogénea en todo el sitio web. En estas plantillas por ejemplo podríamos definir un bloque de cabecera, menú lateral, cuerpo de la página, pie de página, etc.

Para la creación de plantillas utilizaremos la librería de Interfaz de Usuario (UI) de Facelets:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets" (1)
      xmlns:h="http://xmlns.jcp.org/jsf/html">

    <h:head>
        <meta http-equiv="Content-Type"
              content="text/html; charset=UTF-8" />
        <h:outputStylesheet library="css" name="estilo.css"/>
        <title>Plantilla con Facelets</title>
    </h:head>

    <h:body>
        <div id="top" class="top">
            <ui:insert name="cabecera">Cabecera</ui:insert> (2)
        </div>
        <div>
        <div id="left">
             <ui:insert name="menu">Menú lateral</ui:insert>
        </div>
        <div id="content" class="content">
             <ui:insert name="cuerpo">Cuerpo de la página</ui:insert>
        </div>
        </div>
    </h:body>
</html>
1 Declaramos la librería UI de Facelets
2 Utilizamos <ui:insert> para crear un lugar donde insertar contenido dentro de la plantilla general.

En el ejemplo anterior hemos creado una plantilla de página web donde se definen tres lugares donde vamos a poner insertar contenido: cabecera, menu y cuerpo. Vamos a crear a continuación una página que se ajuste a dicha plantilla e inserte contenido en cada una de las secciones.

Consideremos que el fichero anterior con la plantilla ha sido guardado con el nombre plantilla.xhtml. Una página que utilice dicha plantilla podría definirse de la siguiente forma:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

    <h:body>
        <ui:composition template="./plantilla.xhtml"> (1)
            <ui:define name="cabecera"> (2)
                Título de la página
            </ui:define>

            <ui:define name="menu">
                <h:outputLabel value="Menú"/>
            </ui:define>

            <ui:define name="cuerpo">
                <h:graphicImage value="#{resource['images:logo.gif']}"/>
                <h:outputText value="2014 (c) DCCIA"/>
            </ui:define>
        </ui:composition>
    </h:body>
</html>
1 Con la etiqueta <ui:composition> indicamos la plantilla que vamos a utilizar.
2 Con la etiqueta <ui:define> indicamos el contenido que vamos a insertar en cada una de las secciones de la plantilla utilizada.

Vemos en el ejemplo anterior que podremos poner una etiqueta <ui:define> por cada elemento definido mediante <ui:insert> en la plantilla que estamos utilizando.

8.5. Ejercicios

8.5.1. Página de chat con Facelets (1 punto)

Vamos a crear una implementación alternativa del listado de mensajes del chat mediante un Facelet que utilice la librería JSTL y el lenguaje de expresiones. Los Facelets son más apropiados que los Servlets para crear la vista de la aplicación, por lo que será conveniente pasar la presentación que actualmente está realizando ListaMensajesServlet a un Facelet.

Seguiremos los siguientes pasos:

  • Añade la configuración de JSF al proyecto desde IntelliJ. Se deberá crear el fichero faces-config.xml en WEB-INF y mapear el Faces Servlet a las direcciones de tipo *.xhtml.

  • Crea desde IntelliJ un nuevo Facelet llamado listaMensajes.xhtml en el directorio webapp/chat.

  • Importa en el Facelet la librería JSTL Core.

  • Crearemos un recuadro para el chat siguiendo el mismo estilo de las páginas utilizadas en sesiones anteriores. Esta vez podemos importar la hoja de estilo estilo.css como un recurso.

    Deberemos copiar el fichero estilo.css a un directorio donde pueda ser importado como recurso (webapp/resources).
  • En la cabecera del cuadro del chat indicaremos el nombre del usuario que hay actualmente conectado. Podemos poner un mensaje como "Conectado al chat como <nombre>".

    El nombre del atributo donde se guarda el nick es org.expertojava.cweb.chat.nick, lo cual puede causar problemas al utilizarlo dentro del lenguaje de expresiones. Para evitar estos problemas podríamos utilizar una sintaxis como ${sessionScope["nombre_atributo"]}.
  • Utilizaremos un managed bean para mostrar los mensajes en la página. Deberemos:

    • Si la cola está vacía, mostraremos el mensaje "Todavía no se ha enviado ningún mensaje al chat".

    • Si la cola tiene mensajes, iteraremos por la cola para mostrar todos los mensajes junto al nombre del usuario que los envió.

No podemos utilizar directamente el objeto ColaMensajes dentro del lenguaje de expresiones al tratarse de un objeto de tipo LinkedList. Podemos crearnos un nuevo managed bean que nos dé acceso a dicha lista mediante uno de sus atributos. Por ejemplo, podríamos crear un objeto Chat dentro del ámbito de la aplicación con un atributo cola en el que inyectaremos el objeto ColaMensajes. De esta forma podríamos acceder a ella desde lenguaje de expresiones con ${chat.cola}.

Todo lo anterior debe realizarse utilizando únicamente JSTL y lenguaje de expresiones.