domingo, marzo 31, 2013

Curso Business Intelligence Pentaho (OSBI Pentaho)

Hola Gente.


En esta ocasión quería invitarl@s a otra edición del Curso OSBI Pentaho 100% a distancia:


El curso será dictado a través de un aula virtual, y nos encargaremos de guiar el proceso de aprendizaje, así como también de responder todas las consultas que vayan surgiendo.

El curso inicia el 1 de Mayo de 2013 y tiene una duración aproximada de 80hs. Los cupos son limitados así que quien tenga interés, le recomendamos se pongan en contacto o soliciten más información a esta dirección: admin@troyanx.com

Saludos

Mariano

jueves, marzo 28, 2013

Agregar soporte para Hibernate a nuestra Aplicación PrimeFaces/JSF/Spring

Hola Estimad@s,

seguimos avanzando con nuestra aplicación, en este post veremos como hacer para que nuestra aplicación tenga soporte para utilizar un mapeador Objeto-Relacional (ORM), particularmente Hibernate.

Introducción

Como puede notarse en los diferentes posts referidos a esta aplicación, en ningún caso profundizamos en los temas, solo vamos definiendo lo mínimo a medida que lo necesitamos, de lo contrario esto sería un "megamanual" y esa no es la intención. Para un manejo más profundo de la temática recomiendo este curso, el cual es de muy alta calidad, el titulo del curso es Curso de "Hibernate con Spring", aunque en realidad solo profundiza (y muy bien) con Hibernate. De he he utilizado algunas clases utilitarias que propone el autor del curso, oportunamente haré la referencia.
Solo diré que hoy en día el uso de los ORM está muy extendido y facilita enormemente la tarea de la persistencia y el mantenimiento de la aplicación, además de dar solución a diferentes problemas inherentes a la temática, por ejemplo: validaciones, caching, logging, tuning, independencia del SGBDR, etc. Hibernate es lejos el ORM Open Source más usado y robusto del que disponemos.



Descargando Hibernate

La versión que utilizaremos es la 4.1.10.Final y se la puede descargar desde:
hibernate-release-4.1.10.Final.zip (Sistemas tipo window$)
o
hibernate-release-4.1.10.Final.tgz (Sistemas tipo Unix)

Una vez descargada la versión, descomprimimos el archivo en una carpeta que denominaremos [HIBERNATE_HOME].


Descargando Hibernate Validator

También utilizaremos Hibernate Validator, al cual nos referiremos en el futuro, por ello debemos descargarlo desde:
hibernate-validator-4.3.1.Final-dist.zip (Sistemas tipo window$)
o
hibernate-validator-4.3.1.Final-dist.tar.gz (Sistemas tipo Unix)


Una vez descargada la versión, descomprimimos el archivo en una carpeta que denominaremos [HIBERNATEVALIDATOR_HOME].


Copiando las librerías a nuestro proyecto

Ahora debemos copiar los archivos jar a nuestra carpeta  WEB-INF/lib, el listado de archivos necesarios es:


[HIBERNATE_HOME]/lib/required
  antlr-2.7.7.jar
  dom4j-1.6.1.jar
  hibernate-commons-annotations-4.0.1.Final.jar
  hibernate-core-4.1.10.Final.jar
  hibernate-jpa-2.0-api-1.0.1.Final.jar
  javassist-3.15.0-GA.jar
  jboss-logging-3.1.0.GA.jar
  jboss-transaction-api_1.1_spec-1.0.0.Final.jar

[HIBERNATEVALIDATOR_HOME]/
  hibernate-validator-4.3.1.Final.jar
  hibernate-validator-annotation-processor-4.3.1.Final.jar

[HIBERNATEVALIDATOR_HOME]/lib/required
  validation-api-1.0.0.GA.jar

[HIBERNATEVALIDATOR_HOME]/lib/optional
  joda-time-1.6.jar
  jsoup-1.6.1.jar


Archivo de configuración de Hibernate

