Ir al contenido principal

Instalando Apache NetBeans 10 y configurarlo con Jakarta EE

Tutorial JSF 2.2 - Sesión 3: Ciclo de vida de una aplicación

Tutorial JSF 2.2 - Sesión 3: Ciclo de vida de una aplicación
Siguiendo con el tutorial de JSF 2.2, esta vez veremos el ciclo de vida de una aplicación. Es importante conocer esto, ya que podremos saber cómo viaja una petición desde el cliente web, es procesado por el servidor, y cómo devuelve el resultado.

En la página 7.6 The Lifecycle of a JavaServer Faces Application se puede ver el siguiente gráfico que representa el ciclo de vida de una aplicación:

... pero como no quiero dar una simple traducción de otra web, lo que haremos es ver cada fase del ciclo de vida en una aplicación.

Fijémonos en los recuadros azules, son seis. Estos son los pasos que se ejecuta en el lado del servidor. Los recuadros blancos son como los "estímulos" para que la aplicación haga algo. Como podemos ver en el flujo, puede pasar desde los primeros recuadros "Restore View" y "Apply Request" hasta "Process Validations" si es que se aplica una validación, o puede ir directo a la última fase "Render Response" si es que no tiene nada que hacer. Esto último sucede cuando se llama a una página por primera vez y no se procesa nada, mas que mostrar el contenido de la página.

Para probar eso, vamos a crear una aplicación (que yo la llamé jsf-03-lifecycle) y tiene los siguientes .xhtml

index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head >
        <title>Facelet Title</title>
    </h:head>
    <h:body  >
        <h1>Formulario de prueba</h1>
        <h:form>
            <h:panelGrid columns="2">
                <h:outputLabel value="Nombre" for="nombre"/>
                <h:inputText  id="nombre" value="#{lifecycleBean.personaForm.nombre}" />
                
                <h:outputLabel value="Sexo" />
                <h:panelGroup layout="block">
                    <h:selectOneRadio value="#{lifecycleBean.personaForm.sexo}">
                        <f:selectItem itemLabel="Hombre" itemValue="H" />
                        <f:selectItem itemLabel="Mujer" itemValue="M" />
                    </h:selectOneRadio>
                </h:panelGroup>
                
                
            </h:panelGrid>
            <h:commandButton action="result" value="Enviar" />
        </h:form>
    </h:body>
</html>

result.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h2>Result</h2>
        <h:form>
            <h:panelGrid columns="2">
                <h:outputText value="Nombre"/>
                <h:outputText value="#{lifecycleBean.personaForm.nombre}" />

                <h:outputText value="Sexo"/>
                <h:outputText value="#{lifecycleBean.personaForm.sexo}" />

            </h:panelGrid>
            <h:commandLink value="Regresar" action="index"/>
        </h:form>
    </h:body>
</html>


Nuestro ManagedBean tendrá el siguiente código:

//Archivo LifecycleManagedBean.java

package com.apuntesdejava.jsf.controladores;

import com.apuntesdejava.jsf.bean.PersonaForm;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named("lifecycleBean")
@RequestScoped

public class LifecycleManagedBean {

    private PersonaForm personaForm = new PersonaForm();

    public LifecycleManagedBean() {

    }

    public PersonaForm getPersonaForm() {
        return personaForm;
    }

    public void setPersonaForm(PersonaForm personaForm) {
        this.personaForm = personaForm;
    }

}

El bean que nos servirá de bean tiene el siguiente código:
//Archivo PersonaForm.java
package com.apuntesdejava.jsf.bean;

import java.util.Date;

public class PersonaForm {

    private String nombre;
    private char sexo;
    private Date fechaRegistro;
    private String correoElectronico;

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public char getSexo() {
        return sexo;
    }

    public void setSexo(char sexo) {
        this.sexo = sexo;
    }

    public Date getFechaRegistro() {
        return fechaRegistro;
    }

    public void setFechaRegistro(Date fechaRegistro) {
        this.fechaRegistro = fechaRegistro;
    }

    public String getCorreoElectronico() {
        return correoElectronico;
    }

    public void setCorreoElectronico(String correoElectronico) {
        this.correoElectronico = correoElectronico;
    }

}

Hasta aquí no hay nada extraordinario. Ahora debemos agregar un "oidor" que lo engancharemos al ciclo de vida para ver los efectos que sucede cuando ejecutamos la aplicación.
//Archivo MiPhaseListener.java
package com.apuntesdejava.jsf.controladores;

import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

public class MiPhaseListener implements PhaseListener {

    static final Logger LOGGER = Logger.getLogger(MiPhaseListener.class.getName());

    @Override
    public void afterPhase(PhaseEvent event) {
        LOGGER.log(Level.INFO, "Después:{0}", event.getPhaseId());
    }


    @Override
    public void beforePhase(PhaseEvent event) {
        LOGGER.log(Level.INFO, "Antes:{0}", event.getPhaseId());
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }
}


Y para engancharlo al ciclo de vida de la aplicación, debemos crear el archivo faces-config.xml en WEB-INF. Si no existe el archivo, lo creamos desde la opción New File > JavaServer Faces > JSF Faces Configuration:



Una vez creado, registramos el listener en el .xml que acabamos de crear usando el siguiente código:
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    <lifecycle>
        <phase-listener> com.apuntesdejava.jsf.controladores.MiPhaseListener</phase-listener>
    </lifecycle>
</faces-config>

Ahora, ejecutaremos la aplicación, y veamos el log de cada petición que hagamos.

Veamos: La primera vez que ejecutamos:

Solo se presentan dos fases: RESTORE_VIEW y RENDER_RESPONSE. Es decir, la primera y la última fase.

