Minientrada

Internacionalizando una aplicación web basada en JSF 2.x

En esta entrada voy a realizar una aplicación muy básica con JSF 2.x y voy a internacionalizarla, de forma que se pueda ejecutar en dos lenguajes diferentes: español e inglés.

Es conveniente si tenemos claro que la aplicación debe aceptar varios idiomas que la internacionalización la apliquemos en la fase más temprana posible. Incorporar la internacionlización cuando la aplicación ya está muy avanzada suele ser bastante más costoso que hacerlo en etapas tempranas por varios motivos, pero principalmente las diferencias que hay entre los diferentes idiomas en cuanto a decir lo mismo con más o menos palabras pueden afectar al diseño de las páginas.

Requisitos para desarrollar el ejemplo.

Para que se pueda reproducir el ejemplo y esté operativo al 100% comento qué entorno he utilizado para hacerlo funcionar.

IDE desarrollo: Eclipse 3.6 (Neon).
Máquina virtual Java: JDK 1.8.0_65.
Servidor de aplicaciones: Apache Tomcat 8.5
Ciclo de vida: Maven 3.x
Tecnología: JSF 2.2 (Java EE 7)

Recomiendo la lectura de la entrada JSF 2.x Hola Mundo pues en ella comento todos los aspectos de configuración que en esta entrada voy a dar por supuestos, como la elección de librerías, configuración de servlets, etcétera.

Módulo web de aplicación.

Este ejemplo es tan sencillo que va a contar con un único módulo; el módulo web. El módulo web genera un WAR que se despliega directamente en Apache Tomcat.

Estructura

2016-09-09_14-18-13

Fichero: pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>es.egv.jee6.jsf2</groupId>
 <artifactId>i18nBasico</artifactId>
 <version>1.0.0</version>
 <packaging>war</packaging>

 <dependencies>
   <dependency>
     <groupId>com.sun.faces</groupId>
     <artifactId>jsf-api</artifactId>
     <version>2.2.11</version>
   </dependency>

   <dependency>
     <groupId>com.sun.faces</groupId>
     <artifactId>jsf-impl</artifactId>
     <version>2.2.11</version>
   </dependency>

   <dependency>
     <groupId>javax.el</groupId>
     <artifactId>javax.el-api</artifactId>
     <version>3.0.1-b04</version>
   </dependency>

   <dependency>
     <groupId>javax.servlet.jsp.jstl</groupId>
     <artifactId>jstl-api</artifactId>
     <version>1.2</version>
   </dependency>
 </dependencies>

</project>

Fichero: WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>BasicoI18N</display-name>
  <welcome-file-list>
    <welcome-file>i18nwelcome.xhtml</welcome-file>
  </welcome-file-list>
  <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>/</url-pattern>
  </servlet-mapping>
 
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
  
  <context-param>
    <param-name>javax.faces.CONFIG_FILES</param-name>
    <param-value>WEB-INF/faces-config.xml</param-value>
  </context-param>

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
  
</web-app>

Destacar la inclusión de un parámetro de configuración que indica donde se encuentra el fichero de configuración de JSF. En este ejemplo vamos a incluir este fichero de configuración ya que va a ser necesario indicar los ficheros donde se encuentran las traducciones del idioma y el idioma por defecto de la aplicación.

Fichero: WEB-INF/faces-config.xml

<faces-config 
    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"
    version="2.2">
    <application>
        <locale-config>
            <default-locale>es</default-locale>
        </locale-config>
        <resource-bundle>
            <base-name>i18n.i18nBasico</base-name>
            <var>i18n</var>
        </resource-bundle>
    </application>
</faces-config>

El campo <locale-config> sirve para definir las configuraciones relativas a la situación geográfica, entre ellas las localizaciones permitidas <supported-locale> o la localización por defecto <default-locale>. La localización es imprescindible para internacionalizar la aplicación.

