Manual de JSF

RECU-0130 (Recurso Manual)
Tabla de contenidos
  1. 1. Descripción
    1. 1.1. Terminología Básica
    2. 1.2. Pasos del Proceso de Desarrollo
      1. 1.2.1. Desarrollar los Objetos del Modelo
      2. 1.2.2. Añadir las Declaraciones del Bean Controlado
      3. 1.2.3. Crear las Páginas
      4. 1.2.4. Definir las Navegación por las Páginas
    3. 1.3. El Ciclo de Vida de una Página JavaServer Faces
      1. 1.3.1. Escenarios de Procesamiento del Ciclo de Vida de una Petición
      2. 1.3.2. Ciclo de Vida Estándar de Procesamiento de Peticiones
      3. 1.3.3. Reconstituir el Árbol de Componentes
      4. 1.3.4. Aplicar Valores de la Petición
      5. 1.3.5. Procesar Validaciones
      6. 1.3.6. Actualizar los Valores del Modelo
      7. 1.3.7. Invocar Aplicación
      8. 1.3.8. Renderizar la Respuesta
    4. 1.4. Modelo de Componentes de Interfaz de Usuario
      1. 1.4.1. Las Clases de los Componentes del Interface de Usuario
      2. 1.4.2. El Modelo de Renderizado de Componentes
      3. 1.4.3. Modelo de Conversión
      4. 1.4.4. Modelo de Eventos y Oyentes
      5. 1.4.5. Modelo de Validación
    5. 1.5. Modelo de Navegación
    6. 1.6. Creación del Bean de Respaldo
    7. 1.7. Requisitos y restricciones
    8. 1.8. Recomendaciones de uso
      1. 1.8.1. Paso de parámetros a los actions
      2. 1.8.2. Pasar objetos de request en request
      3. 1.8.3. Comunicación entre ManagedBeans
      4. 1.8.4. Uso de datatable
      5. 1.8.5. Obtener un registro seleccionado
      6. 1.8.6. Listener del ciclo de vida
      7. 1.8.7. Mensajes de error
    9. 1.9. Recomendaciones de diseño
      1. 1.9.1. Beans
      2. 1.9.2. Manejo del scope y los managed beans
      3. 1.9.3. Validadores
      4. 1.9.4. Paginación
    10. 1.10. JSF 2.0
  2. 2. Buenas prácticas y recomendaciones de uso
    1. 2.1. Estructura y diseño
    2. 2.2. Codificación
    3. 2.3. Subvistas dinámicas
  3. 3. Ejemplos
  4. 4. Contenidos relacionados

Descripción

Los principales componentes de la tecnología JavaServer Faces son:

  • Un API y una implementación de referencia para: Representar componentes UI y manejar su estado, manejo de eventos, validación del lado del servidor y conversión de datos, definir la navegación entre páginas, soportar internacionalización y accesibilidad y proporcionar extensibilidad para todas estas características.
  • Una librería de etiquetas JavaServer Pages (JSP) personalizadas para dibujar componentes UI dentro de una página JSP.

Este modelo de programación bien definido y la librería de etiquetas para componentes UI facilitan de forma significativa la tarea de la construcción y mantenimiento de aplicaciones Web con UIs del lado del servidor. Con un mínimo esfuerzo, podemos:

  • Conectar eventos generados en el cliente a código de la aplicación en el lado del servidor.
  • Mapear componentes UI a una página de datos del lado del servidor.
  • Construir un UI con componentes reutilizables y extensibles.
  • Grabar y restaurar el estado del UI más allá de la vida de las peticiones de servidor.

Como se puede apreciar en la siguiente figura, el interfaz de usuario que creamos con la tecnología JavaServer Faces (representado por myUI en el gráfico) se ejecuta en el servidor y se renderiza en el cliente.

La página JSP, myform.jsp, dibuja los componentes del interfaz de usuario con etiquetas personalizadas definidas por la tecnología JavaServer Faces. El UI de la aplicación Web (representado por myUI en la imagen) maneja los objetos referenciados por la página JSP:

  • Los objetos componentes que mapean las etiquetas sobre la página JSP.
  • Los oyentes de eventos, validadores, y los conversores que están registrados en los componentes.
  • Los objetos del modelo que encapsulan los datos y las funcionalidades de los componentes específicos de la aplicación

Terminología Básica

  • Componente UI Se trata de un objeto con estado, mantenido por el servidor, que proporciona funcionalidad específica para interactuar con un usuario final. Los Componente UI son JavaBeans con propiedades, métodos y eventos. Están organizados en una vista (View), que es un árbol de componentes normalmente mostrados como una página.
  • Renderer Es el responsable de mostrar un componente UI y traducir la entrada del usuario en valores de componentes. Los Renderers pueden ser diseñados para trabajar con uno o más Componentes UI, y un Componente UI puede ser asociado con varios Renderer diferentes.
  • Validador Es el responsable de asegurar que el valor introducido por un usuario es correcto. Podemos asociar uno o más validadores a un Componente UI.
  • Backing Beans JavaBeans especializados que recogen valores de los componentes UI e implementan métodos listener de eventos. También pueden almacenar referencias a componentes UI.
  • Converter Convierte un valor de un componente a y desde una cadena para mostrarlo. Los converter se asocian a un componente UI.
  • Events y Listeners JSF usa el modelo event/listener de JavaBeans (también usado por Swing). Los Componentes UI (y otros objetos) generan eventos y los listeners pueden ser registrados para manejar dichos eventos.
  • Mensajes Información que se muestra al usuario. Cualquier parte de la aplicación (backing beans, validadores, converters, etc...) pueden generar información o mensajes de error que pueden ser mostrados de vuelta al usuario.
  • Navegación Representa la capacidad de movernos de una página a la siguiente. JSF tiene un sistema de navegación avanzado que está integrado con escuchadores de eventos especializados.

Pasos del Proceso de Desarrollo

Desarrollar una sencilla aplicación JavaServer Faces requiere la realización de estos pasos:

  • Desarrollar los objetos del modelo, los que contendrán los datos.
  • Añadir las declaraciones del bean controlado al fichero de configuración de la aplicación.
  • Crear las páginas utilizando las etiquetas de componentes UI y las etiquetas "core".
  • Definir la navegación entre las páginas.

Estas tareas se pueden realizar simultáneamente o en cualquier orden. Sin embargo, la gente que realice las tareas necesitará comunicarse durante el proceso de desarrollo. Por ejemplo, el autor de las páginas necesita saber los nombres de los objetos del modelo para poder acceder a ellos desde la página.

Desarrollar los Objetos del Modelo

Desarrollar los objetos del modelo es responsabilidad del desarrollador de aplicaciones. El autor de las páginas y el desarrollador de aplicaciones podrían necesitar trabajar juntos para asegurarse que las etiquetas de componentes se refieren a las propiedades del objeto apropiado, que las propiedades del objeto son de los tipos apropiados, y para tener cuidado de otros detalles.

Aquí tenemos la clase UserNumberBean.java que contiene los datos introducidos en el campo de texto de la página greeting.jsp:

package guessNumber;
import java.util.Random;

public class UserNumberBean {
    Integer userNumber = null;
    Integer randomInt = null;
    String response = null;
   
    public UserNumberBean () {
        Random randomGR = new Random();
        randomInt = new Integer(randomGR.nextInt(10));
        System.out.println("Duke’s Number: "+randomInt);
    }

    public void setUserNumber(Integer user_number) {
        userNumber = user_number;
        System.out.println("Set userNumber " + userNumber);
    }

    public Integer getUserNumber() {
        System.out.println("get userNumber " + userNumber);
        return userNumber;
    }

    public String getResponse() {
        if(userNumber.compareTo(randomInt) == 0)
            return "Yay! You got it!";
        else
            return "Sorry, "+userNumber+" is incorrect.";
    }
}

Como podemos ver, este bean es como cualquier otro componente JavaBeans. Tiene un método set o accesor y un campo privado o propiedad. Esto significa que podemos concebir hacer referencia a beans que ya hayamos escrito desde nuestras páginas JavaServer Faces.

Dependiendo del tipo de componente que referencia una propiedad del objeto del modelo, esta propiedad puede ser de cualquiera de los tipos básicos primitivos y los tipos referencia. Esto incluye cualquiera de los tipos numéricos, String, int, double, y float. La tecnología JavaServer Faces convertirá automáticamente el dato al tipo especificado por la propiedad del objeto del modelo. También podemos aplicar una conversión a un componente para convertir los valores de los componentes a un tipo que no esté soportado por el componente.

