Mostrando las entradas con la etiqueta eclipse. Mostrar todas las entradas
Mostrando las entradas con la etiqueta eclipse. Mostrar todas las entradas

viernes, marzo 22, 2013

Integrar Spring en nuestro proyecto JSF/PrimeFaces

Hola Gente,

avancemos un poco más en nuestro proyecto.

En esta ocasión, la idea es incorporar Spring al proyecto JSF/PrimeFaces que venimos desarrollando.

Introducción:
Antes de comenzar diré un par de palabras sobre Spring, comencemos por decir que es uno de los frameworks más extendidos para desarrollo de aplicaciones Java que requieran escalar, fácil mantenimiento y testing entre otras cosas. En este tutorial no me extenderé en teoría acerca de Spring, para ello existen muchos y muy buenos tutoriales y documentos, por ejemplo este.

Objetivo:
Configurar Spring y hacer que maneje nuestros beans, beans que hasta ahora manejaba JSF.

Descargando Spring:
Lo primero que debemos hacer es descargar Spring, se puede descargar de este sitio. Yo he utilizado particularmente la versión 3.2.1 que he descargado desde aquí, aunque en el momento de escribir este tutorial ya se encuentra disponible la versión 3.2.2.

Configurando las librerías en el proyecto:
Una vez que descargamos Spring, debemos descomprimir el archivo y copiar (o linkear) a nuestro proyecto (carpeta WEB-INF/lib) los siguientes archivos:

spring-beans-3.2.1.RELEASE.jar
spring-context-3.2.1.RELEASE.jar
spring-context-support-3.2.1.RELEASE.jar
spring-core-3.2.1.RELEASE.jar
spring-expression-3.2.1.RELEASE.jar
spring-instrument-3.2.1.RELEASE.jar
spring-instrument-tomcat-3.2.1.RELEASE.jar
spring-instrument-tomcat-3.2.1.RELEASE.jar
spring-jdbc-3.2.1.RELEASE.jar
spring-tx-3.2.1.RELEASE.jar
spring-web-3.2.1.RELEASE.jar
spring-webmvc-3.2.1.RELEASE.jar

También debemos copiar la implementación de JSF que hasta ahora la utilizábamos mediante una referencia de librería de usuario:

javax.faces-2.1.17.jar

También necesitamos:

commons-logging-1.1.1.jar

Antes teníamos:

mysql-connector-java-5.1.17.jar
primefaces-3.5.jar

Nos debería quedar algo así:


Archivo de configuración de Spring:Spring no es la excepción, basa su funcionamiento en archivos de configuración, el más importante es el que crearemos a continuación. En la carpeta WEB-INF debemos crear un archivo llamado applicationContext.xml, y el contenido será:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans>

Configuración de web.xml
Ahora debemos decirle a nuestra aplicación web cual es el componente de Spring que procesará el inicio de Spring, entre otras cosas leer el archivo (applicationContext.xml) y crear el contexto y cual procesará los requerimientos http, para ello editamos el archivo WEB-INF/web.xml y agregamos los siguientes listeners:
  ...
  ...
  <display-name>pf</display-name>
  <listener>
     <listener-class>
       org.springframework.web.context.ContextLoaderListener
     </listener-class>
  </listener>
  <listener>
     <listener-class>
       org.springframework.web.context.request.RequestContextListener
     </listener-class>
  </listener>
  <welcome-file-list>
    <welcome-file>login.xhtml</welcome-file>
  </welcome-file-list>
  ...
  ...

Derivación del manejo de beans:
A esta altura ya tenemos funcionando Spring, solo restan dos tareas:

1)  "Decirle" a JSF que Spring manejará los beans. Siempre es una clase la encargada de procesar y resolver las expresiones utilizadas mediante lenguaje de expresiones (EL), JSF tiene una clase asignada por defecto, lo que haremos es cambiar esa clase, para ello editamos el archivo de configuración de JSF WEB-INF/faces-config.xml y  agregamos al inicio:

<?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> 
  </application>
...
...

2) "Mudar"la configuración del manejo de beans desde JSF a Spring, para los cual:
2.a) Editamos el archivo de configuración de JSF (el del punto anterior) WEB-INF/faces-config.xml y comentamos (<!-- -->) las definiciones de beans que teníamos, el archivo quedará así:

<?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> 
  </application>
  <!--  
    <managed-bean>
      <managed-bean-name>gaugeBean</managed-bean-name>
      <managed-bean-class>ar.com.magm.web.primefaces.GaugeBean</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
    </managed-bean>
    <managed-bean>
      <managed-bean-name>ventasBean</managed-bean-name>
      <managed-bean-class>ar.com.magm.web.primefaces.VentasBean</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    <managed-bean>
      <managed-bean-name>loginBean</managed-bean-name>
      <managed-bean-class>ar.com.magm.web.primefaces.LoginBean</managed-bean-class>
      <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
  -->
</faces-config>

2.b) Editamos el archivo de configuración de Spring WEB-INF/applicationContext.xml y agregamos la configuración necesaria, el archivo quedará así:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  <bean id="gaugeBean" class="ar.com.magm.web.primefaces.GaugeBean" scope="singleton"/>
  <bean id="ventasBean" class="ar.com.magm.web.primefaces.VentasBean" scope="session"/>
  <bean id="loginBean" class="ar.com.magm.web.primefaces.LoginBean" scope="session"/>

</beans>

Notemos que para definir cada bean, es necesario solo un elemento XML si usamos Spring. Lo que en JSF definía el nombre o id del bean (<managed-bean-scope>) era un elemento, en el caso de Spring se trata del atributo id, al igual que la clase que implementa el bean que en Spring también es un atributo. En Spring definimos el alcance y ciclo de vida de los beans con el atributo scope, en el caso de session, es igual que lo hacíamos con JSF, en el caso que necesitemos que un bean sea único para toda la aplicación  nuestro scope será ahora singleton.

Bien, eso es todo, si probamos la aplicación, esta seguirá comportándose como antes. En la interface gráfica no hay diferencias.

En breve continuaremos agregando características muy interesantes a nuestro proyecto.

Recuerden que pueden descargarse el war con el proyecto funcionando desde aquí, también pueden sacar del war las librerías (.jar). El proyecto completo sigue estando aquí.

Espero les sea de utilidad.


Saludos

Mariano

miércoles, febrero 20, 2013

Una tabla con datos de una base de datos MySQL con PrimeFaces (Paso a paso)

Hola gente,

una vez más con PrimeFaces, esta vez iremos un poco más allá, crearemos una tabla que mostrará datos de una base de datos MySQL. Además está tabla permitirá ordenar y filtrar y paginar los datos. Un "chiche!".

Como en el tutorial anterior, nos basaremos en el proyecto inicial creado en el post: "Primeros pasos con PrimeFaces, Eclipse y Tomcat (Paso a paso)"

Vamos a comenzar con una modificando el Filtro (LoginFilter.java) y nos aseguramos que la nueva versión contenga las líneas que muestro en negrita a continuación:

private boolean noProteger(String urlStr) {
  /*
  * Este es un buen lugar para colocar y programar todos los patrones que
  * creamos convenientes para determinar cuales de los recursos no
  * requieren protección. Sin duda que habría que crear un mecanizmo tal
  * que se obtengan de un archivo de configuración o algo que no requiera
  * compilación.
  */
  if (urlStr.indexOf("/login.xhtml")!= -1)
    return true;

  if (urlStr.indexOf("/javax.faces.resource/") != -1)
    return true;
  return false;
}


Luego en la clase LoginBean

  FacesContext.getCurrentInstance().addMessage(null, msg);
  context.addCallbackParam("estaLogeado", logeado);
  if (logeado)
    context.addCallbackParam("view", "ventas.xhtml");
}


A raíz de esta modificación, ya estoy arrepentido de haber hecho el post anterior ya que la intención fue dar una idea genérica de como se hace para proteger recursos mediante algún sistema de control de acceso con artefactos Web Java, pero por simplista he caído en lo burdo, a este sistema le falta mucho y las falencias son grandes y es debido a que no se trata de un tema trivial, veré si en el futuro me redimo y vemos algo un poco más adecuado a la realidad, por ejemplo Spring Security, en fin ya veremos las ganas y el tiempo que son dos cosas que no regalan ni se compran.

Bien hecha la mea culpa comencemos.

Creando un DataSource accesible mediante JNDI

Para definir un recurso de este tipo, solo debemos crear un archivo xml llamada "context.xml" en la carpeta WebContent/META-INF cuyo contenido debe ser:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Resource auth="Container" description="BD Practico" 

     name="jdbc/practico" type="javax.sql.DataSource" 
     password="xxxxx" driverClassName="com.mysql.jdbc.Driver"
     maxIdle="2" maxWait="5000" validationQuery="select 1"
     username="root" url="jdbc:mysql://localhost:3306/practico" maxActive="4" />
  <WatchedResource>WEB-INF/web.xml</WatchedResource>
  <WatchedResource>META-INF/context.xml</WatchedResource>