El campo <resource-bundle> es la piedra angular de la internacionalización, ya que en él se indica donde va a encontrar la aplicación los ficheros que contienen las etiquetas en sus respectivos idiomas <base-name> y el nombre de la variable que se va a utilizar para hacer referencias a ellas <var>. Así podremos encontrar en las páginas dinámicas código como el siguiente donde se hace referencia a la etiqueta i18nwelcome.saludo del grupo de recursos identificado por el nombre de variable i18n:

<h:outputText value="#{i18n['i18nwelcome.saludo']}" />

El fichero faces-config.xml añade un montón de posibilidades de configuración más, pero se escapan al ámbito de esta entrada. Pueden consultarse en el siguiente link.

Los ficheros de recursos para los idiomas.

Estos ficheros contienen las traducciones a diferentes idiomas de las etiquetas de la aplicación. El nombre es el indicado en el campo <base-name> al que se le concatena el lenguaje de la variable Locale. Por ejemplo, para contener las etiquetas en inglés de la aplicación, el fichero se llama i18nBasico_en.properties.

Fichero: i18n/i18nBasico_en.properties

i18nwelcome.titulo = JSF 2.x internacionalization example.
i18nwelcome.saludo = Internacionalization with JSF 2.x. 
i18nwelcome.etiquetaIdioma = Language
i18nwelcome.descripcion-metodo1 = Combo Box Method
i18nwelcome.descripcion-metodo2 = Image Method

Fichero: i18n/i18nBasico_es.properties

i18nwelcome.titulo = Ejemplo de internacionalización con JSF 2.x.
i18nwelcome.saludo = Internacionalización con JSF 2.x. 
i18nwelcome.etiquetaIdioma = Idioma
i18nwelcome.descripcion-metodo1 = Método basado en una lista seleccionable. 
i18nwelcome.descripcion-metodo2 = Método basado en imágenes.

Se puede ver que el nombre de las etiquetas es el mismo en ambos ficheros, pero el valor de las mismas está traducido al idioma que se indica en el nombre del fichero.

El Managed Bean que maneja la lógica de presentación del idioma.

Fichero: es.egv.jee6.jsf2.i18n.IdiomaBean.java

package es.egv.jee6.jsf2.i18n;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;

@ManagedBean(name="idiomaBean")
@SessionScoped
public class IdiomaBean implements Serializable {


    private static final long serialVersionUID = 1L;

    private String codigoIdioma;
    
    private Map<String,Object> listaIdiomas;
    
    public IdiomaBean() {
        super();
        this.codigoIdioma = FacesContext.getCurrentInstance().getViewRoot().getLocale().getLanguage();
        this.listaIdiomas = new LinkedHashMap<String,Object>();
        this.listaIdiomas.put("Español", new Locale("es"));
        this.listaIdiomas.put("Inglés", new Locale("en"));
    }

    public String getCodigoIdioma() {
        return codigoIdioma;
    }
    
    public void setCodigoIdioma(String codigoIdioma) {
        this.codigoIdioma = codigoIdioma;
    }
    
    public Map<String, Object> getListaIdiomas() {
        return listaIdiomas;
    }
    
    public void doCambioIdiomaConLista(ValueChangeEvent e)
    {

        String newCodigoIdioma = e.getNewValue().toString();
        System.out.println("newCodigoIdioma=" + newCodigoIdioma);
        System.out.println("idiomaBean=" + this.toString());

        //loop country map to compare the locale code
        for (Map.Entry<String, Object> entry : listaIdiomas.entrySet()) 
        {

               if(entry.getValue().toString().equals(newCodigoIdioma))
               {
                   System.out.println("Asignando nueva locale al contexto de Faces.");
                   FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale)entry.getValue());
               }
        }
    }
    
    public void doCambioIdiomaConImagen(String nuevoIdioma)
    {

        //loop country map to compare the locale code
        for (Map.Entry<String, Object> entry : listaIdiomas.entrySet()) 
        {

               if(entry.getValue().toString().equals(nuevoIdioma))
               {
                   System.out.println("Pinchado en imagen " + nuevoIdioma + ". Asignando nueva locale al contexto de Faces");
                   this.codigoIdioma = nuevoIdioma;
                   FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale)entry.getValue());
               }
        }
    }    
   
    @Override
    public String toString() {
        return "IdiomaBean [codigoIdioma=" + codigoIdioma + ", listaIdiomas=" + listaIdiomas + "]";
    }     
}