En el UserNumberBean, la propiedad userNumber es del tipo Integer. La implementación de JavaServer Faces puede convertir el String de los parámetros de la solicitud que contiene este valor a un Integer antes de actualizar la propiedad del objeto del modelo cuando utilicemos una etiqueta input_number. Aunque este ejemplo lo convierte a un Integer, en general, deberíamos utilizar tipos nativos en vez de utilizar las clases envoltura (int en lugar de Integer).

Añadir las Declaraciones del Bean Controlado

Después de desarrollar los beans utilizados en la aplicación, necesitamos añadir declaraciones para ellos en el fichero de configuración de la aplicación. Cualquier miembro del equipo de desarrollo puede realizar la tarea de añadir las declaraciones al fichero de configuración de la aplicación. Aquí tenemos la declaración de bean controlado para UserNumberBean:

<managed-bean>
    <managed-bean-name>UserNumberBean</managed-bean-name>
        <managed-bean-class>
            guessNumber.UserNumberBean
        </managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

La implementación de JavaServer Faces procesa este fichero en el momento de arranque de la aplicación e inicializa el UserNumberBean y lo almacena en el ámbito de sesión. Entonces el bean estará disponible para todas las páginas de la aplicación. Para aquellos que estén familiarizados con versiones anteriores, esta facilidad de "bean controlado" reemplaza la utilización de la etiqueta jsp:useBean.

Crear las Páginas

La creación de las páginas es responsabilidad del autor de páginas. Esta tarea implica distribuir los componentes UI en las páginas, mapear los componentes a los datos de los objetos del modelo, y añadir otras etiquetas importantes (como etiquetas del validador) a las etiquetas de los componentes. Aquí tenemos la página greeting.jsp con las etiquetas de validador (menos los HTML que lo rodea):

<title>Hello</title> 
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

<h:graphic&#95;image id="wave&#95;img" url="/wave.med.gif" />
<h2>Hi. My name is Duke.
I'm thinking of a number from 0 to 10.
Can you guess it?</h2>
<f:use&#95;faces>
    <h:form id="helloForm" formName="helloForm" >
        <h:graphic&#95;image id="wave&#95;img" url="/wave.med.gif" />
        <h:input&#95;number id="userNo" numberStyle="NUMBER"
            valueRef="UserNumberBean.userNumber">
            <f:validate&#95;longrange minimum="0" maximum="10" />
        </h:input&#95;number>
        <h:command&#95;button id="submit" action="success"
            label="Submit" commandName="submit" /><p>
        <h:output&#95;errors id="errors1" for="userNo"/>
    </h:form>
</f:use&#95;faces>

Esta página demuestra unas cuantas características importantes que utilizaremos en la mayoría de nuestras aplicaciones JavaServer Faces:

  • La etiqueta form: Esta etiqueta representa un formulario de entrada, que permite al usuario introducir algún dato y enviarlo al servidor, normalmente pulsando un botón. Las etiquetas que representan los componentes que conforman el formulario se anidan dentro de la etiqueta form. Estas etiquetas son h:input_number y h:command_button.
  • La etiqueta input_number: Esta etiqueta representa un componente que es un campo de texto dentro del cual el usuario introduce un número. Esta etiqueta tiene tres atributos: id, valueRef, y numberStyle. El atributo id es opcional y corresponde al identificador ID del componente. Si no incluimos uno, la implementación JavaServer Faces generará uno automáticamente.
  • El atributo valueRef utiliza una expresión de referencia para referirse a la propiedad del objeto del modelo que contiene los datos introducidos en el campo de texto. La parte de la expresión que hay antes del "." debe corresponder con el nombre definido por el elemento managed-bean-name del bean controlado en el fichero de configuración. La parte de la expresión que hay después del "." debe corresponder con el nombre del elemento property-name correspondiente en la declaración del propio bean controlado. En este ejemplo, no se declararon elementos property-name porque no se inicializaron propiedades en la arrancada de la aplicación para este ejemplo.
  • El atributo numberStyle indica el nombre del patrón de estilo de número definido según la clase java.text.NumberFormat. Los valores válidos son: currency, integer, number, o percent.
  • La etiqueta validate_longrange: La etiqueta input_number también contiene una etiqueta validate_longrange, que es una del conjunto de etiquetas validadores estándar incluido con la implementación de referencia de JavaServer Faces. Este validador chequea si el valor local de un componente está dentro de un cierto rango. El valor debe ser cualquier cosa que se pueda convertir a long. Esta etiqueta tiene dos atributos, uno que especifica un valor mínimo y otro que especifica un valor máximo. Aquí, la etiqueta se utiliza para asegurarnos de que el número introducido en el campo de texto es un número entre el 0 y el 10.
  • La etiqueta command_button: Esta etiqueta representa el botón utilizado para enviar los datos introducidos en el campo de texto. El atributo action especifica una salida que facilita al mecanismo de navegación la decisión de qué página abrir luego. La siguiente página describe esto en más detalle.
  • La etiqueta output_errors: Esta etiqueta mostrará un mensaje de error si el dato introducido en el campo de texto no cumple con las reglas especificadas por el validador. El mensaje de error se muestra en el lugar de la página donde hayamos situado la etiqueta output_errors.

Definir las Navegación por las Páginas

Otra posibilidad que tiene el desarrollador de la aplicación es definir la navegación de páginas por la aplicación, como qué página va después de que el usuario pulse un botón para enviar un formulario.

El desarrollador de la aplicación define la navegación por la aplicación mediante el fichero de configuración, el mismo fichero en el que se declararon los beans manejados. Aquí están las reglas de navegación definidas para el ejemplo guessNumber:

<navigation-rule>
    <from-tree-id>/greeting.jsp</from-tree-id>
    <navigation-case>
        <from-outcome>success</from-outcome>
        <to-tree-id>/response.jsp</to-tree-id>
    </navigation-case>
</navigation-rule>
<navigation-rule>
    <from-tree-id>/response.jsp</from-tree-id>
        <navigation-case>
        <from-outcome>success</from-outcome>
        <to-tree-id>/greeting.jsp</to-tree-id>
    </navigation-case>
</navigation-rule>

Cada regla de navegación define cómo ir de una página (especificada en el elemento from-tree-id) a otras páginas de la aplicación. El elemento navigation-rule puede contener cualquier número de elemento navigation-case, cada uno de los cuales define la página que se abrirá luego (definida por to-tree-id) basándose en una salida lógica (definida mediante from-outcome).

La salida se puede definir mediante el atributo action del componente UICommand que envía el formulario, como en el ejemplo guessNumber:

<h:command&#95;button id="submit"
    action="success" label="Submit" />

La salida también puede venir del valor de retorno del método llamada (invoke) de un objeto Action. Este método realiza algún procesamiento para determinar la salida. Un ejemplo es que el método invoke puede chequear si la password que el usuario ha introducido en la página corresponde con la del fichero. Si es así, el método invoke podría devolver éxito ("success"); si no es así, podría devolver fallo ("failure"). Un salida de "failure" podría resultar en la recarga de la página de login. Una salida de "success" podría resultar en que se mostrara una página con las actividades de la tarjeta de crédito del usuario, por ejemplo.

El Ciclo de Vida de una Página JavaServer Faces

El ciclo de vida de una página JavaServer Faces page is similar al de una página JSP: El cliente hace una petición HTTP de la página y el servidor responde con la página traducida a HTML. Sin embargo, debido a las características extras que ofrece la tecnología JavaServer Faces, el ciclo de vida proporciona algunos servicios adicionales mediante la ejecución de algunos pasos extras.

Los pasos del ciclo de vida que se ejecutan dependen de si la petición se originó o no desde una aplicación JavaServer Faces y de si la respuesta es o no generada con la fase de renderizado del ciclo de vida de JavaServer Faces. Esta sección explica los diferentes escenarios del ciclo de vida. Luego explica cada una de estas fases del ciclo de vida utilizando el ejemplo guessNumber.

Escenarios de Procesamiento del Ciclo de Vida de una Petición