El archivo de configuración principal de Hibernate es hibernate.cfg.xml y debemos colocarlo en el paquete por defecto de nuestras clases Java, en otras palabras en la carpeta src. El contenido inicial será:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="connection.datasource">java:/comp/env/jdbc/practico</property>
    <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>
    <property name="hibernate.hbm2ddl.auto">update</property>
  </session-factory>
</hibernate-configuration>

Donde:

la propiedad<property name="connection.datasource">java:/comp/env/jdbc/practico</property>
define la conexión a la base de datos que utilizaremos, en este caso le diremos a Hibernate que utilice la fuente de datos JNDI que ya hemos definido en esta aplicación.
Una forma de crear una conexión JDBC genérica sería:
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/practico</property>
<property name="connection.username">root</property>
<property name="connection.password">xxxx</property>


la propiedad<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
Define la implementación del dialecto que se utilizará, en este caso MySQL
la propiedades
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
Definen que se mostrar las sentencias SQL que se generan en el uso de Hibernate y que a su vez se muestren formateadas para su mejor lectura.
por último, la propiedad<property name="hibernate.hbm2ddl.auto">update</property>
Define que las sentencias DDL que se generen serán en forma de delta, esto es que se generarán las modificaciones necesarias para realizar los mapeos correctos entre las clases java y las tablas de MySQL.

Bien, hasta aquí hemos cubierto lo que yo llamo las necesidades básicas de infraestructura para que el framework funcione. A continuación haremos un prueba básica para corroborar el correcto funcionamiento.

Beans que requieren persistencia

Persistiremos dos beans, uno que representa un Cliente y el otro que representa una Zona a la cual el Cliente pertenece. Las tablas que almacenan a los clientes y zonas ya existen en la base de datos que estamos utilizando, el modelo relacional de esas dos tablas es:


La clase que representa al cliente (Cliente.java) tiene el siguiente código:

package ar.com.magm.model;

public class Cliente {
  private String cliente;
  private boolean cuentaHabilitada;
  private int idCliente;
  private Zona zona;

  public String getCliente() {
    return cliente;
  }
  public int getIdCliente() {
    return idCliente;
  }
  public Zona getZona() {
    return zona;
  }
  public boolean isCuentaHabilitada() {
    return cuentaHabilitada;
  }
  public void setCliente(String cliente) {
    this.cliente = cliente;
  }
  public void setCuentaHabilitada(boolean cuentaHabilitada) {
    this.cuentaHabilitada = cuentaHabilitada;
  }
  public void setIdCliente(int idCliente) {
    this.idCliente = idCliente;
  }
  public void setZona(Zona zona) {
    this.zona = zona;
  }
}
La clase que representa la zona (Zona.java) tiene el siguiente código:

package ar.com.magm.model;

public class Zona {
  private int idZona;
  private String zona;

  public int getIdZona() {
    return idZona;
  }
  public String getZona() {
    return zona;
  }
  public void setIdZona(int idZona) {
    this.idZona = idZona;
  }
  public void setZona(String zona) {
    this.zona = zona;
  }
}

Archivos de mapeo de los beans

Es necesario "decirle" a Hibernate cuales son las correspondencias entre las clases Java y las tablas relacionales, Hibernate permite hacerlo con anotaciones o con archivos de configuración XML, he optado por los archivos XML.

Ambos archivos se alojan en el mismo paquete que las clases y son:

ar/com/magm/model/Cliente.hbm.xml


<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="ar.com.magm.model.Cliente" table="clientes">
    <id column="idCliente" name="idCliente" type="integer" />
    <property name="cliente" />
    <property name="cuentaHabilitada" />

    <many-to-one name="zona" class="ar.com.magm.model.Zona" column="idZona"/>
  </class>
</hibernate-mapping>


el elemento
<class name="ar.com.magm.model.Cliente" table="clientes">
Mapea la clase ar.com.magm.model.Cliente con la tabla clientes

el elemento<id column="idCliente" name="idCliente" type="integer" />
Define que la propiedad idCliente de la clase es la clave principal y que se corresponde con la columna idCliente y es de tipo entero.