Defino un managed bean específico para manejar el idioma. Será el encargado de conservar el idioma seleccionado por el usuario, una lista de idiomas seleccionables, y las diferentes acciones que se llevan a cabo cuando se realizan acciones relacionadas con el idioma.

@ManagedBean(name="idiomaBean")
@SessionScoped
public class IdiomaBean implements Serializable {

Este ManagedBean lo defino en el ámbito de la sesión @SessionScoped. De esta manera, el idioma queda almacenado en todas las navegaciones que realice el usuario hasta que cierre el navegador, o la sesión.

    private String codigoIdioma;
    private Map<String,Object> listaIdiomas;

Se definen la variable codigoIdioma para contener el idioma seleccionado por el usuario y la variable listaIdiomas para contener los idiomas que van a mostrarse al usuario para proceder a la traducción de la aplicación.Se incluyen también sus getters y sus setters.

    public IdiomaBean() {
        super();
        this.codigoIdioma = FacesContext.getCurrentInstance().getViewRoot().getLocale().getLanguage();
        this.listaIdiomas = new LinkedHashMap<String,Object>();
        this.listaIdiomas.put("Español", new Locale("es"));
        this.listaIdiomas.put("Inglés", new Locale("en"));
    }

Se incluye un constructor para rellenar el código del idioma con el idioma configurado por JSF en el arranque, que será el idioma que se ha configurado en el <default-locale> del fichero faces-config.xml, y rellenar la lista de idiomas con los idiomas válidos para la aplicación.

   public void doCambioIdiomaConLista(ValueChangeEvent e)
    {

        String newCodigoIdioma = e.getNewValue().toString();
        System.out.println("newCodigoIdioma=" + newCodigoIdioma);
        System.out.println("idiomaBean=" + this.toString());

        //loop country map to compare the locale code
        for (Map.Entry<String, Object> entry : listaIdiomas.entrySet()) 
        {

               if(entry.getValue().toString().equals(newCodigoIdioma))
               {
                   System.out.println("Asignando nueva locale al contexto de Faces.");
                   FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale)entry.getValue());
               }
        }
    }

Se añade un método para manejar el cambio del idioma cuando se seleccione un idioma de la lista.

<h:selectOneMenu value="#{idiomaBean.codigoIdioma}" onchange="submit()"
                valueChangeListener="#{idiomaBean.doCambioIdiomaConLista}">

Y, se añade un método que se ejecutará cuando se seleccione un cambio de idioma pinchando en la imágen del mismo.

    public void doCambioIdiomaConImagen(String nuevoIdioma)
    {

        //loop country map to compare the locale code
        for (Map.Entry<String, Object> entry : listaIdiomas.entrySet()) 
        {

               if(entry.getValue().toString().equals(nuevoIdioma))
               {
                   System.out.println("Pinchado en imagen " + nuevoIdioma + ". Asignando nueva locale al contexto de Faces");
                   this.codigoIdioma = nuevoIdioma;
                   FacesContext.getCurrentInstance().getViewRoot().setLocale((Locale)entry.getValue());
               }
        }
    }
<h:commandLink action="#{idiomaBean.doCambioIdiomaConImagen('en')}">

Destacar que se ha incluido la asignación del idioma seleccionado a la variable codigoIdioma del bean para que quede guardada la sección.

this.codigoIdioma = nuevoIdioma;

Páginas web dinámicas.

El ejemplo consta de tres páginas web dinámicas: i18nwelcome.xhtml que es la página básica de bienvenida, i18nidiomametodo1.xhtml que es la página con el manejo de idioma basado en una lista seleccionable, e i18nidiomametodo2.xhtml que es la página con el manejo del idioma basado en imágenes.

2016-09-09_15-17-48

Fichero: i18nwelcome.xhtml

