0% encontró este documento útil (0 votos)
32 vistas51 páginas

JSF 2.X

Este documento describe los conceptos básicos de JavaServer Faces (JSF), incluido el tratamiento de peticiones, el ciclo de vida y los componentes. Explica cómo JSF usa ManagedBeans para manejar datos y lógica de negocios, y cómo las vistas se crean y recuperan para mostrar información a los usuarios. También cubre temas como conversiones, validaciones, eventos y navegación entre vistas.

Cargado por

Javier Alvarez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
32 vistas51 páginas

JSF 2.X

Este documento describe los conceptos básicos de JavaServer Faces (JSF), incluido el tratamiento de peticiones, el ciclo de vida y los componentes. Explica cómo JSF usa ManagedBeans para manejar datos y lógica de negocios, y cómo las vistas se crean y recuperan para mostrar información a los usuarios. También cubre temas como conversiones, validaciones, eventos y navegación entre vistas.

Cargado por

Javier Alvarez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 51

2022

CURSO JEE 7.

JSF 2.X

Juan Antonio Solves Garcia.

22/06/2022
JSF Página |2

Contenido
JSF.................................................................................... 4
JSF: Tratamiento de la petición ...................................................................... 4

JSF: Ejemplo básico de funcionamiento ......................................................... 6

Tratamiento de eventos ................................................................................ 11

Login en JSF................................................................................................. 12

Gestión de Mensajes .................................................................................... 15

Clase auxiliar: Tratamiento de mensajes ...................................................... 16

Agrupación de temas .................................................................................... 20

Idiomatización ............................................................................................... 23

Carga automática y carga cruzada ............................................................... 24

Conversiones y validaciones ........................................ 28


Conversores ................................................................................................. 28

ConverterDateTime ................................................................................. 28

ConverterNumber .................................................................................... 28

Conversor Propio ..................................................................................... 29

Validadores ................................................................................................... 30

Validadores de rango ............................................................................... 30

Validador de campo vacío ........................................................................ 30

Validador mediante expresiones regulares................................................ 30

Validador Personalizado .......................................................................... 30

Ejemplo de validación y conversión .............................................................. 31

Librerías de componentes adicionales ( IceFaces) ..... 35


Componentes ............................................................................................... 37

Panel ........................................................................................................ 38

TabSet ..................................................................................................... 39

Acordeón ................................................................................................. 41
JSF Página |3

AceDialog................................................................................................ 41

NotificationPanel ..................................................................................... 43

Tooltip ..................................................................................................... 44

Menús ........................................................................................................... 45

Menu y MenuBar ..................................................................................... 45

Menú estatico horizontal con multicolumnas............................................ 47

MenuButton ............................................................................................. 48

ANEXO I: Componentes Web de JSF ............................ 49


JSF Página |4

JSF
JSF (Java Server Faces) es la única tecnología web recomendada por Oracle. Es
una tecnología Java al cien por cien, por lo tanto, todos los objetos son clases y en
general (especialmente las vistas) no tienen nada que ver con otros marcos web. La
programación consiste en clases de Java y archivos XML, de forma que el
funcionamiento de la apliación se controla mediante dichos ficheros XML. Como todo
desarrollo web ha tenido varias versiones, concretamente en la actualidad existen la 1.x
y la 2.x, no siendo necesariamente compatibles entre sí.
JSF es extensible (como cualquier tecnología base), es decir, que se pueden crear
componentes de JSF propios o hacer uso de librerías ya creadas:
• Mojarra1: implementación base de JSF.
• MyFaces: implementación de la comunidad Apache
• RichFaces: implementación de Jboss
• Primefaces: implementación de la comunidad
• IceFaces: implementación de la empresa Icesoft

JSF: Tratamiento de la petición