Una aplicación JavaServer Faces soporta dos tipos diferentes de respuestas y dos tipos diferentes de peticiones:

  • Respuesta Faces: Una respuesta servlet que se generó mediante la ejecución de la fase Renderizar la Respuesta del ciclo de vida de procesamiento de la respuesta.
  • Respuesta No-Faces: Una respuesta servlet que no se generó mediante la ejecución de la fase Renderizar la Respuesta. Un ejemplo es una página JSP que no incorpora componentes JavaServer Faces.
  • Petición Faces: Una petición servlet que fue enviada desde una Respuesta Faces previamente generada. Un ejemplo es un formulario enviado desde un componente de interface de usuario JavaServer Faces, donde la URL de la petición identifica el árbol de componentes JavaServer Faces para usar el procesamiento de petición.
  • Petición No-Faces: Una petición Servlet que fue enviada a un componente de aplicación como un servlet o una página JSP en vez de directamente a un componente JavaServer Faces.

La combinación de estas peticiones y respuestas resulta en tres posibles escenarios del ciclo de vida que pueden existir en una aplicación JavaServer Faces:

  1. Escenario 1: Una Petición No-Faces genera una Respuesta Faces. Un ejemplo de este escenario es cuando se pulsa un enlace de una página HTML que abre una página que contiene componentes JavaServer Faces. Para dibujar una Respuesta Faces desde una petición No-Faces, una aplicación debe proporcionar un mapeo FacesServlet en la URL de la página que contiene componentes JavaServer Faces. FacesServlet acepta peticiones entrantes y pasa a la implementación del ciclo de vida para su procesamiento.
  2. Escenario 2: Una Petición Faces genera una Respuesta No-Faces. Algunas veces, una aplicación JavaServer Faces podría necesitar redirigir la salida a un recurso diferente de la aplicación Web diferente o generar una respuesta que no contiene componentes JavaServer Faces. En estas situaciones, el desarrollador debe saltarse la fase de renderizado (Renderizar la Respuesta) llamando a FacesContext.responseComplete. FacesContext Contiene toda la información asociada con una Petición Faces particular. Este método se puede invocar durante las fases Aplicar los Valores de Respuesta, Procesar Validaciones o Actualizar los Valores del Modelo.
  3. Escenario 3: Una Petición Faces genera una Respuesta Faces. Es el escenario más común en el ciclo de vida de una aplicación JavaServer Faces. Este escenario implica componentes JavaServer Faces enviando una petición a una aplicación JavaServer Faces utilizando el FacesServlet. Como la petición ha sido manejada por la implementación JavaServer Faces, la aplicación no necesita pasos adicionales para generar la respuesta. Todos los oyentes, validadores y conversores serán invocados automáticamente durante la fase apropiada del ciclo de vida estándar, como se describe en la siguiente sección.

Ciclo de Vida Estándar de Procesamiento de Peticiones

La mayoría de los usuarios de la tecnología JavaServer Faces no necesitarán conocer a fondo el ciclo de vida de procesamiento de una petición. Sin embargo, conociendo lo que la tecnología JavaServer Faces realiza para procesar una página, un desarrollador de aplicaciones JavaServer Faces no necesitará preocuparse de los problemas de renderizado asociados con otras tecnologías UI. Un ejemplo sería el cambio de estado de los componentes individuales. Si la selección de un componente, como un checkbox, afecta a la apariencia de otro componente de la página, la tecnología JavaServer Faces manejará este evento de la forma apropiada y no permitirá que se dibuje la página sin reflejar este cambio.

La siguiente figura ilustra los pasos del ciclo de vida petición-respuesta JavaServer Faces

Reconstituir el Árbol de Componentes

Cuando se hace una petición para una página JavaServer Faces, como cuando se pulsa sobre un enlace o un botón, la implementación JavaServer Faces comienza el estado Reconstituir el Árbol de Componentes.

Durante esta fase, la implementación JavaServer Faces construye el árbol de componentes de la página JavaServer Faces, conecta los manejadores de eventos y los validadores y graba el estado en el FacesContext. El árbol de componentes de la página greeting.jsp del ejemplo guessNumber se parecería a esto:

Aplicar Valores de la Petición

Una vez construido el árbol de componentes, cada componente del árbol extrae su nuevo valor desde los parámetros de la petición con su método decode. Entonces, el valor es almacenado localmente en el componente. Si falla la conversión del valor, se genera un mensaje de error asociado con el componente y se pone en la cola de FacesContext. Este mensaje se mostrará durante la fase Renderizar la Respuesta, junto con cualquier error de validación resultante de la fase Procesar Validaciones.

Si durante esta fase se produce algún evento, la implementación JavaServer Faces emite los eventos a los oyentes interesados.

En este punto, si la aplicación necesita redirigirse a un recurso de aplicación Web diferente o generar una respuesta que no contenga componentes JavaServer Faces, puede llamar a FacesContext.responseComplete.

En el caso del componente userNumber de la página greeting.jsp, el valor es cualquier cosa que el usuario introduzca en el campo. Como la propiedad del objeto del model unida al componente tiene un tipo Integer, la implementación JavaServer Faces convierte el valor de un String a un Integer.

En este momento, se han puesto los nuevos valores en los componentes y los mensajes y eventos se han puesto en sus colas.

Procesar Validaciones

Durante esta fase, la implementación JavaServer Faces procesa todas las validaciones registradas con los componentes del árbol. Examina los atributos del componente que especifican las reglas de validación y compara esas reglas con el valor local almacenado en el componente. Si el valor local no es válido, la implementación JavaServer Faces añade un mensaje de error al FacesContext y el ciclo de vida avanza directamente hasta la fase Renderizar las Respuesta para que la página sea dibujada de nuevo incluyendo los mensajes de error. Si había errores de conversión de la fase Aplicar los Valores a la Petición, también se mostrarán.

En este momento, si la aplicación necesita redirigirse a un recurso de aplicación Web diferente o generar una respuesta que no contenga componentes JavaServer Faces, puede llamar a FacesContext.responseComplete

Si se han disparado eventos durante esta fase, la implemetanción JavaServer Faces los envía a los oyentes interesados

En la página greeting.jsp, la implementación JavaServer Faces procesa el validador sobre la etiqueta input_number de UserNumber. Verifica que el dato introducido por el usuario en el campo de texto es un entero entre 0 y 10. Si el dato no es válido, o ocurrió un error de conversión durante la fase Aplicar los Valores a la Petición, el procesamiento salta a la fase Renderizar las Respuesta, durante la que se dibujará de nuevo la página greeting.jsp mostrando los mensajes de error de conversión o validación en el componente asociado con la etiqueta output_errors.

Actualizar los Valores del Modelo

Una vez que la implementación JavaServer Faces determina que el dato es válido, puede pasar por el árbol de componentes y configurar los valores del objeto de modelo correspondiente con los valores locales de los componentes. Sólo se actualizarán los componentes que tenga expresiones valueRef. Si el dato local no se puede convertir a los tipos especificados por las propiedades del objeto del modelo, el ciclo de vida avanza directamente a la fase Renderizar las Respuesta, durante la que se dibujará de nuevo la página mostrando los errores, similar a lo que sucede con los errores de validación.

En este punto, si la aplicación necesita redirigirse a un recurso de aplicación Web diferente o generar una respuesta que no contenga componentes JavaServer Faces, puede llamar a FacesContext.responseComplete.

Si se han disparado eventos durante esta fase, la implementación JavaServer Faces los emite a los oyentes interesados. En esta fase, a la propiedad userNumber del UserNumberBean se le da el valor del componente userNumber.

Invocar Aplicación

Durante esta fase, la implementación JavaServer Faces maneja cualquier evento a nivel de aplicación, como enviar un formulario o enlazar a otra página. En este momento, si la aplicación necesita redirigirse a un recurso de aplicación Web diferente o generar una respuesta que no contenga componentes JavaServer Faces, puede llamar a FacesContext.responseComplete.

La página greeting.jsp del ejemplo guessNumber tiene asociado un evento a nivel de aplicación con el componente Command. Cuando se procesa este evento, una implementación de ActionListener por defecto recupera la salida, “success”, desde el atributo action del componente. El oyente pasa la salida al NavigationHandler por defecto y éste contrasta la salida con las reglas de navegación definidas en el fichero de configuración de la aplicación para determinar qué página se debe mostrar luego.

Luego la implementación JavaServer Faces configura el árbol de componentes de la respuesta a esa nueva página. Finalmente, la implementación JavaServer Faces transfiere el control a la fase Renderizar la Respuesta.

Renderizar la Respuesta