<!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"
    xmlns:ui="http://java.sun.com/jsf/facelets">

<h:body>
    <h1>#{i18n['i18nwelcome.titulo']}</h1>
    <h:form>
        <h2>
            <h:outputText value="#{i18n['i18nwelcome.saludo']}" />
        </h2>        
        <ui:include src="i18nidiomametodo1.xhtml" />        
        <ui:include src="i18nidiomametodo2.xhtml" />
    </h:form>
</h:body>
</html>

Esta página muestra dos etiquetas a internacionalizar; i18nwelcome.titulo e i18nwelcome.saludo. Para mostrar correctamente el idioma se utiliza expresiones EL que son interpretadas por JSF en tiempo de ejecución.

#{i18n['i18nwelcome.titulo']}

Esta expresión indica que se va a recoger del bundle i18n la etiqueta i18nwelcome.titulo.

Por claridad, los dos métodos que se van a utilizar para la selección del idioma se han realizado en dos páginas dinámicas independientes que se incluyen en la página básica mediante la sentencia <ui:include src=»»/>.

<ui:include src="i18nidiomametodo1.xhtml" />

Fichero: i18nidiomametodo1.xhtml

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
        <h:outputText value="#{i18n['i18nwelcome.descripcion-metodo1']}."/>
        <h:panelGrid columns="2">
            <h:outputText value="#{i18n['i18nwelcome.etiquetaIdioma']}:" />
            <h:selectOneMenu value="#{idiomaBean.codigoIdioma}" onchange="submit()"
                valueChangeListener="#{idiomaBean.doCambioIdiomaConLista}">
                <f:selectItems value="#{idiomaBean.listaIdiomas}" />
            </h:selectOneMenu>
        </h:panelGrid>
</ui:composition>

Estos ficheros como se han diseñado para ser incluidos en un fichero padre y no para llamarse directamente se crean como elementos <ui:composition/>.

El valor seleccionado en la lista seleccionable es el codigoIdioma almacenado en el Managed Bean.

<h:selectOneMenu value="#{idiomaBean.codigoIdioma}"

La lista se carga con los valores almacenados en la listaIdiomas del Managed Bean.

<f:selectItems value="#{idiomaBean.listaIdiomas}" />

Cuando se cambia el elemento seleccionado se lanza el método doCambioIdiomaConLista.

onchange="submit()"
                valueChangeListener="#{idiomaBean.doCambioIdiomaConLista}"

Fichero: i18nidiomametodo2.xhtml

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
        <h:outputText value="#{i18n['i18nwelcome.descripcion-metodo2']}."/>
        <h:panelGrid columns="1">
            <h:commandLink action="#{idiomaBean.doCambioIdiomaConImagen('en')}">
                <h:graphicImage library="images" name="bandera-en.png" rendered="#{idiomaBean.codigoIdioma == 'es'}"/>
            </h:commandLink>
            <h:commandLink action="#{idiomaBean.doCambioIdiomaConImagen('es')}">
                <h:graphicImage library="images" name="bandera-es.png" rendered="#{idiomaBean.codigoIdioma == 'en'}"/>
            </h:commandLink>
        </h:panelGrid>
</ui:composition>

Destacar que se muestra el botón con la bandera del idioma que no está seleccionado, para así poder cambiarlo. Para ello se utiliza el atributo rendered y expresiones EL.

rendered="#{idiomaBean.codigoIdioma == 'es'}"

Demo

El pase de diapositivas requiere JavaScript.

Link

Configurar Aplicaciones JSF
Documentación oficial JSF 2.x

 

Minientrada

JSF 2.x Hola Mundo

En esta entrada voy a explicaros como hacer una aplicación muy básica con JSF 2.x, un Hola Mundo. JSF (o Java Server Faces) es la tecnología estándar de Java para simplificar la interfaz de usuario de aplicaciones web.

La versión 2.0 de JSF fue lanzada en Agosto de 2009 y corresponde con Java EE 6. La versión 2.2 de JSF fue lanzada en Abril de 2013 y corresponde con Java EE 7.