El tratamiento de la petición lo define y controla un servlet (FaceServlet), que
se definirá como siempre en el fichero XML. Cuando el controlador recibe la petición,
en primer lugar crea o recupera la vista (un objeto de una clase de Java) a partir de la
información que hay en el XML y se manda al usuario para que la vea y comience a
realizar sus peticiones.
Cuando se recibe una petición hecha a partir de una vista ya creada, no se
vuelven a crear los componentes de dicha vista, sino que se recuperan de la vista, es
decir, las vistas se almacenan en el servidor (por ejemplo, si hay mil usuarios, se
almacenan mil vistas en el servidor, lo que implica que se requiere un servidor con
muchos recursos.
Una vez creada o recuperada la vista, tras recibir una petición del usuario, se
cargan los componentes con los datos de la petición. El siguiente paso es, como
siempre, realizar las conversiones y validaciones necesarias. Al realizar este proceso se
puede dar algún error, pero también es posible que se produzca algún error en la carga
de componentes.

1
El nombre proviene de un pez de cardumen, que es un pez pequeño de acuario. Esto se debe a
que en la comunidad de Java hay un interés especial por los peces.
JSF Página |5

El tratamiento de dichos errores es siempre el mismo y está definido en el ciclo


de vida. Se producen excepciones que lo que provocan es que se devuelva la vista de
entrada. Además, las excepciones no se tratan una a una, sino que se encolan y se tratan
a la vez. Es decir, se realizan todas las conversiones y se tratan las excepciones
producidas.
A diferencia de otros marcos de trabajo web, donde se hace una separación de
objetos de dominio (propiedades) y el tratamiento de los mismos, en JSF (al igual que
ocurría con Spring MVC) se realiza todo el proceso en un único objeto, los
ManagedBean. El ManagedBean es un objeto de contexto que obtiene los datos a partir
de los componentes, por lo tanto se crea automáticamente cuando sea necesario y se le
pasan las propiedades de los componentes. En el caso de que se produzca un error (por
mala gestión de conversión o validación), se devuelve la vista.
El último paso es la lógica o negocio, es decir, se llama al método del
ManagedBean necesario para la realización de dicha lógica. Una vez finalizada la lógica
se devuelve la vista que le corresponda al cliente, que puede ser la misma vista u otra
nueva (en cuyo caso se crean los componentes necesarios). En el diagrama 1 se muestra
un ejemplo del proceso.

Cargar los Conversiones Validaciones Carga


Crear / componentes • Error • Error ManagedBean Lógica /
Recuperar Vista • Error (devuelve vista) (devuelve vista) • Error Negocio
(devuelve vista) (devuelve vista)

Diagrama 1. Ejemplo del funcionamiento de JSF

El tratamiento de los eventos se puede realizar antes de la carga del


ManagedBean o justo después. En el caso del primero, se tiene que tratar el evento a
partir del propio evento, mientras que si se produce después de la carga del
ManagedBean, al disponer de las propiedades ya cargadas en éste, puede tratarse el
evento.
Una petición puede tratar eventos y acción (lógica), no son excluyentes, de la
misma forma que puede tratarlos juntos o por separado. En caso de que se trate un
evento por separado, siempre se devuelve la vista en vigor. En el caso contrario, para la
lógica, siempre se navega. Dependiendo de la versión de JSF, si la vista a devolver es la
que está vigente, se puede reutilizar o volver a cargarla por completo.
JSF Página |6

JSF se diseñó desde un principio para que cumpliese las reglas más estrictas del
desacomplamiento. Así, los ManagedBean son clases puras, sin interfaces ni herencias.
Es decir, POJOS puros. Por lo tanto, para la comunicación entre los componentes y los
ManagedBean se emplean expresiones dinámicas, el componente conoce la expresión y
el ManagedBean conoce el resultado de dicha expresión. En principio dichas
expresiones se situarían en la vista (ficheros XHTML), pero para poder utilizarlas
dentro del ManagedBean se extraerá el motor de expresiones dinámicas de JSF y se
utilizará a conveniencia.

JSF: Ejemplo básico de funcionamiento


Se crea un proyecto Web normal y corriente y se le añaden los Project Facets de
Java Server Faces, seleccionando la versión 2.2. Dependiendo de la versión escogida,
se muestran una o varias opciones a escoger, tal y como se muestra en la Fig. 1. En esta
ventana se muestran opciones como el nombre y la localización del archivo de
configuración, el nombre del servlet y la clase del mismo. También se muestra un
context param del web.xml, denominado Project Stage, que puede tomar los valores
Development o Production (por defecto). Esto proporciona una serie de herramientas de
ayuda que varían en el caso de que la aplicación sea para desarrollo o producción.
Un detalle importante es que la URL Pattern que viene por defecto (*.faces) se
cambia por *.xhtml.
JSF Página |7

Fig. 1. Configuración de JSF

Una vez finalizado, se añaden las librerías al proyecto y se crea el faces-


config.xml vacío y el web.xml con unos cuantos añadidos: el welcome-file-list, el servlet
del controlador y el context-param del Project Stage.
<!-- ******** Zona de controladores (servlets) ******* -->
<!-- Controlador principal de jsf -->
<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>
<!-- ******* Fin zona de controladores (servlets) ******* -->
<!-- ** Zona de parametros iniciales de configuracion de la aplicacion
** -->
<!-- Interesantes para jsf -->
<context-param>
<description>activa el uso de ayudas en desarrollo</description>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- ** Fin zona parametros iniciales configuracion de la aplicacion
** -->
<!-- Mecanismo de arranque en primera peticion -->
<welcome-file-list>
<welcome-file>xhtml/pagina_inicial.xhtml</welcome-file>
</welcome-file-list>
JSF Página |8

Además, se ha seleccionado como página inicial pagina_inicial.xhtml, que


deberá ser creada. Para ello, se puede utilizar el asistente de Advanced XHTML,
indicándole en el Doctype el XHTML 5 (por ejemplo) y la plantilla para Java Server
Faces 2.2.
Esto crea un documento XML, no una página html ni jsp, con su propio espacio
de nombres. De todos los espacios de nombres, los que son de JSF son xmlns:ui,
xmlns:h y xmlns:f.
Las etiquetas de xmlns:h generan componentes que se van a ver en la vista
(componentes visibles), normalmente checkbox, radio-button, textfields, etc. Las
etiquetas xmlns:f son componentes internos de JSF que no son visibles y que no generan
una salida por pantalla. Por último, los xmlns:ui equivalen al tiles de JSF, siendo los
componentes que se encargan de la navegación dinámica entre páginas. Cuando se usan
librerías externas, generalmente los componentes de xmlns:h se sustituyen por los de las
librerías, manteniéndose habitualmente los de xmlns:ui y xmlns:f.
En la versión JSF 2.2 son obligatorios la cabecera y el cuerpo, siendo definidas
dentro del namespace de xmlns:h, es decir, <h:head> y <h:body>. Los formularios, se
definen como <h:form>. En general, se considera una buena práctica indicar un id para
cada componente. En caso de que no se incluya un id personalizado, JSF tiene un
sistema de generación automática basado en letras y números, siendo más complicada la
identificación del componente en caso de error.
A la hora de definir las acciones, a diferencia de lo que ocurría en otros marcos
de trabajo, no se incluyen en los formularios, es decir, no existe el atributo action para
los formularios. La gestión de los componentes siempre va a nivel de contenedor,
siendo el panelGrid el único contenedor disponible en la implementación base de JSF.
El panelGrid (<h:panelGrid>) implementa el GridLayout, que realiza una ordenación
de los elementos por columnas, siendo necesario indicar el número de columnas del
panel (mediante el atributo columns). Para mostrar un texto, se emplea el componente
<h:outputText>, cuyo value será el texto a mostrar (o una expresión dinámica en caso
de usar idiomatización. Para introducir un texto se emplea <h:inputText>, cuyo value
será una expresión dinámica que indique la propiedad relacionada del ManagedBean.
Para que el asistente ayude lo máximo posible, se recomienda crear primero el
ManagedBean, de forma que a la hora de modificar la página, sea el propio asistente el
que sugiera los nombres de las propiedades.
JSF Página |9

Para crear el ManagedBean, se crea un paquete propio denominado


com.atrium.managedbean. Como se comentó anteriormente, la clase que representa el
ManagedBean no extiende de ninguna otra ni implementa ninguna interfaz. Un detalle
importante a tener en cuenta es que el Project Stage fijado a development realiza una
serie de comprobaciones que pueden llegar a dar error. Una de estas comprobaciones es
que los ManagedBean deben implementar la interfaz Serializable. En caso de que no la
implemente, no afecta al funcionamiento de la aplicación, pero se muestran unos
mensajes de error bastante molestos. Para evitarlos, se puede implementar dicha
interfaz, que como no obliga a la implementación de métodos, sigue dejando un POJO
limpio. El ManagedBean deberá tener una propiedad que se corresponda con la del
formulario, así como sus respectivos accesores.
Una vez creado el ManagedBean, se deberá indicar en el archivo faces-
config.xml la localización y el scope (contexto de JSF) del mismo.
<managed-bean>
<managed-bean-name>prueba_managedbean</managed-bean-name>
<managed-bean-class>com.atrium.managedbean.Prueba_Bean</managed-
bean- class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Las propiedades de Scope en la versión uno de JSF disponibles son:


• Request: el ManagedBean es un objeto de la petición, por lo que cuando la
petición finalice, el ManagedBean se elimina
• Session: en el caso de que se vayan a recibir varias peticiones (por ejemplo con
componentes avanzados, que usan Ajax). Sin embargo, presenta un problema,
dado que cuando la vista se desecha, el componente sigue permaneciendo en el
contexto de la sesión, que deberá ser eliminado de forma manual, porque consume
memoria y recursos
• Application: en el caso de que se vaya a usar un ManagedBean para toda la
aplicación, se puede usar esta propiedad, siendo entonces un objeto de contexto
del servlet

Además, en la versión dos se añaden las siguientes:


• Flash: puede tener una duración inferior al de request. Por ejemplo, en el caso de
que se procese una vista por fragmentos, el ManagedBean puede estar asociado a
uno de esos fragmentos, siendo la duración del mismo el tiempo en que se procese
el fragment
• Window: puede permanecer activo más allá de la sesión (si el usuario cierra la
ventana y vuelve a entrar)
JSF P á g i n a | 10

• View: es el más interesante, dado que es un contexto que va ligado a la vista, por
lo que la duración del ManagedBean es la misma que la de la vista. Mientras la
vista permanezca activa, el ManagedBean sigue existiendo

A la hora de incluir las propiedades en la página XHTML, como se comentó


anteriormente, será necesario emplear una expresión dinámica. Al incluir la expresión
dinámica vacía en el campo value, el asistente proporciona los elementos de JSF
disponibles (marcados con un grano de café) y los que solamente pueden existir dentro
de la expresión dinámica. Si se selecciona el ManagedBean y se accede a sus
propiedades y métodos, el asistente indica mediante los simbolos de ángulo verde y rojo
que la propiedad posee métodos get y set, respectivamete.
Por último, se añade un botón mediante <h:commandButton>, que es el que
tiene la accion y permite la gestión de eventos (actionListerner). Tanto action como
actionListener van a realizar llamadas a métodos, que estarán definidos por expresiones
dinámicas. Dichos métodos deben cumplir una serie de condiciones:
Un método de acción deberá ser public, devolver un String (vista que se va a
cargar) y no recibir ningún parámetro. Un método de actionListener deberá ser public,
no devolver nada y recibir por parámetro el evento (javax.server.faces.ActionEvent).
Por lo tanto, la clase Prueba_Bean queda:
public class Prueba_Bean {
//Propiedad para recibir el valor del componente
private String texto_prueba;

public String metodo_Accion() {


System.out.println("Soy la accion");
return "navegacion_accion";
}

public void metodo_Evento(ActionEvent evento) {


System.out.println("Soy el evento");
}

//Accesores para JSF


public String getTexto_prueba() {
return texto_prueba;
}

public void setTexto_prueba(String texto_prueba) {


this.texto_prueba = texto_prueba;
}
}
JSF P á g i n a | 11

En la definición de los métodos dentro de la página XHTML, se recomienda


eliminar los paréntesis de los métodos, debido a que dependiendo de la versión de JSF
pueden dar problemas. Es decir, se sustituye por ejemplo:
<h:commandButton id="boton"
action="#{prueba_managedbean.metodo_Accion()}"
actionListener="#{prueba_managedbean.metodo_Evento()}" />
Por lo siguiente:
<h:commandButton id="boton"
action="#{prueba_managedbean.metodo_Accion}"
actionListener="#{prueba_managedbean.metodo_Evento}" />
Así, pagina_inicial.xhtml queda:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions" lang="en">
<h:head>
<title>JSF 2.2 Page</title>
<meta name="keywords" content="enter,your,keywords,here" />
<meta name="description" content="A short description of this
page." />
<meta name="content-type" content="text/html; charset=UTF-8" />

<!--<link rel="stylesheet" type="text/css" href="styles.css">-->


</h:head>
<h:body>
<h:form id="formu_prueba">
<h:panelGrid id="panel_prueba" columns="2">
<h:outputText id="eti_prueba"
value="#{prueba_managedbean.texto_prueba}" />
<h:commandButton id="boton" value="Comprobar"
action="#{prueba_managedbean.metodo_Accion}"
actionListener="#{prueba_managedbean.metodo_Evento}" />
</h:panelGrid>
</h:form>
</h:body>
</html>

Tratamiento de eventos
El tratamiento de eventos que hace JSF es el modelo de delegación, es decir,
algún objeto (un componente de JSF) genera un evento (emisor) y delega el tratamiento
en un tercero (receptor), no habiendo retroalimentación. A diferencia de Swing, donde
los receptores implementan una interfaz para el tratamiento del evento, en JSF se
emplean las expresiones dinámicas.
JSF P á g i n a | 12

Por ejemplo, si se quiere tratar el evento previamente a la fase 5, mediante la


propiedad valueChangeEvent de <h:inputText> se puede hacer.
<h:inputText id="texto_prueba" value="#{prueba_inicial.texto_prueba}"
valueChangeListener="#{prueba_inicial.metodo_EventoCambioValor}"
/>
Para ello, se deben crear los métodos indicados en las expresiones dinámicas
dentro del ManagedBean. Si en el constructor del ManagedBean se da un valor inicial a
la propiedad texto_prueba, la pagina_inicial.xhtml mostrará inicialmente el valor de
dicha propiedad.
public Prueba_Bean() {
texto_prueba = "Hola Mundo";
}
Si se cambia el valor de dicho campo y se realiza la petición, en primer lugar se
ejecutará el método metodo_EventoCambioValor(), de forma que tras la petición el
valor de la propiedad texto_prueba habrá cambiado con el nuevo texto introducido por
el usuario. Por lo tanto, es un evento previo a la carga del ManagedBean.

Login en JSF
Se crea un nuevo proyecto Web y se añaden los Project Facets de Hibernate,
Spring y JSF. Una vez creado, se cambia el welcome-file-list a la página de inicio
deseada:
<!-- MECANISMO DE ARRANQUE EN PRIMERA PETICION -->
<welcome-file-list>
<welcome-file>xhtml/login.xhtml</welcome-file>
</welcome-file-list>

Se crea un nuevo xhtml dentro de la carpeta xhtml, tal y como se ha indicado en


el welcome-file-list. Dicha página contendrá en el cuerpo lo siguiente:
<h:body>
<!-- FORMULARIO DE LOGIN -->
<h:form>
<h:panelGrid id="panel_login" columns="2">
<h:outputText id="eti_nombre" value="Nombre:" />
<h:inputText id="nombre_login"
value="#{login_bean.nombre_usuario}" />
<h:outputText id="eti_clave" value="Clave:" />
<h:inputText id="clave_login"
value="#{login_bean.clave_usuario}" />
<h:commandButton id="boton_login" value="Comprobar"
action="#{login_bean.comprobar_Credenciales}"
/>
</h:panelGrid>
</h:form>
</h:body>
JSF P á g i n a | 13

En case a lo anterior, será necesario crear un ManagedBean denominado


login_bean, que tendrá dos propiedades (nombre_usuario y clave_usuario) y sus
correspondientes accesores. A efectos de traza se incluye el constructor un System.out.
Por último, se añade el método comprobar_Credenciales, que de momento simplemente
contiene un System.out a modo de traza.
A continuación se añade el ManagedBean en el XML de configuración de JSF y
se corrige el error de ruta de Spring sobre el fichero de configuración de Hibernate:
<managed-bean>
<managed-bean-name>login_bean</managed-bean-name>
<managed-bean-class>com.atrium.managedbean.Login_Bean</managed-
bean- class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Si se realiza una comprobación, se puede ver que se tras realizar la petición al


pulsar en el botón, las propiedades del ManagedBean se actualizan con los valores del
formulario introducidos por el usuario, así como se pasa por el método
comprobar_Credenciales.
Para poder realizar la lógica asociada al login, se obtienen por ingeniería inversa
las clases asociadas a las tablas Usuarios, Roles, Tareas y RolesTareas y se crea la
fachada y paquetes correspondientes. Además, se añade la clase
Acceso_ApplicationContext al proyecto y se añade también dentro del fichero de
configuración de Spring. Así, el método comprobar_Credenciales una vez se haya
modificado para que se comprueben los datos recibidos queda:

public String comprobar_Credenciales() {


//Consulta a la base de datos
Usuarios usuario = Acceso_ApplicationContext.getBean(

IGestion_Usuarios.class).consultar_PorNombre(
getNombre_usuario());
//Se establece la recarga de la vista en caso de error
String salida = "";
if(usuario != null) {
if(usuario.getPassword().equals(getClave_usuario())) {
//Credenciales correctas
salida = "correcto";
}else {
//Error en la clave
}
}else {
//Error de nombre
}
return salida;
JSF P á g i n a | 14

La navegación entre vistas se define también en el face-config.xml. Para ello, se


hace uso de la etiqueta <navigation-rule>, que a su vez contiene:
• <font-view-id>: se le indica la ruta y nombre de la vista a la que pertenece la regla
de navegación
• <to-view-id>: se le indica a qué vista se debe navegar
• <front-outcome>: nombre identificativo de la vista a la que se va a navegar

Por lo tanto, se crea una nueva regla de navegación para el caso en la que los
datos introducidos por el usuario sean correctos:
<navigation-rule>
<from-view-id>/xhtml/login.xhtml</from-view-id>
<navigation-case>
<from-outcome>correcto</from-outcome>
<to-view-id>/xhtml/despues_login.xhtml</to-view-id>
</navigation-case>
</navigation-rule>

De esta forma, si el método comprobar_Credenciales devuelve la cadena de


texto correcto, se navegará a la página despues_login.xhtml, mientras que si se
devuelve una cadena vacía se muestra la vista vigente.
Gestión de Mensajes
La gestión de mensajes en JSF se realiza a partir de los objetos FacesMessage.
Un objeto de este tipo está dividido en dos partes, sumario y detalle, ambos texto plano,
que no son excluyentes entre sí. Además, se le puede asignar un nivel que sirve para
aplicarle un estilo u otro en función del mismo2.
Los niveles que puede tomar un mensaje son los siguientes:
• Info
• Error
• Warn
• Fatal

El FacesMessage, a diferencia de Strut, no obliga a la idiomatización y sus


constructores pueden ser:
• new FacesMessage ( )
• new FacesMessage (Sumario)
• new FacesMessage (Sumario, Detalle)
• new FacesMessage (Severidad, Sumario, Detalle)

Como su propio nombre indica, el concepto es que sumario sea un resumen del
contenido del mensaje, mientras que detalle debería ser una explicación más detallada
de éste.
El mensaje debe estar dentro del contexto de JSF, dado que debe estar accesible
para que la vista pueda leer la información del mismo. Para poder resolver esto, se
emplea la clase FaceContext, que es uno de los contextos (el más básico y principal)
propios de JSF. Cada vista tiene su propio objeto FaceContext. Es una clase abstracta,
por lo que no hay posibilidad de instanciarse un objeto con el constructor, sino que tiene
un método de instancia estático denominado getCurrentInstance (). Éste método
devuelve el objeto FaceContext de cada vista.
FaceContext permite acceder a los contextos de JEE (petición, session, etc) y
también a los de JSF. Uno de los métodos que tiene FaceContext es addMessage, el
cual permite añadir el mensaje al contexto de JSF3.

2
Una de las comprobaciones que se realizan cuando se tiene el Project Studio en development es
si se han utilizado todos los mensajes.
3
A efectos prácticos, para ocultar todos los pasos necesarios para recoger y manejar datos, en la
documentación de JSF se recomienda encapsular el proceso dentro de una clase auxiliar, tal y como se
hará en el proyecto.
JSF P á g i n a | 16

Clase auxiliar: Tratamiento de mensajes


En el caso de que se quiera guardar algo en la sesión, como por ejemplo un
usuario, se debe llamar al getCurrentInstance de FaceContext para así poder acceder a
los contextos web mediante getExternalContext y a partir de ahí acceder a la sessión,
que viene casteada como Object debido a que dependiendo de la tecnología web puede
ser servlet o portlet, por lo que es necesario realizar el polimorfismo a HttpSession, tal y
como se muestra a continuación.
((HttpSession) FacesContext.getCurrentInstance()
.getExternalContext().getSession(false)).setAttribute(
"usuario", usuario);

Por lo tanto, se crea una clase auxiliar que recoja todo este proceso y que
simplifique el código en las clases que lo necesiten, ya que simplemente con llamar a un
método de dicha clase auxiliar se obtiene el resultado deseado. El contenido de la clase
auxiliar es el siguiente:
public class Acceso_Contextos implements INiveles_FacesContext {

public static void addMensaje(String mensaje_sumario,


String mensaje_detalle, Severity nivel, String
identificador) {
// ESTABLECEMOS EL NIVEL POR DEFECTO, SI ES NECESARIO
if (nivel == null) {
nivel = INFO;
}
// GENERAMOS EL MENSAJE

FacesContext.getCurrentInstance().addMessage(identificador,
new FacesMessage(nivel, mensaje_sumario,
mensaje_detalle));
}

public static void addMensaje_Idioma(String clave_sumario,


String clave_detalle, Severity nivel, String
identificador) {
try {
// CREO EL RB PARA LEER DEL PROPERTIES ADECUADO
ResourceBundle rb = ResourceBundle
.getBundle((String) getSesion()
.getAttribute("idioma_seleccionado"));
// COMPRUEBO QUE NO VENGAN VACIOS O NULOS
String sumario = "";
String detalle = "";
// SE RECUPERA EL TEXTO EN EL IDIOMA PREFERIDO POR
EL USUARIO
if (clave_detalle != null &&
!clave_detalle.equals("")) {
detalle = rb.getString(clave_detalle);
}
if (clave_sumario != null &&
!clave_sumario.equals("")) {
JSF P á g i n a | 17

sumario = rb.getString(clave_sumario);
}
// ESTABLECEMOS EL NIVEL POR DEFECTO, SI ES
NECESARIO
if (nivel == null) {
nivel = INFO;
}
// GENERAMOS EL MENSAJE

FacesContext.getCurrentInstance().addMessage(identificador,
new FacesMessage(nivel, sumario,
detalle));
} catch (Exception e) {
System.out.println("NO EXISTE PROPERTIES DEFINIDO.
REVISAR ATRIBUTO DE SESION IDIOMA_ELEGIDO");
}
}

public static Object getAtributo(String nombre_atributo) {


return getSesion().getAttribute(nombre_atributo);
}

public static void setAtributo(String nombre_atributo, Object


objeto_valor) {
getSesion().setAttribute(nombre_atributo, objeto_valor);
}

public static HttpSession getSesion() {


HttpSession salida =
(HttpSession)FacesContext.getCurrentInstance()
.getExternalContext().getSession(false);
return salida;
}

public static HttpServletRequest getPeticion() {


HttpServletRequest peticion = (HttpServletRequest)
FacesContext

.getCurrentInstance().getExternalContext().getRequest();
return peticion;
}

public static ServletContext getAplicacion() {


ServletContext aplicacion = ((HttpSession) FacesContext
.getCurrentInstance().getExternalContext()
.getSession(false)) .getServletContext();
return aplicacion;
}

public static HttpServletResponse getRespuesta() {


HttpServletResponse respuesta =
(HttpServletResponse) FacesContext
.getCurrentInstance().getExternalContext()
.getResponse();
return respuesta;
}

public static ApplicationContext getApplication_Context() {


JSF P á g i n a | 18

ApplicationContext application =
WebApplicationContextUtils
.getWebApplicationContext(Acceso_Contextos.getAplicacion());
return application;
}

Además, en dicha clase auxiliar se incluyen métodos de ayuda para el


tratamiento de mensajes, obtención del contexto de Spring, etc. Así el código anterior
para añadir datos a la sesión queda reducido a:
Acceso_Contextos.setAtributo("usuario", usuario);
Por lo tanto, ya se puede resolver el tratamiento de errores en la clase
Login_Bean, de forma que se emplee también la clase auxiliar.
Acceso_Contextos.addMensaje("Nombre de usuario erroneo", null,
Acceso_Contextos.WARN, "formu_login:nombre_login");
Internamente, sin que se deba configurar nada, JSF añade al id del componente,
el identificador del formulario donde está el componente. Por lo tanto, el identificador
queda formado de la siguiente forma:
nombre_formulario:nombre:componente
A continuación, se deben añadir los datos necesarios para que los mensajes se
muestren por pantalla. En primer lugar, el id del formulario debe de coincidir con el
nombre asignado en el addMessage, en este caso formu_login. Para mostrar el mensaje
en la vista, se hace uso de la etiqueta <h:message>4.
<h:message id="error_nombrelogin" for="nombre_login"
showSummary="true" showDetail="false" warnClass="estilo_warn"
infoClass="estilo_info" />
El campo id del mensaje es un nombre identificador del mismo, el atributo for
indica el id del componente al que va asociado el mensaje. Dado que la etiqueta
<h:message> muestra por defecto el detalle y oculta el sumario, como en la clase
Login_Bean se ha añadido el mensaje en el sumario, se hace uso del atributo
showSummary, que permite mostrar el sumario. Los atributos WarnClass e infoClass
sirven para aplicar estilos a niveles, es decir, en este caso WarnClass estará formateado
en base a un estilo denominado estilo_warn, mientras que infoClass tendrá un formato
basado en una hoja de estilos asociada a estilo_info. Siguiendo la definición de JSF,

4
No se debe confundir con la etiqueta <h:messages>, que muestra todos los mensajes
producidos. Por defecto muestra el sumario y oculta el detalle.
JSF P á g i n a | 19

todos los recursos estáticos estar dentro de una carpeta denominada resources y con
subcarpetas por categoría (resources --> css).
Un detalle importante a tener en cuenta es que se debe cambiar el número de
columnas de 2 a 3 para que se incluya una asociada a los mensajes, dado que sino, al
haber dos columnas únicamente el mensaje de error aparecería en la siguiente fila. En la
Tabla 1 y Tabla 2 se muestra un ejemplo:
Tabla 1. Tabla con dos columnas

Nombre Campo_nombre (input)


Mensaje error_nombre Clave
Campo_clave (input) Mensaje_error_clave

Tabla 2. Tabla con tres columnas

Nombre Campo_nombre Mensaje


(input) error_nombre
Clave Campo_clave (input) Mensaje_error_clave

Para realizar la carga de recursos, en este caso de los estilos, se emplea dentro
del head la etiqueta <h:outputStylesheet> indicándole la ruta relativa (dado que toma
automáticamente como directorio principal la carpeta resources ya comentada), tal y
como se muestra a continuación:
<h:outputStylesheet id="carga_tema" name="css/estilos.css"/>
El contenido de la página de estilos es el siguiente:
.estilo_warn {
color: maroon;
}

.estilo_info {
color: blue;
}

.estilo_fatal {
color: silver;
}

.estilo_error {
color: red;
}

Así, cuando se llama en los mensajes de error a los class del archivo css
denominados estilo_info y estilo_warn, se muestran los mensajes formateados en base a
dicho estilo. Así, la página login.xhtml queda:
<h:head>
<title>JSF 2.2 Page</title>
JSF P á g i n a | 20

<meta name="keywords" content="enter,your,keywords,here" />


<meta name="description" content="A short description of this
page." />
<meta name="content-type" content="text/html; charset=UTF-8" />
<h:outputStylesheet id="carga_tema" name="css/estilos.css" />
</h:head>
<h:body>
<!-- FORMULARIO DE LOGIN -->
<h:form id="formu_login">
<h:panelGrid id="panel_login" columns="3">
<h:outputText id="eti_nombre" value="Nombre:" />
<h:inputText id="nombre_login"
value="#{login_bean.nombre_usuario}" />
<h:message id="error_nombrelogin"
for="nombre_login"
showSummary="true" showDetail="false"
warnClass="estilos_warn"
infoClass="estilos_warn" />
<h:outputText id="eti_clave" value="Clave:" />
<h:inputText id="clave_login"
value="#{login_bean.clave_usuario}" />
<h:message id="error_clavelogin" for="clave_login"
showSummary="true" showDetail="false"
warnClass="estilos_warn"
infoClass="estilos_warn" />
<h:commandButton id="boton_login" value="Comprobar"
action="#{login_bean.comprobar_Credenciales}"
/>
</h:panelGrid>
</h:form>
</h:body>

Agrupación de temas
Al igual que realizaba Spring, se puede realizar una agrupación de temas para
proporcionar recursos específicos a cada navegador. Desde el punto de vista de JSF, la
agrupación de temas consiste en una carpeta que contiene todos los recursos asociados
al tema. Para indicarle el tema, se añade el atributo library a la etiqueta
<h:outputStylesheet>.
<h:outputStylesheet id="carga_tema" name="css/estilos.css"
library="defecto" />

Así, buscaría dentro de la carpeta resources/defecto/css/estilos.css. Para que esto


tenga una utilidad real, se puede hacer uso de una expresión dinámica, que dependiendo
por ejemplo del navegador utilizado, indique un library u otro.
En primer lugar se crea un paquete denominado com.atrium.filtros, en el que se
añadirán las clases necesarias para el tratamiento de cada navegador, que en este caso es
un filtro con el siguiente método doFilter:
JSF P á g i n a | 21

public void doFilter(ServletRequest peticion, ServletResponse


respuesta,
FilterChain cadena_peticion) throws IOException,
ServletException {
String nombre_navegador = null;
Integer int_navegador = null;
// CONVERTIMOS EL TIPO DE LA PETICION
HttpServletRequest http_peticion = null;
if (peticion instanceof HttpServletRequest) {
http_peticion = (HttpServletRequest) peticion;
}
// RECOGEMOS EL NAVEGADOR USADO POR EL CLIENTE EN LA PETICION
String navegador_cliente = http_peticion.getHeader("user-
agent");
// EN FUNCION DEL NAVEGADOR ESTABLECEMOS UN ATRIBUTO DE SESION
PARA
// PODER DETERMINAR DINAMICAMENTE LOS RECURSOS A ENVIAR
COMPARACION
// EXPLORER
boolean reconocido = false;
if (navegador_cliente.indexOf("MSIE 6.0") > -1) {
nombre_navegador = "Internet Explorer 6";
int_navegador = new Integer(navegador[0]);
reconocido = true;
}
if (navegador_cliente.indexOf("MSIE 7.0") > -1) {
nombre_navegador = "Internet Explorer 7";
int_navegador = new Integer(navegador[1]);
reconocido = true;
}
if (navegador_cliente.indexOf("MSIE 8.0") > -1) {
nombre_navegador = "Internet Explorer 8";
int_navegador = new Integer(navegador[2]);
reconocido = true;
}
if (navegador_cliente.indexOf("MSIE 9.0") > -1) {
nombre_navegador = "Internet Explorer 9";
int_navegador = new Integer(navegador[3]);
reconocido = true;
}
if (navegador_cliente.indexOf("MSIE 10") > -1) {
nombre_navegador = "Internet Explorer 10";
int_navegador = new Integer(navegador[4]);
reconocido = true;
}
if (navegador_cliente.indexOf("rv:11.0") > -1) {
nombre_navegador = "Internet Explorer 11";
int_navegador = new Integer(navegador[5]);
reconocido = true;
}
// COMPARACION FIREFOX
if (navegador_cliente.indexOf("Firefox") > -1) {
nombre_navegador = "Mozilla Firefox";
int_navegador = new Integer(navegador[6]);
reconocido = true;
}
/ COMPARACION CHROME
if (navegador_cliente.indexOf("Chrome") > -1) {
JSF P á g i n a | 22

nombre_navegador = "Chrome";
int_navegador = new Integer(navegador[7]);
reconocido = true;
}
// TRATAMIENTO PARA LOS CASOS EN QUE NO SE CONOZCA EL NAVEGADOR
if (!reconocido) {
nombre_navegador = "No reconocido";
int_navegador = new Integer(navegador[8]);
}
// COLOCAMOS EN LA SESION LA IDENTIFICACION DEL NAVEGADOR PARA
SU
// POSTERIOR USO, TANTO EN LETRA COMO EN VALOR NUMERICO
http_peticion.getSession().setAttribute("navegador",
int_navegador);
http_peticion.getSession().setAttribute("nombre_navegador",
nombre_navegador);
// TRAZA DEL PROCESO
if (log.isTraceEnabled()) {
log.trace("identificador navegador en letra..:" +
nombre_navegador);
log.trace("identificador navegador en numero..:"
+ int_navegador.intValue());
}
// CONTINUA LA PETICION COMO CORRESPONDA
cadena_peticion.doFilter(peticion, respuesta);
}

A continuación se define en el web.xml:


<filter>
<description>A partir de la cabecera de la petición del
navegador se obtiene el navegador usado</description>
<filter-name>filtro_navegador</filter-name>
<filter-class>com.atrium.filtros.Filtro_Navegador</filter-class>
</filter>

<filter-mapping>
<filter-name>filtro_navegador</filter-name>
<url-pattern>/xhtml/login.xhtml</url-pattern>
</filter-mapping>

Lo siguiente es crear un ManagedBean que tenga una propiedad para establecer


el tema de forma dinámica, con sus respectivos métodos accesores. Además, contendrá
el siguiente método:
public String tema_seleccionado() {
Integer tipo_navegador = (Integer) Acceso_Contextos
.getAtributo("navegador");
if (tipo_navegador.intValue() ==
IFiltro_Navegador.EXPLORER_6) {
tema_jsf = "explorer";
}
if (tipo_navegador.intValue() == IFiltro_Navegador.EXPLORER_7) {
tema_jsf = "explorer";
}
if (tipo_navegador.intValue() == IFiltro_Navegador.EXPLORER_8) {
tema_jsf = "explorer";
JSF P á g i n a | 23

}
if (tipo_navegador.intValue() == IFiltro_Navegador.EXPLORER_9) {
tema_jsf = "explorer";
}
if (tipo_navegador.intValue() == IFiltro_Navegador.EXPLORER_10)
{
tema_jsf = "explorer";
}
if (tipo_navegador.intValue() == IFiltro_Navegador.CHROME) {
tema_jsf = "chrome";
}
if (tipo_navegador.intValue() == IFiltro_Navegador.MOZILLA) {
tema_jsf = "mozilla";
}
if (tipo_navegador.intValue() == IFiltro_Navegador.DESCONOCIDO)
{
tema_jsf = "defecto";
}
return tema_jsf;
}

Se deberá añadir al fichero de configuración de JSF este bean, mediante el


siguiente código:
<managed-bean>
<managed-bean-name>navegador_bean</managed-bean-name>
<managed-bean-
class>com.atrium.managedbean.Navegador_Bean</managed- bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Por último5, se incluye la expresión dinámica en la página login.xhtml, indicando


el objeto ManagedBean creado y el método a ejecutar. En este caso, al no ser un evento
sino una propiedad, se deben incluir los paréntesis identificativos del método.
<h:outputStylesheet id="carga_tema" name="css/estilos.css"
library="#{navegador_bean.tema_seleccionado()}" />

Idiomatización
Como siempre, se crea un paquete dentro del proyecto con los archivos
properties correspondientes. En primer lugar, se le debe indicar a la vista que se va a
utilizar idiomatización. Para ello, se establece el properties desde el que se va a leer a
partir de la etiqueta <f:loadBundle>, que se encarga de la creación del ResourceBundle.
<f:loadBundle basename="#{sessionScope.idioma_seleccionado}"
var="idioma" />

5
Además, se debe crear la ruta de carpetas necesaria para cada navegador, es decir, defecto,
explorer, chrome y mozilla.
JSF P á g i n a | 24

El resourceBundle es accesible en la vista a partir del nombre del campo var, en


este caso idioma. Por lo tanto, a la hora de realizar la idiomatización, se llama a una
expresión dinámica:
<h:outputText id="eti_nombre"
value="#{idioma['formu.login.eti.nombreusuario']}" />

En la expresión dinámica se llama al resourceBundle y, mediante el uso de


corchetes y comillas simples, se le indica la clave del properties. Lo siguiente es
modificar la clase Login_Bean para que al añadir el mensaje, envíe el texto
correspondiente a partir de la clave del properties.
Acceso_Contextos.addMensaje_Idioma("error.login.clave", null,
Acceso_Contextos.WARN, "formu_login:clave_login");

Además, se deberá crear una clase que se encargue de la idiomatización (ya


utilizada en otros proyectos), así como se deberá incluir en el fichero de configuración
web.xml la información necesaria para que se le reconozca.
<filter>
<description>Filtro para realizar la
idiomatización</description>
<filter-name>filtro_idioma</filter-name>
<filter-class>com.atrium.filtros.Filtro_Idioma</filter-class>
</filter>
<filter-mapping>
<filter-name>filtro_idioma</filter-name>
<url-pattern>/xhtml/login.xhtml</url-pattern>
</filter-mapping>

Carga automática y carga cruzada


Para mostrar el funcionamiento de la carga de componentes 6, se va a hacer uso
de las tablas de Paises, Provincias y Municipios. En primer lugar, se le da al usuario la
opción de seleccionar un país. Como solamente se va a seleccionar uno, se emplea un
componente <h:selectOneMenu>.
Este componente mostrará en primer lugar la opción Seleccione un item a partir
de <f:selectItem> y luego proporcionará la lista de elementos obtenidos de la base de
datos (a partir del ManagedBean denominado selección_bean) a partir de
<f:selectItems>.
<h:panelGrid id="formu_listas" columns="2">
<h:outputText id="eti_pais" value="Paises" />
<h:selectOneMenu id="lista_paises"

6
Dentro de la carpeta xhtml/ejemplos se muestran ejemplos de algunos de los componentes
básicos de JSF.
JSF P á g i n a | 25

value="#{seleccion_bean.pais_seleccionado}">
<f:selectItem id="sin_seleccion" itemValue="0"
itemLabel="Seleccione un pais" />
<f:selectItems id="modelo_paises"
value="#{seleccion_bean.lista_paises}" />
</h:selectOneMenu>
</h:panelGrid>

A continuación se le indica al fichero de configuración de JSF que lea el


ManagedBean.
<managed-bean>
<managed-bean-name>seleccion_bean</managed-bean-name>
<managed-bean-
class>com.atrium.managedbean.Seleccion_Bean</managed- bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Y se deberá crear la clase Selección_Bean que represente el ManagedBean


empleado. Dicha clase deberá tener dos propiedades, una lista de
javax.server.model.SelectItem que contendrá el contenido de la lista de países y otra
propiedad de tipo String que recoja el idioma seleccionado por el usuario, con sus
respectivos accesores para JSF.
Además, dichas propiedades deberán ser inicializadas en el constructor de la
clase, para que estén disponibles, por lo que en dicho constructor se debe realizar la
consulta a la base de datos. Se deben añadir las tablas de Paises, Provincias y
Municipios, así como se debe indicar a Spring las modificaciones necesarias para que
registre correctamente los nuevos DAO. Así, el constructor de la clase Seleccion_Bean
queda:
public Seleccion_Bean() {
lista_paises = new ArrayList<>();
List<Pais> lista = Acceso_ApplicationContext.getBean(
IGestion_Pais.class).consultar_Todos();
SelectItem opcion_nueva;
for(Pais pais: lista) {
opcion_nueva = new SelectItem();
opcion_nueva.setValue(pais.getCodigoPais().toString());
opcion_nueva.setLabel(pais.getPaisNombre());
lista_paises.add(opcion_nueva);
}
}

Con esto ya quedarían listados los paises para que el usuario escoja. Una vez que
el usuario haya seleccionado alguna selección, se deberán mostrar las provincias
asociadas a dicho país. Esto se realiza a partir de una petición via Ajax. El evento que se
va a tratar es un evento de cambio de valor, por lo que se deberá añadir la propiedad
valueChangeListener en el <h:selectOneMenu>.
JSF P á g i n a | 26

<h:selectOneMenu id="lista_paises"
value="#{seleccion_bean.pais_seleccionado}"
valueChangeListener="#{seleccion_bean.carga_Provincias}">

Para que el método carga_Provincias se ejecute, es necesario que se produzca


una petición. Para ello se utiliza la etiqueta <f:ajax>, a la que se indica el componente
que va a generar el evento (execute), que evento se produce (event) y qué parte de la
página se va a actualizar con la respuesta (render).
A la hora de indicar el execute se puede indicar el id (lista_paises), también
puede tener más de un componente asociado (separado por espacios). También dispone
de opciones en base a su posición dentro de la página, marcadas por un @:
• @this: actualiza el componente dentro del cuál está la etiqueta <f:ajax>
• @form: actualiza los componentes del formulario dentro del que se encuentre la
etiqueta <f:ajax>
• @all: actualiza toda la página

En el render se repintará el siguiente select, que se corresponde con el de


provincias.
<h:outputText id="eti_provincia" value="Provincias" />
<h:selectOneMenu id="lista_provincias"
value="#{seleccion_bean.provincia_seleccionada}"
valueChangeListener="#{seleccion_bean.cargar_Municipios}">
<f:selectItem id="sin_provincia" itemValue="0"
itemLabel="#{seleccion_bean.texto_provincia}" />
<f:selectItems id="modelo_provincias"
value="#{seleccion_bean.lista_provincias}" />
<f:ajax event="valueChange" execute="@this"
render="lista_municipios" />
</h:selectOneMenu>

Por tanto, la petición ajax del componente lista_paises queda:


<h:selectOneMenu id="lista_paises"
value="#{seleccion_bean.pais_seleccionado}"
valueChangeListener="#{seleccion_bean.carga_Provincias}">
<f:selectItem id="sin_seleccion" itemValue="0"
itemLabel="Seleccione un pais" />
<f:selectItems id="modelo_paises"
value="#{seleccion_bean.lista_paises}" />
<f:ajax event="valueChange" execute="@this"
render="lista_provincias" />
</h:selectOneMenu>

El método carga_Provincias queda:


public void carga_Provincias(ValueChangeEvent evento) {
// Se recibe el valor seleccionado por el evento
String pais_seleccionado = (String) evento.getNewValue();
SelectItem opcion = null;
lista_provincias.clear();
JSF P á g i n a | 27

lista_municipios.clear();
if (pais_seleccionado.equals("73")) {
// España - modificacion del texto inicial del combo
texto_provincia = "Seleccione una provincia";
// Consultamos la informacion a la fuente de datos
List<Provincias> lista =
Acceso_ApplicationContext.getBean(
IGestion_Provincias.class).consultar_Todas();
// Creamos dinamicamente el contenido de la lista
for (Provincias provincia : lista) {
opcion = new SelectItem();
opcion.setLabel(provincia.getProvincia());
opcion.setValue(provincia.getCodigoProvincia());
lista_provincias.add(opcion);
}
} else {
// Cualquier otro pais - reinicamos las listas de
provincias y
// municipios a valores iniciales
texto_provincia = "Seleccione un pais";
//texto_municipio = "Seleccione una provincia";
}
}

También será necesario cambiar el número de column del formulario


formu_listas de dos a cuatro, para que pueda contener el texto Provincias y su combo.
Se cambia también el contenido de los itemLabel por una expresión dinámica que
apunte al texto de la clase Selección_Bean.
<f:selectItem id="sin_provincia" itemValue="0"
itemLabel="#{seleccion_bean.texto_provincia}" />
JSF P á g i n a | 28

Conversiones y validaciones
Una conversión, como siempre, se puede realizar a la entrada (cuando llega la
petición del usuario), como en la salida (cuando se pinta la respuesta a dicha petición).
Por tanto, un conversor se le puede colocar a un componente de entrada <h:inputText>
como a uno de salida <h:outputText>.

Conversores
Los conversores pueden ser de dos tipos: previamente creados e incluidos en JSF
y conversores propios. Dentro de los conversores propios de JSF se encuentran:

ConverterDateTime
<f:converterDateTime>: sirve para convertir texto a objetos Date. No es otra
cosa que un SimpleDateFormat. Dentro de sus atributos se encuentra por ejemplo type,
que puede tomar los valores:
• Date: solamente admite datos de la fecha de
• Time: al contrario que date, solamente admite la parte horaria (horas, minutos,
segundos, milisegundos, etc.) de la fecha.
• Both: es una combinación de los anteriores, admitiendo cualquier componente de
la fecha.

Otro atributo es Locale, con el que se le indica el objeto Locale que va a definir
el formato de la fecha. La clase Locale está definida en el API I18N7 y se encarga de
controlar todo lo referente a información de fecha, procesos monetarios, etc. Alguno de
los datos que proporciona el Locale son el país y la zona horaria. Esto implica que si no
se le indica nada, el converterDateTime emplea los valores que contenga el Locale
definido en el I18N, por lo que será necesario indicar un Locale con formato propio para
poder mostrar la hora en el formato deseado.
Un tercer atributo de converterDateTime es el denominado pattern, que permite
definir un patrón personalizado para mostrar la hora y fecha en base a las letras que
proporciona la clase SimpleDateFormat. Así, a modo de ejemplo, MM significa el
número del mes, MMM es una abreviatura del mes y MMMM o más se corresponde con
el nombre del mes completo.

ConverterNumber

7
I18N hace referencia a Internationalization y el número 18 son las diferentes letras que se
emplean en la internacionalización.
JSF P á g i n a | 29

El componente <f:converterNumber> se encarga de realizar las conversiones


numéricas, correspondiéndose como un objeto de la clase NumberFormat. El atributo
type permite definir formatos numéricos, siendo sus valores posibles number (número
simple), percent8 (porcentaje) y currency (moneda, llevando asociado el valor del país,
€ para España).
Otras de las propiedades que tiene este conversor son maxIntegerDigit,
minIntegerDigit, maxFractionDigit y minFractionDigit, que permite definir los límites
máximo y mínimo tanto de los números enteros como de los double9, long, etc.
En cuanto a los conversores propios:

Conversor Propio
Para indicar un conversor propio, se debe hacer uso de la etiqueta
<f:converter>, incluyendo en el atributo converter-id el nombre del conversor, que
deberá haber sido definido en el fichero de configuración de JSF. Para definirlo en este
fichero, se emplea la etiqueta <converter>, a la que se le añade el identificador
<converter-id> y la clase asociada al mismo <converter-class>.
La clase asociada al conversor tiene que implementar la interfaz Converter, que
define dos métodos: uno para la conversión de entrada (getAsObject(FacesContext,
UIComponent,String)) y otro para la conversión de salida
(getAsString(FacesContext,UIComponent,String)).
Los componentes de JSF siguen los mismos patrones de diseño que los
componentes de Swing, en el que cada componente hijo va especializando el
componente. UIComponente es el padre común de todos los componentes de JSF y es, a
su vez, el componente del parámetro que se va a convertir.
Para gestionar las excepciones asociadas a la conversión errónea, se emplea la
sentencia throw, lanzando una excepción de la clase ConverterException, que reciber
por parámetro un FacesMessage con la información asociado al fallo en la conversión.

8
Percent tiene una característica importante, y es que al guardar un número escrito lo divide
entre 100 (100 --> 1) y al mostrarlo lo multiplica por 100 (1 --> 100), por lo que es importante tenerlo en
cuenta en los cálculos.
9
Los double, al ser coma flotante debe repartir los digitos antes y después de la coma, por eso se
denomina coma flotante.
JSF P á g i n a | 30

Validadores
Al igual que ocurre con los conversores, existen tanto validadores definidos
como la posibilidad de crear validadores propios. Los validadores solamente tienen
sentido a la entrada.

Validadores de rango
Algunos de los validadores definidos son validateLongRange,
validateDoubleRange, validateLength, que sirven para validar el rango de números
enteros, de números decimales y la longitud de una cadena de caracteres.
Éstos validadores tienen el atributo maximum/minimun, que permite definir el
número mínimo y máximo en los dos primeros y el mínimo y máximo número de
caracteres en el tercero.

Validador de campo vacío


Otro validador es el <f:validateRequired>, que permite exigir una validación,
siempre y cuando se le haya indicado a JSF que valide los campos vacíos. Para hacer
esto, en los componentes existe un atributo denominado required que permite obligar a
que el campo tenga un contenido, así como se puede indicar en el fichero de
configuración faces-config.xml:
<!-- EN VERSION JSF 2.2 POR DEFECTO FALSE -->
<context-param>
<description>ACTIVA LA VALIDACION DE CAMPOS VACIOS</description>
<param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
<param-value>true</param-value>
</context-param>

Validador mediante expresiones regulares


Otro validador es el <f:validateRegex>, que sirven para realizar validaciones a
partir de expresiones regulares. Este validador sólo está disponible en la versión 2 de
JSF. Contiene el atributo pattern, donde se incluye la expresión regular.

Validador Personalizado
Al igual que ocurre con los conversores, se puede crear un validador
personalizado, en este caso mediante la etiqueta <validator>, que también tendrá un
atributo validator-id para asociarlo con el validador definido en el fichero de
configuración de JSF.
Para incluir el validador en el fichero faces-config.xml se emplea la etiqueta
<validator>, que a su vez hace uso de <validator-id> para indicar el nombre del
JSF P á g i n a | 31

validador y <validator-class> para indicar la clase asociada al mismo. Esta clase


implementa la interfaz Validator y debe sobrescribir el método
validate(FacesContext,UIComponent,Object), dado que la validación sólo se realiza en
la entrada, como se comentó anteriormente.
La gestión de errores de validación se realiza lanzando mediante la sentencia
throw una excepción de la clase ValidateException, que también recibe como parámetro
un mensaje de la clase FacesMessage con la información del error de validación que se
ha producido.

Ejemplo de validación y conversión


En primer lugar, se crea una nueva página XHTML denominada
conversion_validacion.xhtml.
<h:body>
<!-- GESTION IDIOMATICA -->
<f:loadBundle basename="#{sessionScope.idioma_seleccionado}"
var="idioma" />
<!-- FORMULARIO DE PRUEBAS -->
<h:form id="formu_conversionvalidacion">
<h:panelGrid id="panel_conversionvalidacion" columns="3">
<h:outputText id="eti_nombre"
value="#{idioma['conversion.nombre.eti']}" />
<!-- VALIDACION DE CAMPO OBLIGATORIO -->
<h:inputText id="nombre_usuario"
value="#{usuario_bean.nombre_usuario}"
required="true"
requiredMessage="El nombre es obligatorio" />
<h:message for="nombre_usuario" />
<h:outputText id="eti_clave"
value="#{idioma['conversion.clave.eti']}" />
<!-- VALIDACION DE CAMPO OBLIGATORIO Y DE TAMAÑO --
>
<h:inputSecret id="clave_usuario" redisplay="true"
value="#{usuario_bean.clave_usuario}"
validatorMessage="Clave obligatoria, minimo 5
caracteres maximo 10.">
<f:validateRequired />
<f:validateLength maximum="10" minimum="5" />
</h:inputSecret>
<h:message for="clave_usuario" />
<h:outputText id="eti_carpeta"
value="#{idioma['conversion.numero']}" />
<!-- VALIDACION/CONVERSION NUMERICA -->
<h:inputText id="numero_usuario"
value="#{usuario_bean.numero}"
validatorMessage="Numero muy grande">
<!-- TYPE ES OBLIGATORIO PARA EL CORRECTO
FUNCIONAMIENTO EN ESTA VERSION -->
<f:convertNumber type="number"
maxIntegerDigits="4" pattern="#,##0" />
<f:validateLongRange maximum="8888" />
JSF P á g i n a | 32

</h:inputText>
<h:message for="numero_usuario" />
<h:outputText id="eti_fechaalta"
value="#{idioma['conversion.fechaalta.eti']}"
/>
<!-- CONVERSION A FECHA -->
<h:inputText id="fecha_alta"
value="#{usuario_bean.fecha_alta}"
converterMessage="#{idioma
['conversion.error.conversion.fecha']}">
<f:convertDateTime type="date"
pattern="dd/MM/yyyy" />
</h:inputText>
<h:message for="fecha_alta" />
<h:outputText id="eti_fechabaja"
value="#{idioma['conversion.fechabaja.eti']}"
/>
<!-- CONVERSION A FECHA -->
<!-- VALIDAMOS LAS FECHAS ENTRE SI,VALIDADOR PERSONALIZADO
-->
<h:inputText id="fecha_baja"
value="#{usuario_bean.fecha_baja}"
converterMessage="#{idioma
['conversion.error.conversion.fecha']}">
<f:convertDateTime type="date"
pattern="dd/MM/yyyy" />
<f:validator
validatorId="validar_rangofechas" />
</h:inputText>
<h:message for="fecha_baja" />
<h:outputText id="eti_email" value="Email" />
<!-- VALIDACION CON EXPRESION REGULAR-->
<h:inputText id="email"
value="#{usuario_bean.email}"
required="false" validatorMessage="No es un
formato correo valido">
<f:validateRegex
pattern="(^[_a-z0-9-]+(\.[_a-z0-9-
]+)*@[a-z0- 9-]+(\.[a-z0-9-]+)*(\.[a-
z]{2,3})$)*" />
</h:inputText>
<h:message for="email" />
</h:panelGrid>
<!-- BOTON DE SUMIT PARA REALIZAR LAS CONVERSIONES/VALIDACIONES
-->
<h:commandButton id="boton"
value="#{idioma['conversion.botonconfirmar.eti']}"
actionListener="#{usuario_bean.boton_Evento}" />
</h:form>
</h:body>

En esta página se hace uso de varios validadores, tanto de fecha como de


número, así como un validador personalizado y una validación mediante expresión
regular. Se emplea un campo <h:inputSecret> para introducir la contraseña de forma
que no se muestre lo que se escribe, con el atributo redisplay a true para que el usuario
JSF P á g i n a | 33

vea los caracteres (codificados) que escribe. De otra forma, aunque escriba no tendría
un feedback de que está introduciendo caracteres (como ocurre en sistemas de Linux).
También se ha declarado un nuevo managedbean, denominado usuario_bean
que deberá ser creado para poder hacer uso de él. Esta clase simplemente debe tener las
propiedades nombre_usuario, clave_usuario e idioma (todas String), las propiedades
fecha_alta y fecha_baja (de tipo Date) y la propiedad numero_usuario (Integer), así
como sus respectivos accesores para JSF y un método para gestionar el evento (cuyo
contenido simplemente es un sysout).
Para declararlo se añade en el fichero faces-config.xml lo siguiente:
<!-- CONVERSIONES Y VALIDACIONES -->
<managed-bean>
<description>Proceso de administracion de usuarios</description>
<managed-bean-name>usuario_bean</managed-bean-name>
<managed-bean-
class>com.atrium.managedbean.Usuario_Bean</managed-bean- class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Así mismo, se debe crear el validador personalizado, declarándolo en primer


lugar también en el fichero de configuración de JSF.
<validator>
<description>Comprobacion de que la segunda fecha sea mayor o
igual a la primera</description>
<validator-id>validar_rangofechas</validator-id>
<validator-
class>com.atrium.validadores.Validar_RangoFechas</validator-class>
</validator>

La clase asociada a dicho validador (Validar_RangoFechas) deberá implementar


la interfaz javax.faces.validator.Validator y sobrescribir el método validate, tal y como
se muestra a continuación.
public void validate(FacesContext contexto, UIComponent componente,
Object objeto_avalidar) throws ValidatorException {
// Segunda fecha para la comprobacion
Date fecha_baja = (Date) objeto_avalidar;
// Se accede al otro componente para obtener la otra fecha
necesaria
// para la validación
UIComponent componente_fechaalta = contexto.getViewRoot()

.findComponent("formu_conversionvalidacion:fecha_alta");
// Se saca el objeto date del componente
Date fecha_alta = (Date) ((HtmlInputText) componente_fechaalta)
.getValue();
// Se comparan las fechas
if (fecha_alta != null && fecha_baja != null
&& fecha_alta.after(fecha_baja)) {
JSF P á g i n a | 34

throw new ValidatorException(new FacesMessage(


"La segunda fecha debe ser posterior a la
primera"));
}
}

En primer lugar se obtiene el componente de la vista que contiene la información


de la fecha de alta para poder compararla con la fecha de baja obtenida por parámetro.
Para ello, se hace uso del método getViewRoot del contexto de JSF y, a partir de este, se
busca el componente por su identificador (id_formulario:id_componente), obteniéndolo
casteado a la clase padre genérica (UIComponent), por lo que será necesario volver a
castearlo a su tipo para poder obtener su valor:
// Se accede al otro componente para obtener la otra fecha necesaria
// para la validación
UIComponent componente_fechaalta = contexto.getViewRoot()
.findComponent("formu_conversionvalidacion:fecha_alta");
// Se saca el objeto date del componente
Date fecha_alta = (Date) ((HtmlInputText)
componente_fechaalta).getValue();
JSF P á g i n a | 35

Librerías de componentes adicionales (IceFaces)


Como se comentó anteriormente, para ampliar las posibilidades de JSF se hace
uso de librerías de componentes externas, dentro de las cuales se destaca IceSoft, cuya
página web es http://www.icesoft.org. Dicha página está completamente mostrada sobre
JSF, permitiendo evitar el uso de Javascript y otras tecnologías web a la hora de realizar
el diseño de la página. Un ejemplo de aplicación que hace uso de IceFaces es
MyEclipse.
Todos los componentes que ofrece la librería desarrollada por IceSoft actualizan
el cliente vía Ajax. En otras palabras, facilitan el control de la parte del cliente, llegando
al punto de evitar la navegación, simplemente recargando distintas partes de la página a
partir de peticiones Ajax. Estos componentes son capaces de generar salidas tanto para
dispositivos Android e iOS10 (componentes multiplataforma), a excepción de la
modificación necesaria de los estilos CSS para poder realizar diseños responsive.
Permite emplear la tecnología Cloud Push (Ajax push), que no es otra cosa que
eventos de Java producidos contra el servidor. Para entender esto mejor, se plantea el
siguiente caso:
Se tiene el aplicativo montado en el servidor y hay una serie de clientes
conectados al mismo. El Cloud Push o Ajax Push consiste en que cuando un cliente
genera un evento contra el servidor, el servidor actualiza a todos los clientes con la
respuesta a dicho evento. Un claro ejemplo de esto es una subasta, donde al enviar una
puja un cliente, se refresca la vista de todo el resto de clientes para que puedan disponer
de la última información actualizada.
La implementación que realiza de los componentes en el cliente es una
implementación estándar, ya que están basados en Jquery11, lo cual proporciona una
garantía de estabilidad debido a que la mayor parte de los navegadores responden muy
bien a esta tecnología.
IceSoft proporciona la sección IceFaces Showcase, en la página http://icefaces-
showcase.icesoft.org/ donde se puede ver el funcionamiento de cada uno de los
componentes que se ofrecen, así como el código para el funcionamiento.

10
Están establecidos dentro de los componentes denominados MOBI
11
Una de las ventajas de JQuery, además de la compatibilidad, es que tiene una muy buena
documentación.
JSF P á g i n a | 36

Uno de los componentes más interesantes (que también está en JSF aunque
menos extenso) es el DataTable, que es un componente de rejilla, tal y como se muestra
en la Fig. 2.

Fig. 2. Ejemplo de componente DataTable

Como se puede observar, a diferencia de JSF, IceFaces proporciona opciones de


filtrado para este componente, así como un sistema de paginación ya implementado.
Esto es un claro ejemplo de la reducción de trabajo que proporcionan las librerías
externas a JSF. Otro recurso bastante empleado son los Efectos, dentro de la categoría
Miscellaneous.
Para poder ver el funcionamiento y la gestión de estos componentes, se crea un
nuevo proyecto web denominado j615-jfs-aceejemplos. A diferencia de otras veces, se
añaden los Project Facets de IceFaces, que ya incluyen los de JSF. Se selecciona la
versión de IceFaces 3.3.0, que se corresponde con la versión 2.1 de JSF.
En el fichero web.xml se produce un error debido al asistente, dado que incluye
código para la versión web 3 (java EE 6.0). Por lo tanto, la etiqueta <multipart-config>
no existe en Java JEE 5.0. Para solucionarlo, simplemente se elimina dicha etiqueta.
Además de las etiquetas habituales en JSF, el asistente añade otras correspondientes a
IceFaces. Por ejemplo, un controlador específico para éste:
<!-- CONTROLADOR ESPECIFICO PARA ICEFACES -->
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-
class>com.icesoft.faces.webapp.CompatResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/xmlhttp/*</url-pattern>
</servlet-mapping>

Este servlet es el encargado de procesar todas las peticiones de recursos


estáticos, como por ejemplo la descarga de las librerías de javascript pertinentes a la
hora de realizar efectos visuales.
JSF P á g i n a | 37

También añade parámetros de contexto, como por ejemplo:


<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
Este parámetro indica que toda la gestión de almacenamiento de estados (sesion,
vista, etc.) la va a realizar el servidor. Otros parámetros son:
<!-- CONFIGURACION INTERNA DE ICEFACES -->
<context-param>
<param-name>org.icefaces.coalesceResources</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>org.icefaces.strictSessionTimeout</param-name>
<param-value>true</param-value>
</context-param>
<!-- SOLO NECESARIOS SI SE VA A USAR EL COMPONENTE DE GOOGLEMAP -->
<context-param>
<param-name>com.icesoft.faces.gmapKey</param-name>
<param-value>AIzaSyAcAbAa7AL1DLU0785OeWn2byf4XOsm7KM</param-
value>
</context-param>
<context-param>
<param-name>org.icefaces.ace.gmapKey</param-name>
<param-value>AIzaSyAATyWVqT2qNusNGmcVTyQ0QmymkpU-B5o</param-
value>
</context-param>

• strictSessionTimeout: sirve para indicar a IceFaces que controle de forma estricta


la duración de las sesiones.
• coalesceResources: si es verdadero, hace una gestión de recursos por paquetes, es
decir, que el ResourceServlet en lugar de enviar los recursos de uno en uno, los
envía empaquetados, de forma que se reducen tiempos de carga en el cliente,
mientras que se amplían en el servidor.
• gmapKey: permite hacer uso de Google Maps dentro de la aplicación. Esto es
necesario debido a que ésta tecnología es de pago y requiere una licencia, siendo
el param-value la clave de IceSoft para poder hacer uso de los mapas de google.

Componentes
Se crea una nueva carpeta dentro del proyecto denominada xhtml, donde se
guardan las páginas a emplear, que se crean a partir de los XHTML Advanced
Templates, pero esta vez seleccionando como plantilla la de IceFaces 3 y nombrándola
como ejemplo_contenedores.xhtml.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
JSF P á g i n a | 38

xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:icecore="http://www.icefaces.org/icefaces/core"
xmlns:ace="http://www.icefaces.org/icefaces/components"
xmlns:ice="http://www.icesoft.com/icefaces/component">
<h:head>
<title>ICEfaces</title>
</h:head>
<h:body>
</h:body>
</html>

Panel
El primer componente que se añade, dentro de un formulario, es el ace:panel,
que es un contenedor que ofrece una funcionalidad extendida sobre el PanelGrid de
JSF, permitiendo entre otras opciones ocultar el panel, incluir título en la barra de título,
minimizarlo, etc.
<ace:panel id="prueba_panel" header="Titulo ventana"
styleClass="capa_ventana" footer="pie ventana"
closable="true"
toggleable="true" toggleSpeed="250">

Los atributos son:


• header: título a mostrar en la barra de título, valga la redundancia
• closable: permitir el cierre de la ventana
• toggleable: permite minimizar la ventana
• toggleSpeed: permite cambiar la velocidad a la que se produce el efecto de
minimizado y maximizado (en ms)
• styleClass: elemento class de la hoja de estilos para definir un estilo propio para
el panel
• footer: añade un pie al panel

Como en JSF, cualquier propiedad puede tener un valor fijo o un valor variable,
asignado mediante una expresión dinámica. A continuación se le añade un outputText,
para que el panel tenga un contenido. Otra característica de los componentes de
IceFaces es que tienen eventos, dos en el caso de acePanel:
<h:outputText id="eti_prueba" value="Hola, texto para prueba de panel"
/>
<!-- EVENTOS DEL ACE:PANEL -->
<ace:ajax event="close"
listener="#{contenedor_bean.evento_CierrePanel}" render="@all"
/>
<ace:ajax event="toggle"
listener="#{contenedor_bean.evento_RepliegePanel}" render="@all"
/>
JSF P á g i n a | 39

Estos eventos se corresponden con las acciones por parte del usuario del cierre
de la ventana (evento close) o bien el repliegue de la ventana (evento toggle). Como se
puede observar, se hace uso de un managedBean denominado contenedor_bean, por lo
que es necesario declararlo en el faces-config.xml.
<managed-bean>
<managed-bean-name>contenedor_bean</managed-bean-name>
<managed-bean-
class>com.atrium.managedbean.Contenedores_Bean</managed- bean-class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Así como se debe crear la clase asociada al mismo, denominada


Contenedores_Bean, que deberá implementar la interfaz Serializable y los dos métodos
declarados.
public class Contenedores_Bean implements Serializable {

public void evento_CierrePanel(CloseEvent evento) {


System.out.println("Soy el evento de cierre de ventana");
}

public void evento_ReplieguePanel(ToggleEvent evento) {


System.out.println("Soy el evento de repliegue de
ventana");
}
}

TabSet
A continuación se añade un nuevo panel contenedor, un panel de pestañas. Este
es un contenedor de contenedores y se define mediante la etiqueta <ace:tabSet>,
mientras que cada una de las pestañas viene definida por <ace:tabPane>.
<ace:tabSet id="prueba_panelpestañas" clientSide="true"
showEffect="slide" showEffectLength="1500"

tabChangeListener="#{contenedor_bean.evento_CambioPestaña}">
<ace:tabPane id="p1" label="Pestaña 1">
<h:outputText id="eti_p1" value="Contenido p1" />
</ace:tabPane>
<ace:tabPane id="p2" label="Pestaña 2">
<h:outputText id="eti_p2" value="Contenido p2" />
</ace:tabPane>
<!-- DEFINICION DEL EVENTO CUANDO SE TRABAJA EN EL CLIENTE -->
<ace:ajax event="clientSideTabChange" execute="@this"
listener="#{contenedor_bean.evento_CambioPestañaCliente}"
/>
</ace:tabSet>
JSF P á g i n a | 40

A diferencia del componente anterior, en éste si se puede modificar el efecto


aplicado mediante el atributo showEffect. Lleva un evento asociado que se corresponde
con el cambio de pestaña, recogido por el tabChangeListener.
Este tipo de panel tiene un atributo denominado clientSide, que puede tomar los
valores true o false. Si toma el valor true, cuando se procesa el contenido de todos los
tabPanel, toda la información de todos los paneles se manda al cliente, lo que implica
que el cambio de pestaña es inmediato. En este caso, dado que el cliente tiene toda la
información, ya no se producen eventos debido a que el cliente no necesita pedir nada al
servidor, por lo tanto al cambiar de pestaña no se realiza ninguna petición al servidor.
Para que el servidor reciba una petición por parte del cliente de forma inmediata y
pueda conocer que se ha producido dicho cambio de pestaña se emplea un evento que
realiza una petición vía Ajax (por tanto, el atributo tabChangeListener no tiene efecto).
Sin embargo, si se asigna un valor false, sólo se manda la información del
tabPanel activo, por lo que cuando el cliente cambia de pestaña se hace una petición
contra el servidor, se procesa la información del tabPanel pedido, se manda al
navegador y se actualiza la página con dicha información. El cambio de pestaña es más
lento, dado que el cliente debe esperar a que el servidor procese la petición, pero reduce
el coste de recursos dentro del propio servidor. En este caso, como sí se produce una
petición al cambiar de pestaña, no es necesario incluir el evento Ajax.
El evento producido por el componente es previo a la fase 5, lo que implica que
el managed bean no tendrá sus valores actualizados. A efectos prácticos, cuando se
produce una petición vía Ajax, el cliente ve el cambio de pestaña antes de que se
produzca dicha petición. Sin embargo, en el caso de que clientSide esté a false y por lo
tanto se produzca el evento TagChangeEvent, hasta que no se reciba la respuesta del
servidor no se actualiza el contenido que visualiza el cliente. Esto se puede comprobar
fácilmente empleando el debug. El código asociado a los eventos en el managed bean es
el siguiente:
public void evento_CambioPestañas(TabChangeEvent evento) {
System.out.println("Soy el evento de cambio de pestaña");
}

public void evento_CambioPestañaCliente(AjaxBehaviorEvent evento) {


System.out.println("Soy el evento de cambio de pestaña en el
cliente");
}
JSF P á g i n a | 41

A efectos de una visualización más elegante, se añade dentro de la carpeta raíz la


ruta resources/css/posiciones.css

Acordeón
Este componente es una variante del tabSet, donde en función de la pestaña
escogida se extiende su contenido, siempre en columna (es especialmente utilizado en
aplicaciones móviles).
Para definirlo se hace uso de la etiqueta <ace:accordion>, incluyendo los
paneles internos mediante <ace:accordionPane>.
<ace:accordion id="prueba_acordeon"
paneChangeListener="#{contenedor_bean.evento_CambioAcordeon}">
<ace:accordionPane id="ac1" title="Acordeon 1">
<h:outputText id="eti_ac1" value="Contenido acordeon 1" />
</ace:accordionPane>
<ace:accordionPane id="ac2" title="Acordeon 2">
<h:outputText id="eti_ac2" value="Contenido acordeon 2" />
</ace:accordionPane>
</ace:accordion>

En este caso, a diferencia del tabSet, no se dispone la opción de clientSide. Esto


implica que todo el proceso lo gestiona el propio componente, generando un evento de
tipo AccordionPaneChangeEvent, tal y como se muestra a continuación en el código del
managed bean.
public void evento_CambioAcordeon (AccordionPaneChangeEvent evento) {
System.out.println("Soy el evento de cambio en el acordeón");
}

AceDialog
Este componente, de cara a lo que visualiza el cliente, es parecido al componente
Panel, con la salvedad de que puede comportarse como una ventana modal12. Por lo
tanto, es habitualmente empleada para mostrar mensajes de lectura obligada.
<ace:dialog id="prueba_ventanamodal" header="Ventana modal"
hideEffect="explode" showEffect="explode" modal="true"
position="center" visible="true" draggable="true">
<h:outputText id="modal" value="Contenido ventana mensajes" />
</ace:dialog>

Permite tener dos efectos diferentes, uno para el momento en el que se muestra y
otro para el momento en el que se oculta. Mediante la opción modal se convierte en una
ventana modal, con la opción draggable se permite al usuario arrastrar la ventana por la

12
Una ventana modal es una ventana emergente que bloquea el contenido de fondo mientras no
se cierre.
JSF P á g i n a | 42

pantalla, el atributo resizable permite al usuario cambiar el tamaño de forma dinámica.


También tiene parámetros para determinar el tamaño máximo y mínimo de la ventana.
Cuando se cierra la ventana empleando la X de cierre, se cambia la propiedad
visible a false en el cliente, por lo que el servidor no tiene constancia del cambio
producido. Para solucionarlo, se hace uso de una expresión dinámica que relacione el
valor del atributo visible con la propiedad del managed bean que lo va a gestionar, así
como se declara la petición Ajax que se mandará en el cierre.
<ace:dialog id="prueba_ventanamodal" header="Ventana modal"
hideEffect="explode" showEffect="explode" modal="true"
position="center" visible="#{contenedor_bean.dialogo_visible}"
draggable="true">
<h:outputText id="modal" value="Contenido ventana mensajes" />
<ace:ajax event="close"
listener="#{contenedor_bean.evento_CierreDialogo}" render="@all"
/>
</ace:dialog>

Por lo tanto, en el managed bean representado por la clase Contenedor_Bean


será necesario incluir una propiedad de tipo boolean denominada dialogo_visible que en
el constructor de la clase deberá ser inicializada a true. También será necesario añadir el
correspondiente conjunto de métodos accesores de dicha propiedad, así como el método
evento_CierreDialogo, que será el que cambie la propiedad de la propiedad
dialogo_visible a false. El código añadido a la clase se muestra a continuación:
public class Contenedores_Bean implements Serializable {

private boolean dialogo_visible;

public Contenedores_Bean() {
dialogo_visible = true;
}

.
.
.
//Evento de cierre de ventana modal
public void evento_CierreDialogo(CloseEvent evento) {
System.out.println("Soy el evento de cierre de la ventana
modal");
dialogo_visible = false;
}

//Accesores para los contendores de ace


public boolean isDialogo_visible() {
return dialogo_visible;
JSF P á g i n a | 43

public void setDialogo_visible(boolean dialogo_visible) {


this.dialogo_visible = dialogo_visible;
}

NotificationPanel
Un panel de notificación es un panel emergente que, a diferencia del dialog, no
es movible y no es modal. A nivel de posicionamiento sólo puede estar situado en la
parte superior o inferior de la página. Se suele emplear para mostrar publicidad, aunque
como todo contenedor, puede ser empleado para mostrar cualquier tipo de contenido.
Como ocurre en el dialog, se puede indicar el tipo de efecto y la duración del mismo. Se
declara mediante la etiqueta <ace:notificationPanel>.
<ace:notificationPanel id="prueba_panelnotificaciones" visible="true"
position="bottom" effect="slide" effectSpeed="slow">
<h:outputText id="notificacion" value="Contenido panel
noticiacion."
title="texto tooltip texto panel notificacion" />
<!-- SUBCOMPONENTE DE CIERRE -->
<f:facet name="close">
<div class="ui-icon ui-icon-closethick"></div>
</f:facet>
<!-- EVENTOS DEL NOTIFICATION PANEL -->
<ace:ajax event="close" execute="@this" render="@form"

listener="#{contenedor_bean.evento_CerrarPanelNotificacion}" />
<ace:ajax event="display" execute="@this" render="@form"

listener="#{contenedor_bean.evento_MostrarPanelNotificacion}" />
</ace:notificationPanel>

Este componente tiene una particularidad, y es que por defecto no muestra el


botón de cierre. Como se ha comentado anteriormente, todos los componentes de JSF
heredan de UIComponent.
Esta clase define dos propiedades de tipo Map. Una de ellas es la propiedad
attributes, que permite cargar el componente con tantos elementos como se desee. La
otra colección es denominada facets. Un facet es una especie de subcomponente del
componente, con la propiedad de ser tratados de forma automática por el componente
(opcionalmente). El hecho de que el tratamiento de los facets sean tratados o no
automáticamente depende de que el nombre del facet (la clave del mapa) sea un nombre
conocido por el componente. En caso de no serlo, se debe hacer un tratamiento propio.
Por lo tanto, si se quiere añadir un botón de cierre al componente
notificationPanel cuyo nombre sea close. Para ello se hace uso de la etiqueta <f:facet>.
JSF P á g i n a | 44

<f:facet name="close">
<div class="ui-icon ui-icon-closethick"></div>
</f:facet>

Además, se declara en el atributo class el estilo (de IceFaces) para que el botón
tenga la forma de un botón de cierre. En cuanto al tratamiento de eventos, se tiene el
mismo problema que en casos anteriores, ya que el cierre del panel se produce en el
cliente. Por tanto, será necesario realizar una petición Ajax para actualizar el parámetro
en el servidor, tanto en el caso de que se cierre como en el caso de que se muestre.
En el managed bean será necesario crear una propiedad boolean denominada
notification_visible que en el constructor se inicialice a true, con sus respectivos
métodos accesores. También será necesario sustituir el atributo visible por una
expresión dinámica que tome el valor de la propiedad anteriormente mencionada, tal y
como se muestra a continuación.
<ace:notificationPanel id="prueba_panelnotificaciones"
visible="#{contenedor_bean.notification_visible}"
position="bottom"
effect="slide" effectSpeed="slow">
<h:outputText id="notificacion" value="Contenido panel
noticiacion."
title="texto tooltip texto panel notificacion" />
<!-- SUBCOMPONENTE DE CIERRE -->
<f:facet name="close">
<div class="ui-icon ui-icon-closethick"></div>
</f:facet>
<!-- EVENTOS DEL NOTIFICATION PANEL -->
<ace:ajax event="close" execute="@this" render="@form"

listener="#{contenedor_bean.evento_CerrarPanelNotificacion}" />
<ace:ajax event="display" execute="@this" render="@form"

listener="#{contenedor_bean.evento_MostrarPanelNotificacion}" />
</ace:notificationPanel>

Se deberán crear los métodos para atender a los eventos de cerrar el panel
(evento_CerrarPanelNotificacion) y de abrirlo (evento_MostrarPanelNotificacion).

Tooltip
Este componente permite que cuando se mantenga el ratón encima de un
componente durante un periodo de tiempo (por defecto dos segundos), se muestra un
panel superpuesto sobre el componente con el contenido deseado (por ejemplo un texto
de ayuda sobre el uso del componente). Para declararlo se emplea la etiqueta
<ace:tooltip>.
<ace:tooltip id="prueba_tooltip" for="eti_p1"
JSF P á g i n a | 45

speechBubble="true" position="topLeft">
<h:outputText id="texto_tooltip" value="Texto prueba tooltip" />
<!-- EVENTO DEL TOOLTIP -->
<ace:ajax event="display"
listener="#{contenedor_bean.evento_MostrarToolTip}" />
</ace:tooltip>

Mediante el atributo for se define el componente al que está asociado, en este


caso eti_p1 que se corresponde con el outputText de la primera pestaña del panel de
pestañas. A partir del atributo showEvent se puede definir cuando se muestra el tooltip,
generalmente al pasar el ratón por encima.
El evento display se produce al mostrar el tooltip, por lo tanto, el managed bean
deberá tener delclarado el método evento_MostrarToolTip.

Menús
Para mostrar un ejemplo de los menús que proporciona IceFaces, se crea una
nueva página XHTML denominada ejemplo_menus.xhtml, incluyendo dentro de ésta un
formulario que será el que contenga los componentes de menú.
Los menús siempre están formados por dos partes: el contenedor que contiene la
estructura de los elementos y los propios elementos que son seleccionables por el
usuario. En otras palabras, existe un menú y un submenú.

Menu y MenuBar
El menú es un organizador en vertical, que se define mediante la etiqueta
<ace:menu>, siendo por contraposición el <ace:menuBar> un menú que se muestra en
horizontal. Para el caso del vertical, se muestra a modo de ejemplo el siguiente código:
<ace:menu id="menu_vertical">
<ace:submenu id="sub1" label="Submenu 1">
<ace:menuItem id="op11" value="Opcion 11"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op12" value="Opcion 12"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:submenu>
<ace:submenu id="sub2" label="Submenu 2">
<ace:menuItem id="op21" value="Opcion 21"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op22" value="Opcion 22"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
JSF P á g i n a | 46

<ace:menuItem id="op23" value="Opcion 23"


actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:submenu>
</ace:menu>

Y para el caso del menú horizontal:


<ace:menuBar id="menu_horizontal" autoSubmenuDisplay="true">
<ace:submenu id="sub1h" label="Submenu 1">
<ace:menuItem id="op11h" value="Opcion 11"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op12h" value="Opcion 12"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:submenu>
<ace:submenu id="sub2h" label="Submenu 2">
<ace:menuItem id="op21h" value="Opcion 21"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op22h" value="Opcion 22"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op23h" value="Opcion 23"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:submenu>
</ace:menuBar>

El contenido de los menús puede tener dos variantes, no excluyentes. Una


opción fija o estática donde el contenido del menú es el mismo y una opción dinámica
en la que el contenido puede variar dinámicamente (por ejemplo, un menú en función
del usuario). Dentro del menú puede haber menuItem, que son elementos directamente
seleccionables, o subMenu, que despliega una lista de menuItem.
El tratamiento de los eventos es exactamente igual a Swing, de forma que
solamente los menuItem producen eventos y estos eventos serán de tipo ActionEvent13.
Para que el servidor conozca de inmediato que se ha seleccionado una opción del menú
se incluye una petición <ace:ajax>, con el campo event con valor activate, que será
común para todos los menuItem. Esto es así porque a partir del propio evento se puede
conocer qué objeto/componente lo ha generado (a partir de su id).

13
El ActionEvent es de tipo javax.faces.event.ActionEvent, no se debe confundir on el de swing.
JSF P á g i n a | 47

Será necesario crear un managed bean denominado prueba_menu dentro del


fichero de configuración de JSF, así como se creará la clase asociada (Prueba_Menu).
<managed-bean>
<managed-bean-name>prueba_menu</managed-bean-name>
<managed-bean-class>com.atrium.managedbean.Menus_Bean</managed-
bean- class>
<managed-bean-scope>view</managed-bean-scope>
</managed-bean>

En la clase Prueba_Menu se debe codificar el método evento_Menu, que


quedará tal y como se muestra a continuación.
public void evento_Menu(ActionEvent evento) {
String opcion_seleccionada = evento.getComponent().getId();
System.out.println("La opcion pulsada es...
"+opcion_seleccionada);
}

Menú estatico horizontal con multicolumnas


Una variante del menú horizontal es el menú estático con multicolumnas. Se
define a partir de la etiqueta <ace:menu>, con la salvedad de que el submenú emplea la
etiqueta <ace:multiColumnSubMenu>.
Para definir las columnas se hace uso de <ace:menuColum>, que contendrá
elementos subMenu y estos a su vez elementos menuItem. A continuación se muestra el
código de ejemplo de un menú multicolumna.
<ace:menuBar id="menu_horizontalmc" autoSubmenuDisplay="true">
<!-- OPCION DE MENU CON MULTIPLES COLUMNAS -->
<ace:multiColumnSubmenu id="columna_multiple"
label="Menu multicolumnas">
<ace:menuColumn id="primera_columna">
<ace:submenu id="sub1mc" label="Submenu 1">
<ace:menuItem id="op11mc" value="Opcion 11"

actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op12mc" value="Opcion 12"

actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:submenu>
</ace:menuColumn>
<ace:menuColumn id="segunda_columna">
<ace:submenu id="sub2mc" label="Submenu 2">
<ace:menuItem id="op21mc" value="Opcion 21"

actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
JSF P á g i n a | 48

<ace:menuItem id="op22mc" value="Opcion 22"

actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op23mc" value="Opcion 23"

actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:submenu>
</ace:menuColumn>
</ace:multiColumnSubmenu>
</ace:menuBar>

Como siempre, se hace uso de las peticiones AJAX para informar al servidor de
las opciones seleccionadas. Al igual que ocurre con los otros menús, dispone de una
propiedad denominada autoSubMenuDisplay que permite que los submenús se muestren
de forma automática al pasar el ratón por encima.

MenuButton
Es un menú muy parecido a los desplegables, con la salvedad de que para que se
muestre (si se emplea un submenú) se debe hacer click con el ratón encima del botón.
En el caso de no emplear submenús, al pinchar en el botón se genera un evento también
de tipo ActionEvent.
<ace:menuButton id="menu_boton" value="Menu boton">
<ace:menuItem id="op21mb" value="Opcion 21"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op22mb" value="Opcion 22"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
<ace:menuItem id="op23mb" value="Opcion 23"
actionListener="#{prueba_menu.evento_Menu}">
<ace:ajax event="activate" />
</ace:menuItem>
</ace:menuButton>

De nuevo, para informar al servidor de forma inmediata de que se ha producido


una selección en el menú se hace uso de peticiones Ajax para disparar eventos con el
atributo event asignado al valor activate.
ANEXO I: Componentes Web de JSF
<h:body>
<h:form id="formu_componentes">
<!-- COMPONENTES DE TEXTO -->
<h:outputText id="" value="texto a mostrar" />
<h:inputText />
<h:inputSecret />
<h:inputTextarea />
<h:inputHidden />
<!-- BOTONES -->
<h:commandButton />
<!-- HIPERVINCULOS -->
<h:link />
<!-- IMAGENES -->
<h:graphicImage />
<!-- SUBIDA DE FICHEROS -->
<h:inputFile />
</h:form>

<h:form id="formu_seleccion">
<h:panelGrid columns="4">
<!-- COMPONENTES DE SELECCION UNICA -->
<h:selectBooleanCheckbox>
<!-- SE MUESTRA UN UNICO CHECKBOX A ELEGIR --
>
</h:selectBooleanCheckbox>
<h:selectOneListbox>
<!-- SE MUESTRAN MULTIPLES OPCIONES -->
<!-- CONTENIDO ESTATICO -->
<f:selectItem />
<!-- CONTENIDO DINAMICO -->
<f:selectItems />
</h:selectOneListbox>
<h:selectOneRadio>
<!-- SE MUESTRAN MULTIPLES RADIO BUTTONS -->
<!-- CONTENIDO ESTATICO -->
<f:selectItem />
<!-- CONTENIDO DINAMICO -->
<f:selectItems />
</h:selectOneRadio>
<h:selectOneMenu>
<!-- SE MUESTRA EL COMBO -->
<!-- CONTENIDO ESTATICO -->
<f:selectItem />
<!-- CONTENIDO DINAMICO -->
<f:selectItems />
</h:selectOneMenu>
</h:panelGrid>
<h:panelGrid columns="3">
<!-- COMPONENTES DE SELECCION MULTIPLE -->
<h:selectManyMenu>
<!-- SE MUESTRA UN COMBO -->
<!-- CONTENIDO ESTATICO -->
<f:selectItem />
<!-- CONTENIDO DINAMICO -->
<f:selectItems />
</h:selectManyMenu>
<h:selectManyCheckbox>
<!-- SE MUESTRAN MUCHOS CHECKBOX -->
<!-- CONTENIDO ESTATICO -->
<f:selectItem />
<!-- CONTENIDO DINAMICO -->
<f:selectItems />
</h:selectManyCheckbox>
<h:selectManyListbox>
<!-- SE MUESTRA UNA LISTA -->
<!-- CONTENIDO ESTATICO -->
<f:selectItem />
<!-- CONTENIDO DINAMICO -->
<f:selectItems />
</h:selectManyListbox>
</h:panelGrid>
</h:form>
</h:body>
JSF 2.X.
Dentro del entorno tecnológico de JEE 7 la parte del servidor
Contenido.
❖ Servlet.
❖ Filtros.
❖ Escuchadores.
❖ Configuración de aplicaciones web.
❖ Nuevas APIs de JEE 6.

NIVEL CURSO TEMATICA.


Medio Avanzado JEE 7. JSF 2.X.

También podría gustarte