Durante esta fase, la implementación JavaServer Faces invoca las propiedades de codificación de los componentes y dibuja los componentes del árbol de componentes grabado en el FacesContext.

Si se encontraron errores durante las fases Aplicar los Valores a la Petición, Procesar Validaciones o Actualizar los Valores del Modelo, se dibujará la página original. Si las páginas contienen etiquetas output_errors, cualquier mensaje de error que haya en la cola se mostrará en la página.

Se pueden añadir nuevos componentes en el árbol si la aplicación incluye renderizadores personalizados, que definen cómo renderizar un componente. Después de que se haya renderizado el contenido del árbol, éste se graba para que las siguientes peticiones puedan acceder a él y esté disponible para la fase Reconstituir el Árbol de Componentes de las siguientes llamadas.

Modelo de Componentes de Interfaz de Usuario

Los componentes UI JavaServer Faces son elementos configurables y reutilizables que componen el interfaz de usuario de las aplicaciones JavaServer Faces. Un componente puede ser simple, como un botón, o compuesto, como una tabla, que puede estar compuesta por varios componentes. La tecnología JavaServer Faces proporciona una arquitectura de componentes rica y flexible que incluye:

  • Un conjunto de clases UIComponent para especificar el estado y comportamiento de componentes UI.
  • Un modelo de renderizado que define cómo renderizar los componentes de diferentes formas.
  • Un modelo de eventos y oyentes que define cómo manejar los eventos de los componentes.
  • Un modelo de conversión que define cómo conectar conversores de datos a un componente.
  • Un modelo de validación que define cómo registrar validadores con un componente.

Las Clases de los Componentes del Interface de Usuario

La tecnología JavaServer Faces proporciona un conjunto de clases de componentes UI que especifican toda la funcionalidad del componente, cómo mantener su estado, mantener una referencia a objetos del modelo, y dirigir el manejo de eventos y el renderizado para un conjunto de componentes estándar. Estas clases son completamente extensibles, lo que significa que podemos extenderlas para crear nuestros propios componentes personalizados.

Todas las clases de componentes UI de JavaServer Faces descienden de la clase UIComponentBase, que define el estado y el comportamiento por defecto de un UIComponent. El conjunto de clases de componentes UI incluido en la última versión de JavaServer Faces es:

  • UICommand: Representa un control que dispara actions cuando se activa.
  • UIForm: Encapsula un grupo de controles que envían datos de la aplicación. Este componente es análogo a la etiqueta form de HTML.
  • UIGraphic: Muestra una imagen.
  • UIInput: Toma datos de entrada del usuario. Esta clase es una subclase de UIOutput.
  • UIOutput: Muestra la salida de datos en un página.
  • UIPanel: Muestra una tabla.
  • UIParameter: Representa la sustitución de parámetros.
  • UISelectItem: Representa un sólo ítem de un conjunto de ítems.
  • UISelectItems: Representa un conjunto completo de ítems.
  • UISelectBoolean: Permite a un usuario seleccionar un valor booleano en un control, seleccionándolo o deseleccionándolo. Esta clase es una subclase de UIInput.
  • UISelectMany: Permite al usuario seleccionar varios ítems de un grupo de ítems. Esta clase es una subclase de UIInput.
  • UISelectOne: Permite al usuario seleccionar un ítem de un grupo de ítems. Esta clase es una subclase de UIInput.

La mayoría de los desarrolladores de aplicaciones no tendrán que utilizar estas clases directamente. En su lugar, incluirán los componentes en una página usando la etiqueta correspondiente al componente. La mayoría de estos componentes se pueden renderizar de formas diferentes. Por ejemplo, un UICommand se puede renderizar como un botón o como un hiperenlace.

El Modelo de Renderizado de Componentes

La arquitectura de componentes JavaServer Faces está diseñada para que la funcionalidad de los componentes se defina mediante las clases de componentes, mientras que el renderizado de los componentes se puede definir mediante un renderizador separado. Este diseño tiene varios beneficios:

  • Los desarrolladores de componentes pueden definir sólo una vez el comportamiento de un componente, pero pueden crear varios renderizadores, cada uno de los cuales define una forma diferente de dibujar el componente para el mismo cliente o para diferentes clientes.
  • Los desarrolladores de aplicaciones pueden modificar la apariencia de un componente de la página seleccionando la etiqueta que representa la combinación componente/renderizador apropiada.

Un kit renderizador define como se mapean las clases de los componentes a las etiquetas de componentes apropiadas para un cliente particular. La implementación JavaServer Faces incluye un RenderKit estándar para renderizar a un cliente HTML.

Por cada componente UI que soporte un RenderKit, éste define un conjunto de objetos Renderer. Cada objeto Renderer define una forma diferente de dibujar el componente particular en la salida definida por el RenderKit. Por ejemplo, un componente UISelectOne tiene tres renderizadores diferentes: Uno de ellos dibuja el componente como un conjunto de botones de radio, otro dibuja el componente como un ComboBox y el tercero dibuja el componente como un ListBox.

Cada etiqueta JSP personalizada en el RenderKit de HTML está compuesta por la funcionalidad del componente, definida en la clase UIComponent, y los atributos de renderizado, definidos por el Renderer. Por ejemplo, las dos etiquetas que podemos ver en la siguiente tabla representan un componente UICommand, renderizado de dos formas diferentes:

EtiquetaSe renderiza como...
command_button Un elemento "input type=type" HTML, donde el valor del tipo puede ser submit, reset, o image.
command_hyperlink Un elemento "a href" HTML

La parte command de las etiquetas corresponde con la clase UICommand, y especifica la funcionalidad, que es disparar un action. Las partes del botón y el hiperenlace de las etiquetas corresponden a un renderizador independiente, que define cómo dibujar el componente.

La implementación de referencia de JavaServer Faces proporciona una librería de etiquetas personalizadas para renderizar componentes en HTML. Soporta todos los componentes listados en la siguiente tabla:

EtiquetaFuncionesSe renderiza como...Apariencia
command_buttonEnviar un formulario a la aplicaciónUn elemento "input type=type" HTML, donde el valor del tipo puede ser submit, reset, o image.Un botón
command_hyperlinkEnlaza a otra página o localización en otra páginaUn elemento "a href" HTMLUn hiperenlace
formRepresenta un formulario de entrada. Las etiquetas internas del formulario reciben los datos que serán enviados con el formularioUn elemento "form" HTMLNo tiene apariencia
graphic_imageMuestra una imagenUn elemento "img" HTMLUna imagen
input_datePermite al usuario introducir una fechaUn elemento "input type=text" HTMLUn string de texto, formateado con un ejemplar de java.text.DateFormat
input_datetimePermite al usuario introducir una fecha y una horaUn elemento "input type=text" HTMLUn string de texto, formateado con un ejemplar de java.text.SimpleDateFormat
input_hiddenPermite introducir una variable oculta en una páginaUn elemento "input type=hidden" HTMLSin apariencia
input_numberPermite al usuario introducir un númeroUn elemento "input type=text" HTMLUn string de texto, formateado con un ejemplar de java.text.NumberFormat
input_secretPermite al usuario introducir un string sin que aparezca el string real en el campoUn elemento "input type=password" HTMLUn campo de texto, que muestra una fila de caracteres en vez del texto real introducido
input_textPermite al usuario introducir un stringUn elemento "input type=text" HTMLUn campo de texto
input_textareaPermite al usuario introducir un texto multi-líneasUn elemento "textarea" HTMLUn campo de texto multi-línea
input_timePermite al usuario introducir una horaUn elemento "input type=text" HTMLUn string de texto, formateado con un ejemplar de java.text.DateFormat
output_dateMuestra una fecha formateadaTexto normalUn string de texto, formateado con un ejemplar de java.text.DateFormat
output_datetimeMuestra una fecha y hora formateadosTexto normalUn string de texto, formateado con un ejemplar de java.text.SimpleDateFormat
output_errorsMuestra mensajes de errorTexto normalTexto normal
output_labelMuestra un componente anidado como una etiqueta para un campo de texto especificadoUn elemento "label" HTMLTexto normal
output_messageMuestra un mensaje localizado (internacionalizado)Texto normalTexto normal
output_numberMuestra un número formateadoTexto normalUn string de texto, formateado con un ejemplar de java.text.NumberFormat
output_textMuestra una línea de textoTexto normalTexto normal
output_timeMuestra una hora formateadaTexto normalUn string de texto, formateado con un ejemplar de java.text.DateFormat
panel_dataItera sobre una colección de datosUn conjunto de filas en una tabla 
panel_gridMuestra una tablaUn elemento "label" HTML.con elementos "tr" y "lt,td"Una tabla
panel_groupAgrupa un conjunto de paneles bajo un padreUna fila en una tabla 
panel_listMuestra una tabla de datos que vienen de una collection, un array, un iterator o un mapUn elemento "table" HTML. con elementos "tr" y "lt,td"Una tabla
selectboolean_checkboxPermite al usuario cambiar el valor de una elección booleanaUn elemento "input type=checkbox" HTMLUn checkBox
selectitemRepresenta un ítem de una lista de ítems en un componente UISelectOneUn elemento "option" HTMLSin apariencia
selectitemsRepresenta una lista de ítems en un componente UISelectOneUn elemento "option" HTMLSin apariencia
selectmany_checkboxlistMuestra un conjunto de checkbox, en los que el usuario puede seleccionar variosUn conjunto de elementos "input" HTMLUn conjunto de CheckBox
selectmany_listboxPermite a un usuario seleccionar varios ítems de un conjunto de ítems, todos mostrados a la vezUn conjunto de elementos "select" HTMLUn ListBox
selectmany_menuPermite al usuario seleccionar varios ítems de un grupo de ítemsUn conjunto de elementos "select" HTMLUn comboBox
selectone_listboxPermite al usuario seleccionar un ítem de un grupo de ítemsUn conjunto de elementos "select" HTMLUn listBox
selectone_menuPermite al usuario seleccionar un ítem de un grupo de ítemsUn conjunto de elementos "select" HTMLUn comboBox
selectone_radioPermite al usuario seleccionar un ítem de un grupo de ítemsUn conjunto de elementos "input type=radio" HTMLUn conjunto de botones de radio