los elementos
<property name="cliente" />
<property name="cuentaHabilitada" />
mapean los atributos simples cliente y cuentaHabilitada, a los tipos los calcula automáticamente Hibernate
el elemento<many-to-one name="zona" class="ar.com.magm.model.Zona" column="idZona"/>
define la propiedad compleja zona y la clave foránea que se debe utilizar para unir la tabla clientes con la tabla zonas que es idZona


ar/com/magm/model/Zona.hbm.xml


<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="ar.com.magm.model.Zona" table="zonas">
    <id column="idZona" name="idZona" type="integer" />
    <property name="zona"/>
  </class>
</hibernate-mapping>


No explicaré este archivo, ya que la explicación anterior es suficiente para entenderlo.


Nuevamente el archivo de configuración principal de Hibernate

Modificaremos ahora el archivo hibernate.cfg.xml agregando las siguientes líneas:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="connection.datasource">java:/comp/env/jdbc/practico</property>
    <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>
    <property name="hibernate.hbm2ddl.auto">update</property>

    <mapping resource="ar/com/magm/model/Cliente.hbm.xml" />
    <mapping resource="ar/com/magm/model/Zona.hbm.xml" />

  </session-factory>
</hibernate-configuration>


Ambos elementos le icen"a Hibernate cuales son los archivos de mapeos que debe tener en cuenta.


Un Servlet de prueba

Crearemos ahora un Servlet con el fin de probar el correcto funcionamiento de Hibernate y los mapeos que hemos definido, la clase tiene el siguiente código:


package ar.com.magm.web.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

import ar.com.magm.model.Cliente;

@WebServlet("/TestHibernate")
public class TestHibernate extends HttpServlet {
  private Session session;

  public TestHibernate() {
    Configuration configuration = new Configuration();
    configuration.configure();
    ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
      .applySettings(configuration.getProperties())
      .buildServiceRegistry();
    SessionFactory sf = configuration.buildSessionFactory(serviceRegistry);
    session = sf.openSession();
  }

  protected void doGet(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    int idCliente = 33;
    Cliente cl = (Cliente) session.get(Cliente.class, idCliente);
    StringBuilder str = new StringBuilder();
    if (cl != null) {
      str.append("Cliente: " + cl.getCliente() + " (Id: "
        + cl.getIdCliente() + ") - Cta Habilitada: "
        + cl.isCuentaHabilitada() + "\n");
      str.append("\tZona: " + cl.getZona().getZona() + " (Id: "
        + cl.getZona().getIdZona() + ")");
    } else {
      str.append("No existe el cliente con id=" + idCliente);
    }
    response.getWriter().print(str);
    response.getWriter().flush();
  }
}

En el constructor obtenemos una sesión, la cual nos permitirá realizar operaciones con Hibernate.
Luego en el método doGet(), ejecutamos:
Cliente cl = (Cliente) session.get(Cliente.class, idCliente);
para que Hibernate obtenga los datos de la base de datos, instancie una clase Cliente y establezca los valores para todos los atributos que hemos mapeado.
El resto del código es trivial, por ello omitiré cualquier comentario.

Ejecutando el servlet

Ahora vamos a nuestro navegador y con la url que ya hemos utilizado colocamos los datos de login:

Presionamos el botón "Ingreso" y cuando nos encontremos en la pantalla principal:


Cambiamos la url por: http://localhost:8080/pf/TestHibernate, presionamos enter y veremos un resultado similar al siguiente:


Con esto hemos comprobado el correcto funcionamiento del framework.

Recomiendo ver la salida de la vista "Consola" de eclipse, en la cual se podrán apreciar las sentencias SQL que ha emitido Hibernate para obtener los datos relacionales con los cuales se crearon las instancias de las clases Cliente y Zona.

En mi caso es la siguiente:

Hibernate: 
    select
        cliente0_.idCliente as idCliente0_0_,
        cliente0_.cliente as cliente0_0_,
        cliente0_.cuentaHabilitada as cuentaHa3_0_0_,
        cliente0_.idZona as idZona0_0_ 
    from
        clientes cliente0_ 
    where
        cliente0_.idCliente=?