Veamos qué pasa cuando colocamos valores y le damos clic en "Enviar":

Al parecer ya pasa por todas las fases del ciclo de vida. Veamos si ponemos una validación. Para ello agregamos las líneas resaltadas en el index.html como sigue:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head >
        <title>Facelet Title</title>
    </h:head>
    <h:body  >
        <h1>Formulario de prueba</h1>
        <h:form>
            <h:panelGrid columns="2">
                <h:outputLabel value="Nombre" for="nombre"/>
                <h:inputText  id="nombre" value="#{lifecycleBean.personaForm.nombre}" />

                <h:outputLabel value="Correo electrónico" for="email"/>
                <h:inputText  id="email" value="#{lifecycleBean.personaForm.correoElectronico}" >
                    <f:validator validatorId="emailValidator"/>
                </h:inputText>

                <h:outputLabel value="Sexo" />
                <h:panelGroup layout="block">
                    <h:selectOneRadio value="#{lifecycleBean.personaForm.sexo}">
                        <f:selectItem itemLabel="Hombre" itemValue="H" />
                        <f:selectItem itemLabel="Mujer" itemValue="M" />
                    </h:selectOneRadio>
                </h:panelGroup>


            </h:panelGrid>
            <h:commandButton action="result" value="Enviar" />
        </h:form>
    </h:body>
</html>



Y creamos la siguiente clase:

//EmailValidator.java
package com.apuntesdejava.jsf.validation;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator("emailValidator")
public class EmailValidator implements Validator {

    private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\." + "[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*" + "(\\.[A-Za-z]{2,})$";
    private final Pattern pattern;
    private Matcher matcher;

    public EmailValidator() {
        pattern = Pattern.compile(EMAIL_PATTERN);
    }

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        matcher = pattern.matcher(value.toString());
        if (!matcher.matches()) {
            FacesMessage msg = new FacesMessage("Falló la validación del correo electrónico.", "El formato de correo electrónico no es válido");
            msg.setSeverity(FacesMessage.SEVERITY_ERROR);
            throw new ValidatorException(msg);
        }

    }

}

Y al ejecutar el proyecto, ingresemos un valor no válido para el correo electrónico. Esto es lo que nos mostrará en el log del servidor:

(He limpiado el log antes de ejecutar el error. Esto es para poder más claramente el log que responde)
Como podemos ver, no muestra el paso 4 ni el paso 5. Es decir, no está actualizando el modelo, ni está dando la ejecución a la aplicación, ya que la validación no lo dejó.

De esta manera, nos podemos asegurar que cada fase se ejecuta de manera independiente, y no afecta con la lógica de nuestra aplicación. Si queremos validar una entrada de un campo, debemos asegurarnos que se haga en su capa respectiva (usando los validadores) y no cuando haya guardado los valores en el modelo, ya que sería más difícil el manejo de mensajes de error al usuario. Con esto en mente, veremos poco a poco como este ciclo de vida nos ayudará en las siguientes aplicaciones.

Esto es todo por ahora.

El código fuente lo pueden descargar desde aquí: https://java.net/projects/apuntes/downloads/download/web/Tutorial%20JSF%202.2/jsf-03-lifecycle.tar.gz


Bibliografía

El modelo del ciclo de vida lo tomé de: Java EE 7 Tutorial - 7.6 The Lifecycle of a JavaServer Faces Application

El ejemplo de validador de correo electrónico, lo tomé de aquí: Custom Email Validator in JSF.

Nos vemos en un siguiente post

Bendiciones para todos!

Comentarios

Entradas más populares de este blog

RESTful... la forma más ligera de hacer WebServices (Parte 1)

Quienes hayan usado SOAP para WebService, sabrán que es bien fácil de diseñar, pero algo complicado de consumir: se necesita toda una API para construir los clientes utilizando el WSDL. Por ejemplo, para PHP se necesita de la biblioteca NuSOAP. Entonces, para lograr el concepto de "lenguaje único XML" es un dolor de cabeza. Y más aún si el cliente es tan simple como JavaScript, manejar XML de SOAP provocaría suicidos masivos... o no usar WebServices.

Además, con SOAP se permite crear un solo servicio y ponerle varios métodos. Esto puede llevar a un mal diseño del servicio ya que podría tener un servicio que haga de todo: por ejemplo, un servicio de manejo de Clientes que permita también manejar Proveedores.

RESTful es una propuesta muy interesante de Roy Fielding que permite manejar los servicios web con métodos definidos, manteniendo la simpleza del protocolo como XML, pero que cada servicio sea identificado únicamente con un solo URI.

En este post veremos cómo crear un Ser…

¿Por valor o por referencia?

Esta es una pregunta existencial para todo programador Java. Cada uno encuentra una respuesta satisfactoria. Pero ahora veremos, basándonos en el libro para Certificación SCJP 1.5 de Katty Sierra, sobre la respuesta correcta.

Contraseñas encriptadas con Java

¿Quién no ha querido encriptar texto o archivo? Hay diversas maneras para hacer eso, por ejemplo, utilizando un diccionario donde se reemplazara cada caracter por un código.. y para poder desencriptarlo se debería utilizar el mismo diccionario para revertir el proceso. Pero si el diccionario cae en manos de alguien que no queremos que lo sepa, entonces estamos en peligro.
Cuando yo programaba en pascal, mi encriptación favorita era invirtiendo los bits... pero cualquiera también puede invertir los bits y listo.
Pero ya gente experta logró algoritmos de encriptación populares. Los más conocidos: MD5 y SHA.
En este post hablaremos cómo encriptar texto, sobretodo las contraseñas, utilizando MD5 o SHA.