</Context>

Sin entrar en mucho detalle acerca de la definición del pool de conexiones, solo diremos que los datos principales que contiene este archivo son aquellos referidos a los que requiere el driver JDBC los cuales he resaltado en negrita, un atributo fundamental es el nombre de recurso que estamos creado que es "jdbc/practico" y colocar la password correcta.
Una vez que disponemos de este archivo debemos colocar en el classpath el driver JDBC de MySQL que pueden descargar desde aquí: http://cdn.mysql.com/Downloads/Connector-J/mysql-connector-java-5.1.23.zip, una vez que descarguen este archivo deben descomprimir solo mysql-connector-java-5.1.23-bin.jar que se encuentra dentro del zip y copiarlo o linkearlo (como ya hemos visto) dentro de la carpeta WebContent/WEB-INF/lib

Obteniendo una conexión del pool y alacenandola en el contexto global de la aplicación

Ya hemos realizado la configuración necesaria para que Tomcat cree un pool de conexiones al iniciar, es nuestro trabajo ahora obtener una referencia a ese pool y pedirle una conexión a la base de datos para ser utilizada en la aplicación. Es buena práctica hacer esto una sola vez y al inicio de la aplicación. Afortunadamente la especificación web de java nos provee de una serie de herramientas para poder hacerlo, en este caso haremos uso de los métodos callback de control de ciclo de vida, también se los llama listeners, usaremos particularmente el listener a nivel de contexto, el cual posee dos métodos que son llamados por el contenedor (tomcat) cuando se inicia y cuando finaliza.

Crear un listener es muy sencillo, se trata de una clase java que debe implementar para este caso la interface ServletContextListener y estar marcado con la anotación @WebListener, el código se muestra a continuación:

package ar.com.magm.web.listeners;

import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.sql.DataSource;

@WebListener
public class InitListener implements ServletContextListener {
  public InitListener() { }

  public void contextInitialized(ServletContextEvent sce) {
    DataSource fuenteDatos = null;
    Context ctx;
    try {
      ServletContext sc = sce.getServletContext();
      ctx = new InitialContext();

      fuenteDatos = (DataSource) ctx.lookup("java:comp/env/jdbc/practico");

      Connection cn = fuenteDatos.getConnection();

      sc.setAttribute("datasource", cn);

    } catch (NamingException e) {
      throw new RuntimeException(e.getMessage());
    } catch (SQLException e) {
      throw new RuntimeException(e.getMessage());
    }
  }


  public void contextDestroyed(ServletContextEvent sce) { }
}

En las líneas:
ServletContext sc = sce.getServletContext();
ctx = new InitialContext();
fuenteDatos = (DataSource) ctx.lookup("java:comp/env/jdbc/practico");

Obtenemos una referencia al recurso mediante JNDI y el nombre que establecimos en el archivo de configuración.

La línea:
Connection cn = fuenteDatos.getConnection();
Obtiene la conexión.

La línea: 
sc.setAttribute("datasource", cn);
almacena en el contexto global de la aplicación la instancia de la conexión con el nombre datasource.

Una vez que contamos con este listener es muy sencillo acceder desde la aplicación a esta conexión.

Bien hasta aquí no hemos tocado un tema importante, a que base de datos nos conectamos, pues bien es una base de datos que he utilizado desde hace años para diversos ejemplos y la pueden descargar desde aquí, el archivo es BDDump.sql y se encuentra dentro de archivos.zip. Una vez descargado el zip y descomprimido el archivo .sql, y desde una consola escribimos:

$ mysql --user=UnUsuarioVálido --password=LaClave < BDDump.sql

Esto creará la base de datos practico en la instancia de MySQL.

Creando la vista del usuario

Ahora crearemos la vista del usuario, se trata como ya hemos visto antes de un archivo .xhtml, en este caso "ventas.xhtml", en este archivo crearemos una tabla en la cual mostraremos información de ventas que saldrán de la siguiente consulta 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 
GROUP BY 
  zona, 
  cliente, 
  anio, 
  mes 
ORDER BY 
  anio,
  mes,
  zona,
  cliente


Para nuestro caso una venta tiene los atributos que se muestran en la imagen, o sea: año, mes, zona, cliente e importe de venta. Para representar cada venta (cada hecho), o cada fila que retorne la consulta, crearemos una clase java.
Ahora veremos el código de la vista, o sea del archivo "ventas.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">
  <h:form>


    <p:dataTable id="tablaDeDatos" var="venta"
      value="#{ventasBean.ventas}" widgetVar="tablaDeVentas"
      emptyMessage="No hay ventas con este criterio de filtrado"
      filteredValue="#{ventasBean.ventasFiltradas}" paginator="true"
      rows="10" style="width:800px">

      <f:facet name="header">
        <p:outputPanel>
          <h:outputText value="Buscar en todos:" />
          <p:inputText id="globalFilter" onkeyup="tablaDeVentas.filter()" 