Hibernate: 
    select
        zona0_.idZona as idZona1_0_,
        zona0_.zona as zona1_0_ 
    from
        zonas zona0_ 
    where
        zona0_.idZona=?


Bien, eso es todo por ahora.

Como siempre el proyecto completo y el WAR en el repo de GITHUB: https://github.com/magm3333/workspace-pftuto

Espero les sea útil.

Tutorial anterior: http://jmagm.blogspot.com/2013/03/internacionalizando-nuestra-aplicacion.html
Próximo tutorial: http://jmagm.blogspot.com/2013/04/mejorando-el-uso-de-hibernate-en.html

Saludos

Mariano


Leer datos de XML y escribirlos en un archivo tabulado CSV con Kettle

Hola Gente,

tengo algo armado hace un tiempo y a raíz de alguna pregunta en redopenbi lo publico aquí.
Se trata de leer un archivo XML y convertirlo en un archivo plano separado por comas utilizando Pentaho Data Integration (PDI) o Kettle.

Introducción

PDI siempre tratará de convertir las fuentes de entrada en filas y columnas, con este principio hay que partir siempre que uno intente hacer algo con este software.

El XML en un tipo de datos jerárquico, por ello habrá que transformarlo a dato tabular. Para manipular XML se utiliza XQuery y XPath como para manipular datos relacionales utilizamos SQL, cualquiera que quiera trabajar con este tipo de datos debe interiorizarse con estos conceptos.


Objetivo

Nuestro objetivo es leer el archivo entrada.txt, obtener los datos que deseamos y escribirlos en un archivo tabular separado por comas llamado salidaCSVFromXML.txt



El archivo entrada.xml contiene:



<?xml version="1.0" encoding="UTF-8"?>
<raiz> 
  <dato id="1">dato 1 
    <otroDato atributo="ab">otro dato 1</otroDato> 
  </dato>  
  <dato id="2">dato 2 
    <otroDato atributo="abc">otro dato 2</otroDato> 
  </dato>  
  <dato id="3">dato 3 
    <otroDato atributo="abcd">otro dato 3</otroDato> 
  </dato>  
  <dato id="4">dato 4 
    <otroDato atributo="abcde">otro dato 4</otroDato> 
  </dato> 
</raiz>



El archivo salidaCSVFromXML.txt contendrá:


datoCol;idCol;otroDatoCol;atributoCol
dato 1; 1;otro dato 1;ab
dato 2; 2;otro dato 2;abc
dato 3; 3;otro dato 3;abcd
dato 4; 4;otro dato 4;abcde

El mapeo está claro y es:

Texto del nodo /raiz/dato a columna datoCol
Valor del atributo id del nodo /raiz/dato a columna idCol

Texto del nodo /raiz/dato/otroDato a columna otroDatoCol
Valor del atributo atributo del nodo /raiz/dato/otroDato a columna atributoCol


Manos a la obra

Abrimos Kettle utilizando el comando spoon.sh o spoon.bat según nuestro sistema operativo.
Creamos una nueva transformación y arrastramos al editor los pasos Entrada\"Obtener datos XML" y a la derecha Salida\"Salida Fichero de Texto", luego unimos ambos pasos y editamos la entrada XML haciendo doble click.

En la primera pestaña configuramos:



En la segunda pestaña debemos configurar cual será el nodo a ciclar, cada ciclo creará una nueva fila. En nuestro caso /raiz/nodo, en cada ciclo se asume que se trabaja a partir de este nodo. La cadena /raiz/nodo es una instrucción XPath.



Para darnos una idea de que implica esto, podemos utilizar uno de los tantos testers XPath online que existen, por ejemplo este http://www.mizar.dk/XPath/Default.aspx

Si colocamos:


Y presionamos el botón "Test XPath", obtenemos el resultado con los nodos seleccionados por la expresión XPath en color:


Recomiendo utilizar estas herramientas siempre que haya que evaluar expresiones XPath.