Modelo de Conversión

Una aplicación JavaServer Faces opcionalmente puede asociar un componente con datos del objeto del modelo del lado del servidor. Este objeto del modelo es un componente JavaBeans que encapsula los datos de un conjunto de componentes. Una aplicación obtiene y configura los datos del objeto modelo para un componente llamando a las propiedades apropiadas del objeto modelo para ese componente.

Cuando un componente se une a un objeto modelo, la aplicación tiene dos vistas de los datos del componente: la vista modelo y la vista presentación, que representa los datos de un forma que el usuario pueda verlos y modificarlos.

Una aplicación JavaServer Faces debe asegurarse que los datos del componente puedan ser convertidos entre la vista del modelo y la vista de presentación. Esta conversión normalmente la realiza automáticamente el renderizador del componente.

En algunas situaciones, podríamos querer convertir un dato de un componente a un tipo no soportado por el renderizador del componente. Para facilitar esto, la tecnología JavaServer Faces incluye un conjunto de implementaciones estándar de Converter que nos permite crear nuestros conversores personalizados. La implementación de Converter convierte los datos del componente entre las dos vistas.

Modelo de Eventos y Oyentes

Un objetivo de la especificación JavaServer Faces es mejorar los modelos y paradigmas existentes para que los desarrolladores se puedan familiarizar rápidamente con el uso de JavaServer Faces en sus aplicaciones. En este espíritu, el modelo de eventos y oyentes de JavaServer Faces mejora el diseño del modelo de eventos de JavaBeans, que es familiar para los desarrolladores de GUI y de aplicaciones Web.

Al igual que la arquitectura de componentes JavaBeans, la tecnologia JavaServer Faces define las clases Listener y Event que una aplicación puede utilizar para manejar eventos generados por componentes UI. Un objeto Event identifica al componente que lo generó y almacena información sobre el propio evento. Para ser notificado de un evento, una aplicación debe proporcionar una implementación de la clase Listener y registrarla con el componente que genera el evento. Cuando el usuario activa un componente, como cuando pulsa un botón, se dispara un evento. Esto hace que la implementación de JavaServer Faces invoque al método oyente que procesa el evento. JavaServer Faces soporta dos tipos de eventos: eventos value-changed y eventos action.

Un evento value-changed ocurre cuando el usuario cambia el valor de un componente. Un ejemplo es seleccionar un checkbox, que resulta en que el valor del componente ha cambiado a true. Los tipos de componentes que generan estos eventos son los componentes UIInput, UISelectOne, UISelectMany, y UISelectBoolean. Este tipo de eventos sólo se dispara si no se detecta un error de validación.

Un evento action ocurre cuando el usuario pulsa un botón o un hiperenlace. El componente UICommand genera este evento.

Modelo de Validación

La tecnología JavaServer Faces soporta un mecanismo para validar el dato local de un componente durante la fase del Proceso de Validación, antes de actualizar los datos del objeto modelo.

Al igual que el modelo de conversión, el modelo de validación define un conjunto de clases estándar para realizar chequeos de validación comunes. La librería de etiquetas jsf-core también define un conjunto de etiquetas que corresponden con las implementaciones estándar de Validator.

La mayoría de las etiquetas tienen un conjunto de atributos para configurar las propiedades del validador, como los valores máximo y mínimo permitidos para el dato del componente. El autor de la página registra el validador con un componente anidando la etiqueta del validador dentro de la etiqueta del componente. Al igual que el modelo de conversión, el modelo de validación nos permite crear nuestras propias implementaciones de Validator y la etiqueta correspondiente para realizar validaciones personalizadas.

Modelo de Navegación

Virtualmente todas las aplicaciones Web están compuestas de un conjunto de páginas. Uno de los principales problemas de un desarrollador de aplicaciones Web es manejar la navegación entre esas páginas.

El nuevo modelo de navegación de JavaServer Faces facilita la definición de la navegación de páginas y el manejo de cualquier procesamiento adicional necesario para elegir la secuencia en la que se cargan las páginas. En muchos casos, no se requiere código para definir la navegación. En su lugar, la navegación se puede definir completamente en el fichero de configuración de la aplicación usando un pequeño conjunto de elementos XML. La única situación en que necesitaremos proporcionar algo de código es si necesitamos algún procesamiento adicional para determinar qué página mostrar luego.

Para cargar la siguiente página en una aplicación web, el usuario normalmente pulsa un botón. Como vimos anteriormente, una pulsación de botón genera un evento action. La implementación de JavaServer Faces proporciona un nuevo oyente de eventos action por defecto para manejar este evento. Este oyente determina la salida del evento action, como success o failure. Esta salida se puede definir como una propiedad String del componente que generó el evento o como el resultado de un procesamientro extra realizado en un objeto Action asociado con el componente. Después de determinar la salida, el oyente la pasa al ejemplar de NavigationHandler (manejador de navegación) asociado con la aplicación. Basándose en la salida devuelta, el NavigationHandler selecciona la página apropiada consultando el fichero de configuración de la aplicación.

Creación del Bean de Respaldo

Otra función crítica de las aplicaciones Web es el manejo apropiado de los recursos. Esto incluye la separación de la definición de objetos componentes UI de los objetos de datos y almacenar y manejar estos ejemplares de objetos en el ámbito apropiado. Las versiones anteriores de la tecnología JavaServer Faces nos permitían crear objetos del modelo que encapsulaban los datos y la lógica del negocio separadamente de los objetos de componentes UI y almacenarlos en un ámbito particular. La nueva versión especifica completamente cómo se crean y se manejan estos objetos.

Esta versión presenta APIs para:

  • Evaluar una expresión que se refiere a un objeto del modelo, una propiedad de un objeto del modelo, u otro tipo de datos primitivo o estructura de datos. Esto se hace con el API Value-Binding.
  • Recuperar un objeto desde el ámbito. Esto se hace con el API VariableResolver.
  • Crear un objeto y almacenarlo en un ámbito si no está ya allí. Esto se hace con el VariableResolver por defecto, llamada la Facilidad Bean Controlado, que se configura con el fichero del configuración de la aplicación descrito en la siguiente página.

Requisitos y restricciones

En relación con el uso de JSF, se han detectado lo siguientes requisitos agrupados por categoría:

Versiones de Java

  • JDK 1.4.x
  • JDK 1.5.x

Contenedor de Servlet

  • Tomcat 4.x
  • Tomcat 5.x
  • JRun 4 (SP1a)
  • JBoss 3.2.x
  • JBoss 4.0.x
  • BEA Weblogic 8.1
  • Jonas 3.3.6 w/ Tomcat
  • Resin 2.1.x
  • Jetty 4.2.x
  • Jetty 5.1.x
  • Websphere 5.1.2
  • OC4J