Para probar la aplicación voy a publicarla en un contenedor de servlets Apache Tomcat 8.5. Al no ser un servidor de aplicaciones homologado con Java EE habrá que incluir algunas librerías que no serían necesarias si la publicación se hiciera en un contenedor de aplicaciones homologado.

Al tener que incluir las librerías en la aplicación Java voy a utilizar la versión 2.2 de JSF. Hay que tener en cuenta que si se va a publicar en un servidor de aplicaciones existente la versión de JSF que incorporan depende de la Java EE homologada que implementen; Java EE 5 – JSF 1.2, Java EE 6 – JSF 2.0, Java EE 7 – JSF 2.2.

Entorno utilizado para desarrollar el ejemplo.

Para que se pueda reproducir el ejemplo y esté operativo al 100% comento qué entorno he utilizado para hacerlo funcionar.

IDE desarrollo: Eclipse 3.6 (Neon).
Máquina virtual Java: JDK 1.8.0_65.
Servidor de aplicaciones: Apache Tomcat 8.5
Ciclo de vida: Maven 3.x
Tecnología: JSF 2.2 (Java EE 7)

Módulo web de aplicación.

Este ejemplo es tan sencillo que va a contar con un único módulo; el módulo web. El módulo web genera un WAR que se despliega directamente en Apache Tomcat.

Fichero: pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>es.egv.jee6.jsf2</groupId>
 <artifactId>BasicoJSF2</artifactId>
 <version>1.0.0</version>
 <packaging>war</packaging>

 <dependencies>
   <dependency>
     <groupId>com.sun.faces</groupId>
     <artifactId>jsf-api</artifactId>
     <version>2.2.11</version>
   </dependency>

   <dependency>
     <groupId>com.sun.faces</groupId>
     <artifactId>jsf-impl</artifactId>
     <version>2.2.11</version>
   </dependency>

   <dependency>
     <groupId>javax.el</groupId>
     <artifactId>javax.el-api</artifactId>
     <version>3.0.1-b04</version>
   </dependency>

   <dependency>
     <groupId>javax.servlet.jsp.jstl</groupId>
     <artifactId>jstl-api</artifactId>
     <version>1.2</version>
   </dependency>
 </dependencies>

</project>

En este caso, al desplegarse la aplicación en un contenedor de Servlets y no en un servidor de aplicaciones se incluyen como dependencias las librerías: jsf-api, jsf-impl (mojarra), javax.el-api y jstl-api. Si hubieramos desplegado esta aplicación en un servidor de aplicaciones homologado con la Java EE 6 o superiores, solo deberíamos incluir como dependencia la librería jsf-api, ya que el resto serían proporcionadas por el contenedor. En ese caso tener en cuenta también que la versión Java EE 6 incluye la versión 2.0 de JSF.

Fichero: web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>BasicoJSF2</display-name>
  <welcome-file-list>
    <welcome-file>holamundo.xhtml</welcome-file>
  </welcome-file-list>
  <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>/</url-pattern>
  </servlet-mapping>
 
    <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
</web-app>

Destacar dos cosas importantes en el fichero de configuración del módulo web:

  • Al cargarse el módulo web en el servidor se ejecuta el servlet que JSF incorpora para interpretar sus páginas (javax.faces.webapp.FacesServlet).
  • Mientras se esta desarrollando la aplicación es conveniente informar el parámetro JSF javax.faces.PROJECT_STAGE con el valor Development. De esta forma el servidor proporciona mucha información de DEBUG que de otra manera no estaría disponible. Cuando se quiera poner la aplicación en producción se sustituye ese valor por el de Production.

JSF 2.x Managed Bean

Los managed bean son clases java que se registran en el framework de JSF, lo que permite que puedan ser consumidos desde las páginas dinámicas. Desde la versión 2.0 de JSF este registro se puede llevar a cabo mediante la anotación @javax.faces.bean.ManagedBean.

Los managed bean contienen métodos get y set para poder guardar y recoger datos de la sesión y métodos de lógica de negocio, aunque si la aplicación es compleja la lógica de negocio recomiendo que recaiga en componentes EJB.