Volvamos a Kettle y configuremos la pestaña "Campos" de la siguiente manera:


Notemos que lo que colocamos en la columna XPath es relativo a lo que definimos en la pestaña  anterior, por ejemplo la tercera fila que define la columna otroDatoCol, tiene la expresión XPath otroDato/text(), pero la expresión completa es /raiz/dato/otroDato/text(), podemos ver la salida en el tester online con esta expresión completa:



Ahora podemos presionar el botón "Previsualizar filas" en Kettle y obtendremos:


Ahora cerramos el editor del paso presionando el botón "OK" y editamos el paso de salida de texto haciendo doble clic en el, en la primera pestaña configuramos lo siguiente:


Luego en la pestaña "Campos" presionamos el botón "Traer Campos" y quedará como sigue:



Cerramos este paso presionando el botón "OK" y listo, ya lo podemos ejecutar.

He subido a mi repo de github la trasnformación y el archivo de entrada de ejemplo: https://github.com/magm3333/material-osbi/tree/master/XML2CSV

Espero les sea de utilidad

Saludos

Mariano

martes, marzo 26, 2013

Internacionalizando nuestra aplicación PrimeFaces/JSF/Spring

Hola gente,

intentaremos dar un paso más en la construcción de la aplicación PrimeFaces/JSF/Spring, en este caso agregaremos la característica de Localización (I10N) mediante el agregado de componentes de Internacionalización (I18N).

Introducción

Según wikipedia "La internacionalización es el proceso de diseñar software de manera tal que pueda adaptarse a diferentes idiomas y regiones sin la necesidad de realizar cambios de ingeniería ni en el código. La localización es el proceso de adaptar el software para una región específica mediante la adición de componentes específicos de un locale y la traducción de los textos, por lo que también se le puede denominar regionalización. No obstante la traducción literal del inglés es la más extendida.

En informática, un locale es un conjunto de parámetros que define el idiomapaís y cualquier otra preferencia especial que el usuario desee ver en su interfaz de usuario.
Generalmente un identificador de locale consiste como mínimo de un identificador de idioma y un identificador de región. Este concepto es de fundamental importancia en el campo de la localización de idiomas."

Esto parece complejo, pero es sencillo de implementar con JSF.

Manos a la obra!

Archivos de recursos con mensajes

Los archivos de recursos con mensajes son archivos de texto plano que contienen una serie de claves y cada clave contiene un valor asociado, los valores serán las cadenas con mensajes internacionalizados. Luego estos archivos son transformados en objetos java.util.Map para su fácil manipulación.

Crearemos dos archivos de recursos en el paquete ar.com.magm.recursos, los recursos son simples archivos planos, se pueden crear haciendo botón derecho sobre el paquete y seleccionando New > Other... / General / File

Los archivos a crear y sus respectivos contenidos son:

mensajes.properties


lbl.login=Ingreso
lbl.username=Usuario
lbl.password=Clave
lbl.welcome=Bienvenid@
lbl.error.login=Error en el ingreso
lbl.invalidcredentials=Credenciales inválidas

lbl.ventas=Ventas
lbl.gauge=Tacómetro
lbl.logout=Salir
lbl.all.m=Todos
lbl.all.f=Todas
lbl.months=Enero,Febrero,Marzo,Abril,Mayo,Junio,Julio,Agosto,Septiembre,Octubre,Noviembre,Diciembre

lbl.table.sales.empty=No hay ventas con este criterio de filtrado
lbl.find.all=Buscar en todos
lbl.col.zone=Zona
lbl.col.customer=Cliente
lbl.col.year=Año
lbl.col.month=Mes
lbl.col.salesamount=Importe Ventas

lbl.gauge.title=Tacómetro Personalizado


mensajes_en.properties


lbl.login=Login
lbl.username=User
lbl.password=Password
lbl.welcome=Welcome
lbl.error.login=Login error
lbl.invalidcredentials=Invalid credentials

lbl.ventas=Sales
lbl.gauge=Gauge
lbl.logout=Logout
lbl.all.m=All
lbl.all.f=All
lbl.months=January,February,March,April,May,June,July,August,September,October,November,December