style="width:150px" />

        </p:outputPanel>
      </f:facet>

      <p:column id="zonaCol" filterBy="#{venta.zona}" headerText="Zona"
        filterMatchMode="exact" filterOptions="#{ventasBean.zonasOptions}">
        <h:outputText value="#{venta.zona}" />
      </p:column>

      <p:column id="clienteCol" filterBy="#{venta.cliente}"
        headerText="Cliente" filterMatchMode="startsWith">
        <h:outputText value="#{venta.cliente}" />
      </p:column>


      <p:column id="anioCol" filterBy="#{venta.anio}" headerText="Año"
        filterMatchMode="startsWith">
        <h:outputText value="#{venta.anio}" />
      </p:column>

      <p:column id="mesCol" filterBy="#{venta.mesLetra}" headerText="Mes"
        filterMatchMode="exact" filterOptions="#{ventasBean.mesesOptions}">
        <h:outputText value="#{venta.mesLetra}" />
      </p:column>

      <p:column id="ventasCol" headerText="Importe Venta" style="text-align:right">
        <h:outputText value="#{venta.ventaFormat}"/>
      </p:column>

    </p:dataTable>
  </h:form>

</h:body>
</html>

El resultado final se verá así:


Bien, ese es todo el código de la vista del cliente, la analizaremos por partes:

Este tag:
    <p:dataTable id="tablaDeDatos" var="venta"
      value="#{ventasBean.ventas}" widgetVar="tablaDeVentas"
      emptyMessage="No hay ventas con este criterio de filtrado"
      filteredValue="#{ventasBean.ventasFiltradas}" paginator="true"
      rows="10" style="width:800px">
define la tabla de datos que se llama "tablaDeDatos" (id="tablaDeDatos"), los valores que se mostrarán en la tabla se obtendrán de: ventasBean.ventas (value="#{ventasBean.ventas}") que es una lista de instancias de Venta que es la clase java que representa solo una venta y que aún no hemos creado, por cada instancia que se recorra de la lista se crea una variable llamada venta (var="venta"). La lista que se encuentra en ventasBean.ventas contiene todas las ventas que obtenemos de la consulta SQL, este componente permite que se apliuen filtros sobre esa lista y lo implementa utilizando una segunda lista, esta es: ventasBean.ventasFiltradas (filteredValue="#{ventasBean.ventasFiltradas}") lo bueno de esto es que no tenemos que preocuparnos mas que por definir esta lista en el server, PrimeFaces la mantiene por nosotros. La lista se paginará automáticamente y mostrará 10 filas por página, el ancho de la lista será de 800 píxeles (paginator="true" rows="10" style="width:800px"). También se podrá realizar una búsqueda global (en cualquier columna), está búsqueda y filtrado se produce en el cliente, por ello debemos definir el nombre que tendrá el elemento html (el widget) en el cliente y este será: tablaDeVentas (value="#{ventasBean.ventas}"). Por último diremos que si no existen items ante algún criterio de búsqueda se mostrará el mensaje: "No hay ventas con este criterio de filtrado" (emptyMessage="No hay ventas con este criterio de filtrado").

El filtro que mencioné en el último párrafo se fine con el tag:
 <p:inputText id="globalFilter" onkeyup="tablaDeVentas.filter()"   
   style="width:150px" />
El widget tablaDeVentas posee automáticamente un método llamada filter(), al cual se llamará cada vez que finalice la presión de una tecla (onkeyup), el método de filtrado descartará todas las filas en las cuales no se encuentre en algún lugar el valor que escribamos en el campo de entrada con id="globalFilter", esto es siempre así, el id debe ser ese y ningún otro, esto la verdad que no me gusta demasiado, pero en fin...

Ahora solo resta definir las columnas que mostraremos en esta tabla de datos.

La columna que muestra la zona se define el el tag:

<p:column id="zonaCol" filterBy="#{venta.zona}" headerText="Zona"
  filterMatchMode="exact" filterOptions="#{ventasBean.zonasOptions}">
  <h:outputText value="#{venta.zona}" />
</p:column>