No obstante, cualquier motor de servlet que cumpla la especificación 2.3 debería valer. Respecto a JSP, con la versión 1.2 es bastante, pero MyFaces usa características de la versión 2.0, por lo tanto en estos contenedores habrá que instalar un jar adicional que viene con la distribución, jsp-2.0.jar.

IMPORTANTE. No añadir ese jar si no es necesario, por ejemplo, en tomcat 5.5, la presencia de este archivo causaría que el motor de servlet dejara de funcionar.

Además habría que tener en cuenta las siguientes consideraciones:

  • El motor de servlet debe ser compatible con la especificación 2.3 y las JSP deben ser acordes a la especificación 1.2.
  • JSF únicamente soporta peticiones realizadas con POST.
  • La especificación no obliga a que haya validaciones en el cliente, si bien dos desarrollos como MyFaces y Shale proporcionan esta posibilidad.

Recomendaciones de uso

Paso de parámetros a los actions

Con los tags h:commandLink y h:commandButton se puede invocar un método del backing bean utilizando el atributo action o actionListener, pero no se le puede pasar un parámetro directamente por lo que el tag f:attribute puede resultar útil usándolo junto con el actionListener, un ejemplo:

<h:form>
    <h:commandLink actionListener="#{miBean.action}">
        <f:attribute name="nombreAtributo1" value="valorAtributo1" />
        <f:attribute name="nombreAtributo2" value="valorAtributo2" />
       
        <h:outputText value="De click aquí" />
    </h:commandLink>
    <h:commandButton value="Click" actionListener="#{miBean.action}">
        <f:attribute name="nombreAtributo1" value="valorAtributo1" />
        <f:attribute name="nombreAtributo2" value="valorAtributo2" />
       
    </h:commandButton>
</h:form></pre>

Luego, estos atributos pueden ser recuperados utilizando el método getAttributes() del componente que implementa el ActionEvent que manejó el actionListener.

package ejemplo;
import javax.faces.event.ActionEvent;
import es.juntadeandalucia.cice.util.FacesUtil;
public class MyBean {
     public void action(ActionEvent event) {
        String strAtributo1 = FacesUtil.getActionAttribute(event, "nombreAtributo1");
        String strAtributo2= FacesUtil.getActionAttribute(event, "nombreAtributo2");
        ...
    }
}
package es.juntadeandalucia.cice.util;
import javax.faces.event.ActionEvent;
public class FacesUtil {
    public static String getActionAttribute(ActionEvent event, String name) {
        return (String) event.getComponent().getAttributes().get(name);
    }
}

Por lo que las variables strAtributo1 y strAtributo2 ahora ya tienen los valores asignados de nombreAtributo1 y nombreAtributo2 respectivamente. Se debe tener en cuenta que cada nombre de atributo debe ser único y no sobreescribir atributos por defecto del componente como "id", "name", "value", "binding", "rendered", etc.

Pasar objetos de request en request

Si se tiene un bean con scope de request y se quiere reutilizar una propiedad, parámetro u objeto en la siguiente petición sin tener que reinicializarlo otra vez, se puede utilizar el tag h:inputhidden para guardar el objeto:

<h:form>
    ...
    <h:inputHidden value="#{miBean.value}" />
    ...
</h:form>

También se puede utilizar un RequestMap para pasar objetos al siguiente request, teniendo en cuenta que serán "limpiados" después de un request.

package es.juntadeandalucia.cice.util;
import javax.faces.context.FacesContext;
public class FacesUtil {
    public static Object getRequestMapValue(FacesContext context, String key) {
        return context.getExternalContext().getRequestMap().get(key);
    }
    public static void setRequestMapValue(FacesContext context, String key, Object value) {
        context.getExternalContext().getRequestMap().put(key, value);
    }
}

Otra manera es utilizar un SessionMap para mantener los valores que deben ser guardados durante la sesión del usuario:

package es.juntadeandalucia.cice.util;
import javax.faces.context.FacesContext;
public class FacesUtil {
 
    public static Object getSessionMapValue(FacesContext context, String key) {
        return context.getExternalContext().getSessionMap().get(key);
    }
    public static void setSessionMapValue(FacesContext context, String key, Object value) {
        context.getExternalContext().getSessionMap().put(key, value);
    }
    public static Object removeSessionMapValue(FacesContext context, String key) {
        return context.getExternalContext().getSessionMap().remove(key);
    }
}

Comunicación entre ManagedBeans

Es posible tener más de un managed bean en un scope, si es absolutamente necesario por diseño. En ese caso se puede utilizar el método getSessionMap() de FacesContext para comunicar los beans durante una sesión de navegador. Un ejemplo de dos managed beans declarados en el fichero faces-config.xml:

<managed-bean>
    <managed-bean-name>miBean1</managed-bean-name>
    <managed-bean-class>ejemplo.MiRequestBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
    <managed-bean-name>miBean2</managed-bean-name>
    <managed-bean-class>ejemplo.MiSessionBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Tanto miBean1 como miBean2 serán accesibles desde cualquier página JSF, no importando que el scope de ambos sea distinto. El scope de miBean1 es request por lo que en cada petición se creará una nueva instancia mientras que el de miBean2 está puesto en session, en cuyo caso la misma instancia del bean será utilizada durante toda la sesión. Con la finalidad de obtener y asignar los valores en el SessionMap, podría ser útil crear una superclase con algunos métodos protected que fueran heredados por cada backing bean, o sólo poner el mapa en una clase utility como la que se está viendo en los ejemplos, FacesUtil, de cualquier manera sería:

FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key);
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(key, value);

Uso de datatable

Las tablas dinámicas que se pueden mostrar con el tag h:dataTable reciben un objeto de tipo List o DataModel con una colección de beans o pojos, o un mapa de objetos que los contiene.

Por ejemplo para una tabla que contenga tres campos, ID, Nombre y Valor, se crearía una clase wrapper que represente cada fila de la tabla, una clase simple con los campos privados que se accedan con los métodos públicos getters y setters. Como los datos de la bbdd pueden ser nulos se evita utilizar tipos de datos primitivos.

Se puede usar el tag h:commandLink o h:commandButton en una o más columnas para llamar a un action de MiBean.java y pasarle el objeto MiData apropiado, esto es más rápido que pasarlo con el tag f:attribute con un ID para obtener el elemento seleccionado directamente de la base de datos. Para conocer la fila seleccionada se usa el binding de h:dataTable para dinámicamente sincronizar el estado de la tabla con el backing bean.

Se muestra un ejemplo:

<h:form>
    <h:dataTable
        value="#{myBean.myDataList}"
        var="myDataItem"
        binding="#{myBean.myDataTable}">
        <h:column>
            <f:facet name="header">
                <h:outputText value="ID" />
            </f:facet>
            <h:commandLink action="#{myBean.editMyData}">
                <h:outputText value="#{myDataItem.id}" />
            </h:commandLink>
        </h:column>
        ...
    </h:dataTable>
</h:form>

Obtener un registro seleccionado

En el código anterior se indica que al hacer click en h:commandLink se invocará el método editMyData() de MyBean.

El elemento MyData que corresponde a ese registro puede ser obtenido con el método getRowData() de la clase HtmlDataTable. Extendiendo la clase MiBean con el siguiente código:

package ejemplo;
import javax.faces.component.html.HtmlDataTable;
public class MyBean {
    private HtmlDataTable myDataTable;
    private MyData myDataItem = new MyData();
    public String editMyData() {
        // Obtener el elemento MyData para editarlo
        myDataItem = (MyData) getMyDataTable().getRowData();
        return "edit"; // Navega hacia edit
    }
  
    public HtmlDataTable getMyDataTable() {
        return myDataTable;
    }
    public MyData getMyDataItem() {
        return myDataItem;
    }
    public void setMyDataTable(HtmlDataTable myDataTable) {
        this.myDataTable = myDataTable;
    }
    public void setMyDataItem(MyData myDataItem) {
        this.myDataItem = myDataItem;
    }
}

Seleccionar varios registros utilizando checkboxes. Se pueden seleccionar varios registros, agregando una propiedad de tipo boolean a la clase wrapper en este caso MyDate y en la columna desplegarlo como h:selectBooleanCheckbox.