lbl.table.sales.empty=There are no sales for this filter criteria
lbl.find.all=Find all
lbl.col.zone=Zone
lbl.col.customer=Customer
lbl.col.year=Year
lbl.col.month=Month
lbl.col.salesamount=Sales amount

lbl.gauge.title=Custom Gauge



Tres cosas son fáciles de notar con solo ver el contenido y nombre de los archivos, una es que el nombre del archivo contiene como parte de el el locale, en el caso de mensajes_en.properties, el locale es en, si por ejemplo necesitásemos crear un archivo con información de localización para Francia, el archivo debería llamarse mensajes_fr.properties. La extensión debe ser .properties, aunque ya no la mencionaremos más. Lo otro que se puede notar es que ambos archivos poseen las mismas claves, solo los valores son diferentes y por último se nota que uno de los archivos no tiene locale, este es el archivo con la localización por defecto o base, en nuestro caso es por Español.

Configuración de archivos de recursos en JSF

Ahora debemos "decirle" a JSF cual es el archivo de recursos base y cual será el nombre del bean que lo representará en tiempo de ejecución, además configuraremos el locale por defecto y los locales disponibles. Para ello debemos editar el archivo WEB-INF/faces-config.xml y agregar:


<?xml version="1.0" encoding="UTF-8"?>

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1">

  <application>
    <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    <locale-config>
      <default-locale>es</default-locale>
      <supported-locale>en</supported-locale>
    </locale-config>
    <resource-bundle>
      <base-name>ar.com.magm.recursos.mensajes</base-name>
      <var>msg</var>
    </resource-bundle>
  </application>
...
...

Creo que el agregado no merece mucha explicación, la estructura XML y los nombres de elemento hablan por si solos. A partir de aquí solo debemos recordar que accederemos al recurso mediante msg, ya que <var>msg</var> y que el recurso es un mapa, por ello haremos cosas como msg['clave'].

Hasta aquí hemos hecho todo lo necesario a nivel de infraestructura para dar soporte a I18N y L10N, ahora debemos recodificar algunos componentes para que esto realmente funcione.

Componentes del lado del cliente

A continuación copiaré los códigos de los archivos a modificar y resaltaré las modificaciones en negrita.

login.xhtml

<html xmlns="http://www.w3c.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body style="text-align:center">
  <p:growl id="mensajes" showDetail="true" life="2000" />
  <h:form>
    <p:panel header="#{msg['lbl.login']}" style="width:300px">
      <h:panelGrid columns="2" cellpadding="5">
        <h:outputLabel for="username" value="#{msg['lbl.username']}:" />
        <p:inputText value="#{loginBean.nombre}" id="username" required="true" label="username" />
        <h:outputLabel for="password" value="#{msg['lbl.password']}:" />
        <p:password value="#{loginBean.clave}" id="password" required="true" label="password" />
        <f:facet name="footer">
          <p:commandButton id="loginButton" value="#{msg['lbl.login']}actionListener="#{loginBean.login}" update=":mensajes" oncomplete="manejarLogin(xhr, status, args)" />
        </f:facet>
      </h:panelGrid>
    </p:panel>
  </h:form>
</h:body>

<script type="text/javascript">
  //<![CDATA[
  function manejarLogin(xhr, status, args) {
    if (!args.validationFailed && args.estaLogeado) {
      setTimeout(function() {
        window.location = args.view;
      }, 1000);
    }
  }
  //]]>
</script>
</html>


menu.xhtml


<html xmlns="http://www.w3c.org/1999/xhtml"

  xmlns:h="http://java.sun.com/jsf/html"

  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
  <p:layout fullPage="true">
    <p:layoutUnit position="center">
      <iframe id="frame" src="ventas.xhtml" style="width: 100%; height: 100%; text-align: center;" seamless='seamless' />
    </p:layoutUnit>
  </p:layout>
  <h:form id="form">
    <p:dock>
      <p:menuitem value="#{msg['lbl.ventas']}" icon="/images/ventas.png" url="javascript:cambioPagina('ventas.xhtml')" />
      <p:menuitem value="#{msg['lbl.gauge']}" icon="/images/gauge.jpg" url="javascript:cambioPagina('gauge.xhtml')" />
      <p:menuitem value="#{msg['lbl.logout']}" icon="/images/logout.png" actionListener="#{loginBean.logout}" oncomplete="logout(xhr, status, args)" />
    </p:dock>
  </h:form>