Fichero: HolaMundoMBean.java

package es.egv.jee6.jsf2.mbean;

import java.io.Serializable;
import java.text.SimpleDateFormat;

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

@ManagedBean(name="holaMundoMBean")
@SessionScoped
public class HolaMundoMBean implements Serializable {
    private static final long serialVersionUID = -239729940657225276L;
    
    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCurrentTime() {
         
        return new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new java.util.Date().getTime()); // Older version, SimpleDateFormat is not thread safe
    }
    
    
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("HolaMundoMBean [name=");
        builder.append(name);
        builder.append("]");
        return builder.toString();
    }    
    
}

JSF 2.x Páginas dinámicas

El formato recomendado al desarrollar páginas dinámicas en JSF es el formato XHTML (Extensible HTML) .

Para el ejemplo de hola mundo voy a realizar dos páginas dinámicas; holamundo.xhtml y holamundobienvenida.xhtml. Desde la página inicial se viaja a la de bienvenida llevándose la información del nombre para que la bienvenida sea personalizada.

Fichero: holamundo.xhtml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!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:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">
<h:head>
    <title>Aplicación JSF 2.x</title>
</h:head>
<h:body bgcolor="white">
    <h2>Ejemplo. Hola Mundo en JSF 2.x</h2>
    <h:form>
        <h3>Fecha y hora: <h:outputLabel> #{holaMundoMBean.currentTime}</h:outputLabel></h3>
        <h3>A continuacion escribe tu nombre en el siguiente campo:</h3>
        <h:inputText value="#{holaMundoMBean.name}"/>
        <h:commandButton value="Pulsa" action="holamundobienvenida"/>
    </h:form>
</h:body>
</html>

Fichero: holamundobienvenida.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://java.sun.com/jsf/html">
 
    <h:head>
        <title>Aplicación JSF 2.x</title>
    </h:head>
    <h:body bgcolor="cyan">
        <h2>Ejemplo. Hola Mundo en JSF 2.x</h2>
        <h3>Hola #{holaMundoMBean.name}, bienvenido al ejemplo Hola Mundo con JSF 2.x.</h3>
        <h3>Fecha y hora: #{holaMundoMBean.currentTime} </h3>
        <h3>Hasta Luego.</h3>
        <h:form>
            <h:commandButton value="Volver" action="holamundo"/>
        </h:form>
    </h:body>
</html>

Hay que fijarse en las siguientes peculiaridades:

  • En JSF 1.x es obligatorio declarar una «navigation rule» en el fichero faces-config.xml para indicarle a la aplicación a qué página hay que ir cuando se pulsa un botón. Sin embargo, en JSF 2.x para facilitar la navegación se puede poner el nombre de la página directamente en el atributo action del botón. En aplicaciones pequeñas esta ventaja es un buen recurso. En aplicaciones grandes es recomendable seguir utilizando el fichero faces-config.xml para definir las reglas de navegación.
  • El campo #{…} indica una expresión dinámica de JSF que se interpreta en tiempo de ejecución. En la página holamundo.xhtml la expresión  #{holaMundoMBean.name} en el componente inputText indica que cuando se hace submit del formulario, JSF encuentra el Managed Bean holaMundoMBean, y en la propiedad name recoge el valor del componente mediante el método set correspondiente. Cuando en la página holamundobienvenida.xhtml se ejecute la expresión #{holaMundoMBean.name}, esta accede a la propiedad correspondiente del Managed Bean y recupera el valor incluido en la anterior página.

Resultado final.

A continuación se pueden ver los pantallazos con la aplicación funcionando.

En la primera página se teclea el nombre y se pulsa el botón de submit del formulario.

2016-08-29_14-45-14

Una vez tecleado y cuando se pulsa el botón se muestra la página de bienvenida.

2016-08-29_14-46-20

Si se pincha el botón Volver se regresa a la página holamundo.xhtml.

Links

Java Server Faces en la Wikipedia

Java EE Historial de versiones

Extensible HTML