package ejemplo;
public class MyData {
    private boolean selected;
    public boolean isSelected() {
        return selected;
    }
    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

Y en la página JSF

<h:form>
    <h:dataTable
        value="#{myBean.myDataList}"
        var="myDataItem"
        binding="#{myBean.myDataTable}">
        <h:column>
            <f:facet name="header">
                <h:outputText value="Select" />
            </f:facet>
            <h:selectBooleanCheckbox value="#{myDataItem.selected}" />
        </h:column>
        ...
    </h:dataTable>
    <h:commandButton
        action="#{myBean.getSelectedItems}"
        value="Elementos seleccionados"/>
</h:form>

Listener del ciclo de vida

Puede ser útil para depurar y cuando se están dando los primeros pasos con JSF el poner un listener de las diferentes etapas del ciclo de vida de una página JSF. Las 6 fases de la vida JSF son:

  • Restaurar vista.
  • Asignar valores de petición.
  • Realizar validaciones.
  • Actualizar los valores del modelo.
  • Invocar la aplicación.
  • Presentar la respuesta.

Este sería un ejemplo de un LifeCycleListener:

package ejemplo;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class LifeCycleListener implements PhaseListener {
  public void beforePhase(PhaseEvent event) {
    System.out.println("Fase Anterior: " + event.getPhaseId());
  }
  public void afterPhase(PhaseEvent event) {
    System.out.println("Fase Posterior: " + event.getPhaseId());
  }
  public PhaseId getPhaseId() {
    return PhaseId.ANY&#95;PHASE;
  }
}

Se agrega al fichero faces-config.xml:

<lifecycle>
   <phase-listener>mypackage.LifeCycleListener</phase-listener>
</lifecycle>

y se obtendría la salida:

Fase Anterior: RESTORE&#95;VIEW 1
Fase Posterior: RESTORE&#95;VIEW 1
Fase Anterior: APPLY&#95;REQUEST&#95;VALUES 2
Fase Posterior: APPLY&#95;REQUEST&#95;VALUES 2
Fase Anterior: PROCESS&#95;VALIDATIONS 3
Fase Posterior: PROCESS&#95;VALIDATIONS 3
Fase Anterior: UPDATE&#95;MODEL&#95;VALUES 4
Fase Posterior: UPDATE&#95;MODEL&#95;VALUES 4
Fase Anterior: INVOKE&#95;APPLICATION 5
Fase Posterior: INVOKE&#95;APPLICATION 5
Fase Anterior: RENDER&#95;RESPONSE 6
Fase Posterior: RENDER&#95;RESPONSE 6

Mensajes de error

En JSF se pueden configurar paquetes de recursos y personalizar los mensajes de error para convertidores y validadores. El paquete de recursos se configura dentro de faces-config.xml:

<message-bundle>catalog.view.bundle.Messages</message-bundle>

Las parejas clave-valor de los mensajes de error se añaden al fichero Message.properties:

#conversion error messages
javax.faces.component.UIInput.CONVERSION=Input data is not in the correct type.
#validation error messages
javax.faces.component.UIInput.REQUIRED=Required value is missing.

Recomendaciones de diseño

Beans

JSF presenta dos nuevos términos:

  • managed bean (objeto manejado): Un managed bean describe cómo se crea y se maneja un bean. No tiene nada que ver con las funcionalidades del bean.
  • backing bean (objeto de respaldo). El backing bean define las propiedades y la lógica de manejo asociadas con los componentes UI utilizados en la página. Cada propiedad del bean de respaldo está unida a un ejemplar de un componente o a su valor. Un backing bean también define un conjunto de métodos que realizan funciones para el componente, como validar los datos del componente, manejar los eventos que dispara el componente y realizar el procesamiento asociado con la navegación cuando el componente se activa.

Una típica aplicación JSF acopla un backing bean con cada página de la aplicación. Sin embargo, algunas veces en el mundo real de nuestras aplicaciones, forzar una relación uno-a-uno entre el backing bean y la página no es la solución ideal. Puede causar problemas como la duplicación de código. En el escenario real, varias páginas podrían necesitar compartir el mismo backing bean tras bambalinas.

En comparación con la aproximación ActionForm y Action de Struts, el desarrollo con beans de respaldo de JSF sigue unas mejores prácticas de diseño orientado a objetos. Un backing bean no sólo contiene los datos para ver, también el comportamiento relacionado con esos datos. En Struts, Action y ActionForm contienen los datos y la lógica por separado.

Manejo del scope y los managed beans

Se denomina scope a la disponibilidad (o contexto) de un objeto y a su período de vida en una aplicación web.

Se verá en un ejemplo de aplicación de encuesta la utilización de un objeto con scope application para guardar los votos y un objeto de scope session para asegurar que el usuario solo pueda votar una vez por sesión. Se utilizará también un objeto con scope request para mostrar en pantalla la hora en que el usuario emitió su voto, este objeto es de tipo request porque ese valor ya no se necesitará más una vez mostrado.

Tan pronto como un usuario está en una pagina, los valores de los componentes son "recordados" cuando se muestra la página por ejemplo el hecho de que el usuario haga click en un botón que regrese null. Sin embargo cuando abandona la página el valor del componente desaparece.

Para tener disponibles valores en otras páginas o incluso en la misma página lo lógico sería guardar los valores. Antes de crear una propiedad para que guarde un valor, se debe determinar el scope apropiado para ese valor, ya que muchos usuarios podrían acceder a la aplicación al mismo tiempo, así que se necesitaría utilizar el scope más corto posible para hacer un mejor uso de los recursos del servidor. La siguiente figura muestra la duración de cada tipo de scope.

Por ejemplo, una aplicación que tenga una lista desplegable con tipos de medidas (píxeles, centímetros y pulgadas), se tendría que guardar en un ApplicationBean1, así todos los usuarios en sesiones concurrentes podrían compartir la lista. Por otro lado el nombre del usuario logado se guardaría en un SessionBean1 para que el nombre esté disponible en todas las páginas que el usuario acceda durante su sesión. En caso de no necesitar alguna información más allá de la petición actual entonces se utilizaría el RequestBean1.

Validadores

Los validadores estándar que vienen con JSF son básicos y podrían no cumplir con los requerimientos de la aplicación, pero desarrollar validadores JSF propios es sencillo, aunque lo más recomendable es utilizar los built-in y sólo en casos de extremo detalle o complejidad de la validación crear uno pero extendiendo de la clase base de JSF.

Paginación

Cuando la información a presentar en una página es demasiada, se debe optar por partirla en varios apartados o páginas, permitiendo desplazarse al usuario entre todas ellas, con el típico interfaz Página 1 < Atrás | 1 2 3 4 | Adelante >. La paginación debe hacerse desde back-end, es decir, desde las consultas a la base de datos si la cantidad de registros es muy grande y no sólo a nivel front-end. En el caso de displaytag es recomendable utilizarlo cuando las colecciones de objetos son pequeñas ya que sube a sesión toda la colección para después manipularla.

JSF 2.0

Las metas de la especificación 314 para JSF 2.0 se dividen en cuatro áreas importantes:

La primera de estas cuatro áreas se concentra en el desarrollo fácil de componentes JSF:

  • Desarrollo de componentes mediante agregación sin utilización de codificación Java.
  • No es necesaria la configuración. No faces-config.xml, no web.xml.
  • Utilización de anotaciones para componentes, Managed Beans entre otros.
  • JSP ya no será el único sistema de templates usado por JSF. Ahora se tendrá soporte oficial para Facelets

La segunda en Nuevas características:

  • Expandir el ciclo de vida del request para que tenga conciencia de Ajax
  • Proveer de un mecanismo sencillo de acceso a la capa de persistencia
  • Agregaciones estratégicas al RenderKit estándar de HTML: Date Picker, Tree, Tab View, File Upload components.
  • Navegación implicita: ya no es necesario definir cada regla de navegación en un xml. JSF buscará automáticamente una vista que haga match con el nombre de un outcome de acción y redirigirá automáticamente.

La tercera en el Desempeño y Escalabilidad del Runtime:

  • Salvar y restaurar parte de las páginas en lugar de restaurar el estado de la vista completamente cada vez que se requiere.
  • Streamline, el proceso de renderizado vía cache si es posible.

Y la cuarta en la Adopción:

  • Mejorar la especificación de UIComponent para permitir un incremento en la interoperabilidad de las librerías de UIComponents de diferentes vendedores.
  • Permitir a los recursos de las aplicaciones JSF ser accedidos vía REST.
  • "Skinning", o "Themeing" de componentes.

Buenas prácticas y recomendaciones de uso

Estructura y diseño

La estructura de directorios estándar de una aplicación JSF es la siguiente:

proyect\MyApp \\
\src \\
->YourBeans.java \\
\web \\
->YourJSP-pages.jsp \\
->AnyImages.gif \\
\WEB-INF \\
->web.xml \\
->faces-config.xml

Donde en el directorio “src” se sitúan los beans de la aplicación, en “web” todas las páginas jsp ó html, así como las imágenes y demás archivos necesarios para la aplicación, y en “WEB-INF” los ficheros de configuración como el fichero descriptor de despliegue (web.xml) y el de configuración JSF (faces-config.xml).

Codificación

En cuanto a la importación de librerías, se debe usar el prefijo “f” para hacer referencia a etiquetas del núcleo de la implementación mientras que el prefijo “h” para hacer referencia a etiquetas de componentes HTML:

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

Respecto a los validadores empleados, es recomendable la creación de un catálogo de validadores, fácilmente accesible, donde se encontrará la relación entre validadores, objetos de negocio y controles. Estos validadores se crearán en el mismo paquete, al igual que las clases para las etiquetas JSP. El nombre del validador deberá identificar, como mínimo, el control o tipo de control sobre el que actúa y la acción de validación que realiza. Sería recomendable que también el validador incluyera comentarios en el código, tanto para comentar el proceso como para generar documentación javadoc.

Subvistas dinámicas

En cuanto al uso de scriptlets, la etiqueta jsp:include sólo acepta el uso de scriptlets. Se requiere que el managed bean tenga el alcance sesión, de lo contrario, surgirán problemas al usar el alcance request. Sin embargo, los mismos problemas surgen al usar múltiples subvistas e incluir etiquetas como se describe arriba.

El código XML relevante del fichero faces-config es el siguiente:

<managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>mypackage.MyBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

El código relevante del fichero JSP principal es:

<%
    mypackage.MyBean myBean = (mypackage.MyBean) session.getAttribute("myBean");
    if (myBean == null) { // First-time initialization of bean not done yet.
        myBean = new mypackage.MyBean();
    }
    String includePage = myBean.getIncludePage() + ".jsp";
%>
<f:view>
       
            <h:panelGroup rendered="#{myBean.includePage != null}">
                <jsp:include page="<%= includePage %>" />
            </h:panelGroup>
       
</f:view>

IMPORTANTE: no usar f:subview en lugar de h:panelGroup. Todas las páginas incluidas deberían tener su propio f:subview con un único ID. El código java del backing bean MyBean.java debería parecerse al siguiente:

private String includePage;
public String getIncludePage() {
    if (...) { // Do your thing, this is just a basic example.
        includePage = "includePage1";
    } else if (...) {
        includePage = "includePage2";
    } else if (...) {
        includePage = "includePage3";
    }
    return includePage;
}

Ejemplos

Dentro del catálogo interno de la Junta de Andalucía se encuentra el proyecto CRIJA, en el cual se hace uso del framework JSF. Este proyecto se encarga del mantenimiento del censo de equipos microinformáticos. En CRIJA se utilizan páginas xhtml las cuales serán referenciadas mediante extensiones .jsf para ser controladas por el FacesServlet definido en el web.xml:

<!-- Configuracion Facelets -->
    <context-param>
        <param-name>facelets.LIBRARIES</param-name>
        <param-value>/WEB-INF/facelet/tomahawk.taglib.xml;/WEB-INF/facelet/viavansi-custom.taglib.xml</param-value>
    </context-param>
    <context-param>
        <param-name>facelets.DEVELOPMENT</param-name>
        <param-value>true</param-value>
    </context-param> 
    <!-- Solo durante el desarrollo, en produccion quitar--> 
    <context-param>
        <param-name>facelets.REFRESH&#95;PERIOD</param-name>
        <param-value&#621;</param-value>
    </context-param>
    <!-- Los comentarios xhtml son ignorados -->
    <context-param>
        <param-name>facelets.SKIP&#95;COMMENTS</param-name>
        <param-value>true</param-value>
    </context-param>   
   
    <!-- Configuracion JSF-->
    <context-param>
        <param-name>javax.faces.STATE&#95;SAVING&#95;METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.DEFAULT&#95;SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <!--<context-param>
        <param-name>org.apache.myfaces.PRETTY&#95;HTML</param-name>
        <param-value>true</param-value>
    </context-param>-->
    <context-param>
        <param-name>org.apache.myfaces.ALLOW&#95;JAVASCRIPT</param-name>
        <param-value>false</param-value>
    </context-param>   
    <context-param>
        <param-name>javax.faces.CONFIG&#95;FILES</param-name>
        <param-value>
            /WEB-INF/faces-config.xml,
            /WEB-INF/admin-faces-config.xml
        </param-value>
        <description>
            Ficheros de definicion
        </description>       
    </context-param>
    <context-param>
        <param-name>com.sun.faces.validateXml</param-name>
        <param-value>true</param-value>
        <description>
            Set this flag to true if you want the JavaServer Faces
            Reference Implementation to validate the XML in your
            faces-config.xml resources against the DTD.  Default
            value is false.
        </description>
    </context-param>
    <context-param>
        <param-name>com.sun.faces.verifyObjects</param-name>
        <param-value>true</param-value>
        <description>
            Set this flag to true if you want the JavaServer Faces
            Reference Implementation to verify that all of the application
            objects you have configured (components, converters,
            renderers, and validators) can be successfully created.
            Default value is false.
        </description>
    </context-param>

Una vez que se configura el fichero web.xml las páginas xhtml se desarrollarán atendiendo a a las librerías incorporadas. En este caso extraído de la página index.xhtml del directorio web /admin/actualización se hace referencia a las librerías de tomahawk y myFaces que serán utilizadas a través de etiquetas JSTL. Además, se utilizan tags propios definidos por el usuario a través de la etiqueta 'v:'. De esta manera, la página quedaría estructurada como sigue:

<ui:composition template="/includes/${skin}/template.xhtml">
<ui:define name="title">Actualizacion Masiva de Modelos</ui:define>
<ui:define name="head">
        <v:script js="embedded, jquery.highlightFade, forms.help, search.loading, tables" />
    </ui:define>
    <ui:define name="header">
        <h2>Actualizacion Masiva de Modelos</h2>
    </ui:define>   
    <ui:define name="body">
    <!-- enctype="multipart/form-data" -->   
        <v:form id="formularioModelos">
            <t:htmlTag value="div" styleClass="mensajes error"                        
rendered="#{not empty requestScope.resultadoUpdate}">
                <ul><li>
                    <span class="error">#{resultadoUpdate}</span>
                </li></ul>
            </t:htmlTag>
            <p>
                Este proceso actualizara el modelo de todos los                    
registros de hardware, desde un valor origen a un valor destino.
                Posteriormente, eliminara el modelo original.
                <br/>
                La principal utilidad consiste en fusionar modelos                    
similares en uno unico.
            </p>                                             
            <v:inputText label="ID Modelo Original (se borrara)"                
value="#{adminActualizaModelosController.idmodeloOrigen}"
                title="ID MODELO ORIGEN" id="idmodeloOrigen"
                size="10"
                required = "true"
                maxlength="10"/>
                                                       
            <v:inputText label="ID Modelo Destino (todos los modelos                
tendrán este ID Modelo)"                                    
value="#{adminActualizaModelosController.idmodeloDestino}"
                title="ID MODELO DESTINO" id="idmodeloDestino"
                size="10"
                required = "true"
                maxlength="10"/>
                                 
             <p class="botonera">
                 <h:commandButton styleClass="boton"
                     value="Enviar"                                    
action="#{adminActualizaModelosController.actionProcesaModelos}" />         
</p>                           
        </v:form>       
        <!-- Volver a la página anterior -->
        <v:volverPanel label="${msg.volver}"                                
title="${msg.volverApanelDeControl}">           
        </v:volverPanel>
  </ui:define>
</ui:composition>

Contenidos relacionados

Recursos
Área: Desarrollo » Construcción de Aplicaciones por Capas » Capa de Presentación
Código Título Tipo Carácter
RECU-0131 JSF2 Referencia Recomendado
RECU-0819 Implementación de la capa de presentación con JSF Referencia Recomendado