</h:body>
<script type="text/javascript">
  //<![CDATA[
  var actual = 'ventas.xhtml';
  function cambioPagina(pagina) {
    if (pagina != actual) {
      $('#frame').attr('src', pagina);
      actual=pagina;
    }
  }
  function logout(xhr, status, args) {
    setTimeout(function() {
      window.location = 'login.xhtml';
    }, 500);
  }
  //]]>
</script>
</html>

gauge.xhtml

<html xmlns="http://www.w3c.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
  <h:form id="formGauge">
    <p:poll interval="2" update="gauge" />
    <p:meterGaugeChart id="gauge" value="#{gaugeBean.meterGaugeModel}" showTickLabels="false" labelHeightAdjust="110" intervalOuterRadius="130" seriesColors="66cc66, 93b75f, E7E658, cc6666" style="width:400px;height:250px" title="#{msg['lbl.gauge.title']}" label="km/h" />
  </h:form>
</h:body>
</html>

Fácil no?, como se puede observar solo hay que sustituir la cadena que se desee por la expresión EL de forma: #{msg['clave']}


Componentes del lado del server

Los componentes del lado del server también contienen datos que se muestran en las vistas del usuario y deben ser internacionalizados. A continuación se mostrarán las porciones de código y las clases que hay que modificar.

ar.com.magm.web.primefaces.LoginBean (solo método login())

public void login(ActionEvent actionEvent) {
  RequestContext context = RequestContext.getCurrentInstance();
  FacesContext jsfCtx= FacesContext.getCurrentInstance();
  ResourceBundle bundle = jsfCtx.getApplication().getResourceBundle(jsfCtx, "msg");
  FacesMessage msg = null;
  if (usuarioValido(nombre, clave)) {
    logeado = true;
    msg = new FacesMessage(FacesMessage.SEVERITY_INFO, bundle.getString("lbl.welcome")nombre);
  } else {
    logeado = false;
    msg = new FacesMessage(FacesMessage.SEVERITY_WARN, bundle.getString("lbl.error.login")bundle.getString("lbl.invalidcredentials"));
  }
  FacesContext.getCurrentInstance().addMessage(null, msg);
  context.addCallbackParam("estaLogeado", logeado);
  if (logeado)
    context.addCallbackParam("view", "menu.xhtml");
}

Con las siguientes líneas:
FacesContext jsfCtx= FacesContext.getCurrentInstance();
ResourceBundle bundle = jsfCtx.getApplication().getResourceBundle(jsfCtx, "msg");
Obtenemos una referencia al archivo de recursos en la variable bundle.

Luego para obtener un valor utilizamos: bundle.getString("clave")


ar.com.magm.web.primefaces.VentasBean


public class VentasBean implements Serializable {



  private static final long serialVersionUID = -6690574219803425728L;

  private String[] meses ;

  private String sql = "SELECT year(fecha) as anio, month(fecha) as mes, zona, cliente, sum(importe*cantidad) as ventas FROM dw_ventasfact v INNER JOIN clientes c ON v.idCliente=c.idCliente INNER JOIN zonas z ON z.idZona=c.idZona WHERE cliente like ? GROUP BY zona, cliente, anio, mes ORDER BY anio,mes,zona,cliente";
  private List<Venta> ventas;
  private List<Venta> ventasFiltradas;
  private List<String> zonas;
  private FacesContext jsfCtx;
  private ResourceBundle bundle;

  public VentasBean() {
    jsfCtx = FacesContext.getCurrentInstance();
    bundle = jsfCtx.getApplication().getResourceBundle(jsfCtx, "msg");
    // processList(null);
  }

