Internacionalización y Logs en JSF 2.0

En esta segunda entrada vamos a abordar tres nuevos retos:
  • Separar los textos de los mensajes de los archivos xhtml.
  • Configurar el proyecto para obtener unos logs. 
  • Dotar de navegabilidad a la aplicación creada en la anterior entrada.
Empecemos por el último que lo desarrollaremos de forma paralela al primer objetivo.
Inicialmente teníamos una página de inicio "index.xhtml", a esta le vamos añadir un botón que nos lleve a una página, "login.xhtml" con dos cuadros para introducir un usuario con contraseña  y un botón que nos lleve a una pantalla de bienvenida "loginok.xhtml" o de nuevo a "index.xhtml" si no hay ningún usuario.
El primer paso va a ser crear en la carpeta resources un archivo que contenga los literales. Por comodidad tengo instaladas los plugins de jboss que ayudan para la edición, pero desde el eclipse se pueden editar "a pelo" sin problemas.
Mi archivo "messages.properties" queda algo así:

mainTittle = HelloWorld JSF
loginOkWelcome=Bienvenido {0} .
indexWelcome = Bienvenido al mundo de JSF. Chaval\u00EDn
gotologin=Login
loginWelcome Bienvenido a la P\u00E1gina de Login
loginName=Nombre
loginPass=Contrase\u00F1a

Destacar la etiqueta {0} que me permitirá insertar de forma dinámica un nombre.

El la página de inicio queda así:

?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://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">

<f:loadBundle basename="messages" var="msg" />

<h:head>
    <title>#{msg.mainTittle}</title>
</h:head>
<h:body>
    <h3>#{msg.indexWelcome}</h3>
    <h:form>
        <h:commandButton value="#{msg.gotologin}" action="login" />
    </h:form>
</h:body>
</html>

La etiqueta nueva es <f:loadBundle basename="messages" var="msg" /> es la encargada de que es carguen los literales del mensages.properties. La sintaxis para recuperar los mensajes es bastenta evidente, en la definición se crea una variable  "msg" para mapear los textos. Con "msg.mainTittle" recuperamos el literal "HelloWorld JSF".
En la etiqueta commandButton además del texto tenemos una propiedad "action" que mediante el mecanismo de navegación implícita nos llevará hasta la página "login.xhtml".
Dentro del body de la página tenemos el siguiente formulario:
    <h:form>
        <h:panelGrid border="1" columns="2">
             #{msg.loginName} <h:inputText value="#{user.name}" /> 
            #{msg.loginPass} <h:inputSecret value="#{user.pass}" />
            <h:commandButton value="submit" action="#{login.goToLogin}" />
        </h:panelGrid>
    </h:form>

El camtpo de texto se define como inputText, el de contraseñas como inputSecret y queremos que cuando clickemos en el botón se ejecute un método goToLogin que nos comprueba si el nombre introducido comienza por "A" . La lógica no es que sea gran cosa, pero algo tenía que meter para validar.
El contenedor del nombre y la contraseña sera un "ManagedBean" que en nuestro caso será un javabean con apenas un par de anotaciones:

package es.sinjava;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean (name="user")
@SessionScoped
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private String pass;

// Getter y Setter...

}

El método "goToLogin"  lo metemos en una clase java:

    @ManagedProperty(value = "#{user}")
    private User user;

    public String goToLogin() {
 
        if (user != null && user.getName().startsWith("A")) {
            return "loginOk";
           
        } else {   
            return "index";
        }
    }
En la que hemos anotado que el "user" es uno de los "gestionados" por JSF.

Por último la pantalla de loginOk será:

<body>
    <h:outputFormat value="#{msg.loginOkWelcome}">
        <f:param value="#{user.name}" />
    </h:outputFormat>
</body>

la etiqueta outputFormat se va a encarga de inyectar de forma dinámica y elegante el valor de "user.name" en el literal definido en el messages.properties.

Ahora queda el último objetivo: utilizar los logs. Para ello vamos a añadir en el pom una dependencia:
<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.4</version>
        </dependency>

Y vamos a configurar el log4j en el web.xml:

  <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:/log4j.properties</param-value>
    </context-param>

Y en la carpeta resources vamos a meter el archivo de configuración "log4j.properties" siguiente:

log4j.rootLogger = DEBUG, stdout, logfile
log4j.category.es.sinjava= DEBUG

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold = DEBUG
log4j.appender.stdout.Target   = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %5p | %c | %M | %L | %m%n

log4j.appender.logfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File = /logs/firstJsf.log
log4j.appender.logfile.append = true
log4j.appender.logfile.DatePattern = '.'yyyy-MM-dd'.log'
log4j.appender.logfile.layout = org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern = %5p | %d{yyyy-MM-dd HH:mm:ss} | %c | %M | %L | %m%n

Para comprobar que el logger funciona correctamente en la clase "Login.java" he definido un logger:

    private final Logger log = Logger.getLogger(Login.class);
   
    @ManagedProperty(value = "#{user}")
    private User user;

    public String goToLogin() {
        log.debug("Hola goToLogin");
        if (user != null && user.getName().startsWith("A")) {
        ....
        }
    }
Mi idea inicial es utilizar siempre que se pueda (y sepa) anotaciones en vez de pelear con los xml. (El terrilble infierno de los xml es poderoso).
Con JSF 2 estamos utilizando en las expresiones "#{}" EL (Expresion Language).
En todos los archivos xhtml está la etiqueta <f:loadBundle basename="messages" var="msg" />. En cualquier caso puedes bajarte el proyecto maven para ver los archivos completos. El proyecto completo se puede descargar aquí.

Quizá pueda ser interesante visitar la página de chuidiang si se quiere partir de un proyecto similar sin utilizar maven o utilizando maven esta entrada en mkyong .






Comentarios