Para todas las columnas definiremos un id, en este caso id="zonaCol", también el texto que se mostrará en la cabecera, en este caso: headerText="Zona", también definiemos cual es el componente que renderizará el valor en cada celda, en este caso lo hacemos con el tag:
<h:outputText value="#{venta.zona}" />
donde podemos ver que se utiliza el atributo zona del bean venta, recordemos que este bean lo definimos cuando definimos la tabla (var="venta"). 
No entraré en detalle sobre estos atributos para las próximas columnas.

Particularmente para la columna zona definimos un filtro que será una lista desplegable que contiene todas las zonas posibles, a esto lo hacemos en: filterOptions="#{ventasBean.zonasOptions}", el bean ventasBean poseerá una lista especial llamada zonasOptions que otorgará la lista de zonas que mencionamos. Además definimos que cada vez que se selccione una zona se realice una búsqueda por exactamente igual en: filterMatchMode="exact". El filtro en acción se ve así:


La columna que muestra el cliente se define el el tag:

<p:column id="clienteCol" filterBy="#{venta.cliente}"
  headerText="Cliente" filterMatchMode="startsWith">
  <h:outputText value="#{venta.cliente}" />
</p:column>

La diferencia más grande (amén del valor que se muestra) es el filtro, solo hemos definido que se buscará todos aquellos valores que comiencen con: filterMatchMode="startsWith", si no se define otra cosa se utiliza un campo de texto en el cual debemos escribir el criterio de filtrado como se ve en la siguiente imagen:


El resto de las columnas están definidas utilizando conceptos que hemos explicado ya, por ello omitiré explicaciones al respecto.

El bean que representa una venta.

Como ya he comentado antes, crearemos una clase java que representará cada venta y que instanciaremos en el bean que posee las listas de ventas más adelante.

La clase se llama: ar.com.magm.model.Venta y el código es:

package ar.com.magm.model;

import java.io.Serializable;
import java.text.DecimalFormat;

public class Venta implements Serializable {
  private static final long serialVersionUID = 8060348552656940209L;

  public static long getSerialversionuid() {
    return serialVersionUID;
  }

  private int anio;
  private String cliente;
  private int mes;
  private String mesLetra;
  private double venta;
  private String zona;

  public Venta(String zona, String cliente, int anio, int mes,
               String mesLetra, double venta) {
    super();
    this.zona = zona;
    this.cliente = cliente;
    this.anio = anio;
    this.mes = mes;
    this.mesLetra = mesLetra;
    this.venta = venta;
  }

  public int getAnio() {
    return anio;
  }
  public String getCliente() {
    return cliente;
  }
  public int getMes() {
    return mes;
  }
  public String getMesLetra() {
    return mesLetra;
  }
  public double getVenta() {
    return venta;
  }

  public String getVentaFormat() {
    DecimalFormat df = new DecimalFormat("0.00");
    return df.format(venta);
  }

  public String getZona() {
    return zona;
  }
}

No hay mucho que decir de este bean, solo que tenemos un par de getters especiales, por un lago uno que nos retorna el nombre del mes getMesLetra() y el importe formateado getVentaFormat().

El bean manejado por faces que mantiene las listas de ventas.

Bien, es hora de crear el controlador que es una clase llamada ar.com.magm.web.primefaces.VentasBean y cuya configuración en faces-config.xml es: 

<managed-bean>
  <managed-bean-name>ventasBean</managed-bean-name>
  <managed-bean-class>ar.com.magm.web.primefaces.VentasBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

Notemos que el scope de este bean es session, si la lista fuese común a todos los usuarios, el scope correcto sería application ya que no sería necesario crear más instancias. En algún post futuro haremos uso de esto.

El código completo del bean es el siguiente:

package ar.com.magm.web.primefaces;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.servlet.ServletContext;
import ar.com.magm.model.Venta;

public class VentasBean implements Serializable {
  private static final long serialVersionUID = -6690574219803425728L;

  private String[] meses = new String[] { "Enero", "Febrero", "Marzo",
    "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre",
    "Octubre", "Noviembre", "Diciembre" };

  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 GROUP BY zona, cliente, anio, mes ORDER BY anio,mes,zona,cliente";
  private List<Venta> ventas;
  private List<Venta> ventasFiltradas;
  private List<String> zonas;

  public VentasBean() {
    processList(null);
  }

  public SelectItem[] getMesesOptions() {
    SelectItem[] r = new SelectItem[13];
    r[0] = new SelectItem("", "Todos");
    for (int t = 0; t < meses.length; t++)
      r[t + 1] = new SelectItem(meses[t], meses[t]);
    return r;
  }