  public SelectItem[] getMesesOptions() {
    meses=bundle.getString("lbl.months").split(",");
    SelectItem[] r = new SelectItem[13];
    for (int t = 0; t < meses.length; t++)
      r[t + 1] = new SelectItem(meses[t], meses[t]);
    return r;
  }
...
...
  public SelectItem[] getZonasOptions() {
    SelectItem[] r = new SelectItem[zonas.size() + 1];
    r[0] = new SelectItem("", bundle.getString("lbl.all.f"));
    for (int t = 0; t < zonas.size(); t++)
      r[t + 1] = new SelectItem(zonas.get(t), zonas.get(t));
    return r;
  }

  private void processList(Object args[]) {
    meses=bundle.getString("lbl.months").split(",");
    ventas = new ArrayList<Venta>();
    zonas = new ArrayList<String>();
...
...

En el caso de VentasBean solo hemos utilizado la internacionalización para la opción "Todos"/"Todas" de las listas de filtrado y para internacionalizar los meses.

Probando la Aplicación

Para probar la aplicación, solo debemos reconfigurar nuestro brower y cambiar entre los idiomas Español e Inglés, a continuación adjunto un video del funcionamiento de la aplicación internacionalizada.


Como siempre el proyecto completo y el WAR en el repo de GITHUB: https://github.com/magm3333/workspace-pftuto

Espero les sea útil.
Saludos

Mariano

Etiquetas

pentaho (45) java (35) eclipse (23) jdbc (14) curso (13) tomcat (13) primefaces (12) db2 (11) mondrian (10) review (10) jsf (9) openI (9) pdi (9) prd (9) libro (8) plugin (8) musql (7) struts (7) javascript (6) spring (6) cdf (5) ctools (5) instalar (5) linux (5) mysql (5) data studio (4) hibernate (4) ireport (4) jasper (4) meteor (4) videocurso (4) eglu (3) eglubi (3) elearning (3) graphite (3) grupo eglu (3) jboos tools (3) mexico (3) openbits (3) packt (3) python (3) undec (3) websphere (3) applet (2) cde (2) dao (2) db2university (2) exelearning (2) flexigrid (2) hadoop (2) iua (2) kettle (2) moodle (2) node (2) olap (2) osbi (2) pivot4j (2) scorm (2) sql (2) stpivot (2) actionscript (1) amazon (1) autenticacion (1) avanzado (1) base de datos (1) big data (1) bigdata (1) bodoc (1) cambiar (1) ccc (1) cdc (1) chat (1) cloud (1) coffeescript (1) control de acceso (1) corti (1) csv (1) cuba (1) curso meteor undec (1) dashboard (1) datamart (1) dataptix.tv (1) datasource (1) datatable (1) db2 ExpressC (1) demonio (1) distancia (1) driver (1) driver jdbc (1) eglufiltertwolist (1) encapsulamiento (1) especialización (1) etl (1) excepciones (1) export (1) faces (1) federación (1) filas afectadas (1) filtertwolist (1) filtrado (1) flegrid (1) flex (1) google (1) google viz (1) hostname (1) html (1) i18n (1) ibm (1) identidad (1) indignación (1) instancias (1) inteligencia de negocios (1) jee (1) jpivot (1) l10n (1) la azada (1) la zaga de los confines (1) layout (1) liberado (1) libre (1) libro promoción (1) lob (1) marktplace (1) menu (1) meteor node javascript google oauth autenticacion (1) mobile (1) mongoDB (1) node.js (1) oauth (1) olap4j (1) open source (1) orm (1) persistencia (1) personalizada (1) prd5 (1) psw (1) publicidad (1) rad6 (1) recursividad (1) reporting (1) rock (1) saiku (1) script (1) servicio (1) sessiontimeout (1) sourceforge (1) spinneta (1) sqlserver (1) ssl (1) taller (1) troyanx (1) ubuntu (1) ucc (1) ui (1) web (1) web console (1) xampp (1) xml (1) xpath (1)

Seguidores