  public List<Venta> getVentas() {
    return ventas;
  }

  public List<Venta> getVentasFiltradas() {
    return ventasFiltradas;
  }

  public SelectItem[] getZonasOptions() {
    SelectItem[] r = new SelectItem[zonas.size() + 1];
    r[0] = new SelectItem("", "Todas");
    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[]) {
    ventas = new ArrayList<Venta>();
    zonas = new ArrayList<String>();
    ServletContext sc = (ServletContext) FacesContext.getCurrentInstance()
                                        .getExternalContext().getContext();
    Connection cn = (Connection) sc.getAttribute("datasource");
    try {
      PreparedStatement pst = cn.prepareStatement(sql);
      if (args != null) {
        for (int t = 0; t < args.length; t++) {
          pst.setObject(t + 1, args[t]);
        }
      }
      ResultSet rs = pst.executeQuery();
      while (rs.next()) {
        String zona = rs.getString("zona");
        Venta venta = new Venta(zona, rs.getString("cliente"),
                                rs.getInt("anio"), rs.getInt("mes"),
                                meses[rs.getInt("mes") - 1], 
                                rs.getDouble("ventas"));
        ventas.add(venta);
        if (!zonas.contains(zona))
          zonas.add(zona);
      }
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

  public void setVentasFiltradas(List<Venta> ventasFiltradas) {
    this.ventasFiltradas = ventasFiltradas;
  }
}

Como pueden ver en el constructor se llama al método que llena la lista de ventas (List<Venta> ventasprocessList(), lo hace en base a los datos obtenidos de la consulta sql además llena una lista con las zonas (List<String> zonas). También es necesario mencionar como obtenemos la conexión que almacenamos en el contexto general, primero obtenemos el contexto:
ServletContext sc = (ServletContext) FacesContext.getCurrentInstance()
                                     .getExternalContext().getContext();
Luego usamos el contexto para obtener la conexión:
Connection cn = (Connection) sc.getAttribute("datasource");

La lista List<Venta> ventasFiltrada junto a los métodos setVentasFiltradas() y getVentasFiltradas() es lo único que necesitamos para que los filtros se procesen correctamente del lado del server.

El método getVentas() es el fundamental, el que provee los datos iniciales de la lista de ventas y el que se utiliza para generar las listas filtradas.

Por último los métodos getMesesOptions() y getZonasOptions() retornan un arreglo de objetos SelectItem que utilizará el componente tabla para renderizar los filtros de las columnas de zona y mes.

Bien, esto es todo por ahora, recuerden que disponen del proyecto eclipse completo en: https://github.com/magm3333/workspace-pftuto
Saludos

Mariano





martes, febrero 12, 2013

Primeros pasos con PrimeFaces, Eclipse y Tomcat (Paso a paso)

Hola Gente,

en este post vamos a pegar un vistazo a PrimeFaces.



Que es PrimeFaces?

Se trata de una suite open source de componentes que extiende a JSF


  • Posee un gran set de componentes UI (HtmlEditor, Dialog, AutoComplete, Charts, DatePicker, DataTables, TreeComponent, etc).
  • Tecnología Ajax basada an la API Ajax JSF 2.0
  • Muy liviana
  • Solo un archivo jar
  • No se requiere configuración
  • No tiene dependencias (salvo por supuesto JSF runtime 2.0 o 2.1)
  • Soporta Ajax Push via websockets
  • Mobile UI kit que permite la creación de aplicaciones web para dispositivos mobiles
  • Soporta Skinning y posee más de 35 temas preconstruídos. Soporta el editor visial de temas.
  • Documentación mantenida por la comunidad muy actualizada.
  • Muchos más
Se puede descargar la documentación en formato pdf desde aquí.

Se puede ver una demo llamada showcase que abarca todos los componentes de la UI y mobile desde aquí, además showcase permite ver el código fuente necesario para cada uno de las demostraciones.

Dependencias opcionales:

Que haremos?

Vamos a crear un proyecto web dinámico utilizando Eclipse 4.3 Kepler con WTP en el cual habilitaremos JSF y PrimeFaces. El proyecto y el post no son muy pretenciosos, solo veremos como implementar algunos componentes para que quede la idea de la facilidad con la que se trabaja utilizando esta suite.


Prerequisitos:
  • Tener instalado JRE 1.6 o posterior. 
  • Eclipse 4.3 Kepler, el cual puede descargarse desde aquí. En la página que se mostrará hay que seleccionar el sistema operativo, la distribución, en nuestro caso "Eclipse IDE for Java EE Developers" y la plataforma (32 o 64 bits). Una vez descargado solo hay que descomprimir el archivo y se creará una carpeta llamada eclipse, solo debemos entrar en esa carpeta y ejecutar el comando eclipse
  • Apache Tomcat 7.0.35, que puede descargarse desde esta página: http://tomcat.apache.org/download-70.cgi#7.0.35, una vez allí navegamos hasta la sección "Binary Distributions" se seleccionamos la distribución para nuestro SO. Recomiendo (zip para Window$ o tar.gz para Linux) a fin de no hacer más compleja la instalación. Una vez descargado el archivo lo descomprimimos en un carpeta, vemos que dentro se creará otra carpeta llamada "apache-tomcat-7.0.35" a la cual llamaremos [TOMCAT_HOME]
  • JSF 2.1.17 que se puede descargar desde aquí. Una vez descargado el archivo lo copiamos en una carpeta cualquiera la que llamaremos [LIB_HOME]
  • PrimeFaces 3.5 que se puede descargar desde aquí. Una vez descargado el archivo lo copiamos en [LIB_HOME].

Creando el proyecto.

Una vez que iniciamos eclipse y determinamos cual es nuestro workspace (recomiendo crear uno nuevo en el caso de tener uno ya), presionamos Ctrl+N para acceder al asistente de creación de nuevo proyecto y seleccionamos "Dynamic Web Proyect" y presionamos "Next >"


Como nombre del proyecto colocamos "pf" y luego presionamos "New Runtime..."


Ahora configuraremos un Server Runtime, esto implica que le diremos a eclipse donde se encuentran los archivos componentes del server.

Seleccionamos "Apache Tomcat v7.0" y presionamos "Next >"

Ahora debemos indicar el [TOMCAT_HOME] y presionamos "Finish". 


Ahora nos encontramos en el asistente principal nuevamente, presionamos "Next >"


En la próxima pantalla solo presionamos "Next >"



En la última pantalla del asistente solo debemos marcar para que se genere el descriptor de despliegue web.xml y presionamos "Finish"

Para finalizar nos cambiamos a la perspectiva JEE



Habilitando JSF.

En la vista "Project Explorer" presionamos botón derecho sobre el proyecto "pf" y seleccionamos "Properties" y seleccionamos "Project Facets", luego chequeamos "JavaServer Faces" y presionamos el link "Further configuration required..."


En la siguiente pantalla presionar el icono del administrador de librerías.


Luego crear una nueva librería presionando el botón "New...", el nombre de la librería será "JSF" y presionamos "Ok"


Una ve definida la librería, la seleccionamos y presionamos el botón "Add External JARs...", debemos seleccionar ahora el path [LIB_HOME]/javax.faces-2.1.17.jar. Presionamos "OK"


Una vez en la pantalla principal del asistente, seleccionamos la librería y agregamos un patrón de mapeo que debe ser "*.xhtml"



Los mapeos deben quedar como se ve en la siguiente figura, para finalizar presionamos "OK" en esta pantalla y en la anterior.


Habilitar PrimeFaces

Esto es por demás sencillo, solo debemos arrastrar el archivo "javax.faces-2.1.17.jar" sobre la capeta WEB-INF/lib. 


Una vez que lo soltamos seleccionamos la opción "Link to files" como se ve en la siguiente imagen.


Creando una instancia del servidor

Nos resta solo un paso para tener un proyecto JSF/PrimeFaces funcionando en un servidor, esto es, la instancia del servidor. Para ello en la vista "Servers" presionamos el link "new server wizard"

En la primera pantalla seleccionamos "Tomcat v7.0 Server" y presionamos "Next >"


En la última pantalla del asistente debemos pasar nuestro proyecto "pf· de la lista "Available" a la lista "Configured" para que se publique sea publicado en esta instancia del servidor, presionamos "OK" y listo.

 La vista de "Servers" debe verse más o menos así:


Primeros pasos con PrimeFaces (La vista)

Bien, ya tenemos todo listo para trabajar con PrimeFaces, esta es la parte más corta de este post!

Vamos a crear un archivo llamado gauge.xhtml, para ello debemos seleccionar la carpeta "WebContent" y luego presionar Ctrl+N, navegamos a la categoría "General" y seleccionamos "File", presionamos "Next >" y en la siguiente pantalla colocamos "gauge.xhtml", chequeemos que "parent folder" sea: "pf/WebContent" como nombre del archivo y presionamos "Finish".

Una vez creado el archivo lo abrimos en el editor haciendo doble click en el. El código que debe contener es:


<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="Custom Options" label="km/h" />

   </h:form>
</h:body>
</html>

Diremos que el tag:

<html xmlns="http://www.w3c.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:p="http://primefaces.org/ui">
Son las que permiten utilizar los tags JSF y PrimeFaces

El tag:
      <p:poll interval="2" update="gauge" />
Hará que se actualice el elemento id="gauge" cada 2 segundos.

El tag:

      <p:meterGaugeChart id="gauge" value="#{gaugeBean.meterGaugeModel}"
         showTickLabels="false" labelHeightAdjust="110"
         intervalOuterRadius="130"
         seriesColors="66cc66, 93b75f, E7E658, cc6666"
         style="width:400px;height:250px" 
         title="Gauge Personalizado" label="km/h" />

Crea un gauge customizado, se personalizan cosas como los colores para los rangos, el alto el ancho, etc. El atributo value="#{gaugeBean.meterGaugeModel}", es uno de los más importantes ya que define la fuente de datos para este gauge, en este caso un bean llamado gaugeBean tendrá un método llamado getMeterGaugeModel(), el cual se traduce en un atributo llamado meterGaugeModel.

Con lo anterior hemos creado la capa de presentación, ahora falta el modelo que aportará datos a la presentación, en otras palabras el gaugeBean.


Primeros pasos con PrimeFaces (El modelo)

Vamos a crear una nueva clase java, para ellos debemos seleccionar el proyecto "pf" y luego presionar Ctrl+N, navegamos a la categoría "Java" y seleccionamos "Class", presionamos "Next >" y en la siguiente pantalla colocamos los datos que se muestran en la siguiente figura y presionamos "Finish":



Package: ar.com.magm.web.primeface
Name: GaugeBean
Debe implementar la interface Seralizable


Abrimos la clase recién creada y agregamos el código que se muestra en negrita.

package ar.com.magm.web.primefaces;
import java.io.Serializable;

import java.util.ArrayList;
import java.util.List;
import org.primefaces.model.chart.MeterGaugeChartModel;

public class GaugeBean implements Serializable {

private MeterGaugeChartModel meterGaugeModel;

public MeterGaugeChartModel getMeterGaugeModel() {
meterGaugeModel.setValue(Math.random() * 220);
return meterGaugeModel;
}
public GaugeBean() {
createMeterGaugeModel();
}
private void createMeterGaugeModel() {
List<Number> intervals = new ArrayList<Number>() {
{
add(20);
add(50);
add(120);
add(220);
}
};
meterGaugeModel = new MeterGaugeChartModel(0, intervals);
}

}

El método más importante es:
public MeterGaugeChartModel getMeterGaugeModel() {
meterGaugeModel.setValue((Math.random() * 190) + 20);
return meterGaugeModel;
}

Simplemente define una valor al azar para el MeterGaugeChartModel y lo retorna, esta es la fuente de datos, el modelo ya fue creado cuando se crea instancia y se definen los rangos que son:
add(20);
add(50);
add(120);
add(220);


A estas alturas, ya tenemos los dos componentes necesarios, solo resta "decirle" a faces que este bean existe y como lo debe manejar. Para ello debemos hacer doble click en "faces-config.xml" que se encuentra en la carpeta "WEB-INF", una vez abierto el editor seleccionamos la pestaña "ManagedBean" y luego presionamos el botón "Add".



En la siguiente pantalla rellenar "Qualified class name" con ar.com.magm.web.primefaces.GaugeBean y presionar "Next >"


Por último nos aseguramos que el nombre del bean es gaugeBean, que es el que usamos en el tag y que el scope es application, esto nos asegura una sola instancia para toda la aplicación, luego presionamos "Finish".


Para finalizar presionamos Ctrl+Shift+S para guardar todos los cambios.

Probando la aplicación

Para probar nuestra aplicación debemos iniciar la instancia que maneja nuestro sitio web, para ellos vamos a la vista "Servers", seleccionamos la instancia que configuramos al principio y presionamos el botón start.



Apache Tomcat por defecto escucha en el puerto 8080, nuestro sitio tiene por nombre "pf", por ello debemos abrir nuestro navegador e ingresar la URL http://localhost:8080/pf/gauge.xhtml, luego podremos ver algomo lo que muestra la siguiente imagen, pero si nos detenemos veremos que el valor del gauge se actualiza de forma automática cada 2 segundos.


Bien, no tengo mucha más para decir que: "QUE LO DISFRUTEN"

Próximo tutorial: http://jmagm.blogspot.com/2013/02/login-y-control-de-acceso-basico-con.html 

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