martes, abril 23, 2013

Reimplementar administración de Usuarios en nuestra aplicación PrimeFaces/JSF/Spring/Hibernate (Parte 1/2)

Hola Estimad@s,

a continuación comenzaremos con una serie de cambios en nuestra aplicación a fin de implementar la persistencia de las cuentas de usuario con Hibernate, en esta primera parte solo inicializaremos las cuentas de usuario de los clientes y la cuenta admin, pero nos servirá para reforzar lo que hemos venido haciendo en los últimos posts.

Desarrollo

Comenzamos creando la clase Usuario, la cual encapsulará los datos pertenecientes a una cuenta de usuario.

Creamos las clases nuevas

Usuario.java (representa los datos de cuenta de un usuario)


package ar.com.magm.model;
public class Usuario {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isAdmin() {
return admin;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
private int id;
private String nombre;
private String password;
private boolean admin;
}

Cliente.java (agregamos al final de la clase el atributo usuario)


...
        ...
        private Usuario usuario;

public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
}


Creamos los mapeos nuevos

Usuario.hbm.xml (mapeo nuevo)


<?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.Usuario" table="usuarios">
<id column="id" name="id" type="integer" />
<property name="nombre" />
<property name="password" />
<property name="admin" />
</class>
</hibernate-mapping>



Cliente.hbm.xml (modificamos el existente)


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

   ...
   ...


Hibernate.cfg.xml (modificamos el existente)

   ...
   ...

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



Definimos e implementamos los DAO

Lo que se plantea a continuación debería servirnos para reforzar el procedimiento a seguir para crear un nuevo DAO para una nueva clase del modelo.

UsuarioDAO.java (primero la interfaz con los servicios)


package ar.com.magm.persistencia.dao;
import ar.com.magm.model.Usuario;
public interface UsuarioDAO extends GenericDAO<Usuario, Integer> {}



UsuarioDAOImplHibernate.java (luego la implementación particular para Hibernate)


package ar.com.magm.persistencia.dao.hibernateimpl;
import ar.com.magm.model.Usuario;
import ar.com.magm.persistencia.dao.UsuarioDAO;
public class UsuarioDAOImplHibernate extends
                GenericDAOImplHibernate<Usuario, Integer> implements UsuarioDAO {
}



applicationContext.xml (modificamos el existente)
 ...
 ...

 <bean class="ar.com.magm.persistencia.dao.hibernateimpl.ZonaDAOImplHibernate" />
 <bean class="ar.com.magm.persistencia.dao.hibernateimpl.UsuarioDAOImplHibernate" />
 ...
 ...



Creamos el controlador

InicializaCuentasClientesController.java


package ar.com.magm.model.dao.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import ar.com.magm.model.Cliente;
import ar.com.magm.model.Usuario;
import ar.com.magm.persistencia.dao.ClienteDAO;
import ar.com.magm.persistencia.dao.UsuarioDAO;
import ar.com.magm.persistencia.exception.BussinessException;

@Component("inicializaCuentasClientesController")
@Scope("request")
public class InicializaCuentasClientesController {
  @Autowired
  private ClienteDAO clienteDAO;
  @Autowired
  private UsuarioDAO usuarioDAO;

  public void processRequest(HttpServletRequest request,
                    HttpServletResponse response) throws IOException {
    String salida = "";
    try {
      // Parte 1
      // Inicializamos usuario admin si no existe
      Usuario usuarioAdmin = usuarioDAO.get(33333);
      if (usuarioAdmin == null) { 
        usuarioAdmin = new Usuario();
        usuarioAdmin.setId(33333);
        usuarioAdmin.setAdmin(true);
        usuarioAdmin.setNombre("admin");
        usuarioAdmin.setPassword("admin");
        usuarioDAO.saveOrUpdate(usuarioAdmin);
        salida += "Inicializada cuenta admin.\n";
      } else {
         salida += "La cuenta admin ya se encontraba inicializada.\n";
      }
      int cuentasInit = 0;
      // Parte 2
      // Inicializamos cuentas de usuario de cada cliente si no existe
      List<Cliente> clientes = clienteDAO.findAll();
      for (Cliente c : clientes) {
        Usuario usuario = usuarioDAO.get(c.getIdCliente());
        if (usuario == null) {
          cuentasInit++;
          usuario = new Usuario();
          usuario.setId(c.getIdCliente());
          usuario.setAdmin(false);
          usuario.setNombre(c.getCliente());
          usuario.setPassword("Clave " + c.getIdCliente());
          c.setUsuario(usuario);
          usuarioDAO.saveOrUpdate(usuario);
          clienteDAO.saveOrUpdate(c);
        }
      }
      salida += "Cuentas de cliente inicializadas=" + cuentasInit;
    } catch (BussinessException ex) {
      ex.printStackTrace();
      salida = "Error inicializando cuentas de clientes y admin\n"
              + ex.getMessage();
    }
    response.getWriter().print(salida);
    response.getWriter().flush();
  }
}


El controlador en muy sencillo, consta de dos partes, en la primera comprueba si existe la cuenta "admin", a esto lo hace intentando cargar el id=33333, si no existe crea una instancia de Usuario y utiliza el método DAO saveOrUpdate() para almacenarlo.La parte 2 obtiene la lista de todos los clientes mediante el método DAO findAll(), luego chequeamos uno a uno si existe la cuenta de usuario asociada, si no existe se crea una y se asegura el almacenamiento.
En breve mejoraremos el mapeo de estas entidades para no tener que llamar a los métodos de persistencia de ambas.


Creamos el servicio
InicializarCuentasClientes.java


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.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import ar.com.magm.model.dao.controller.InicializaCuentasClientesController;

@WebServlet("/InicializarCuentasClientes")
public class InicializarCuentasClientes extends HttpServlet {
  protected void doGet(HttpServletRequest request,
                     HttpServletResponse response) 
                     throws ServletException, IOException {
    ApplicationContext applicationContext = WebApplicationContextUtils
.getWebApplicationContext(this.getServletContext());
    InicializaCuentasClientesController controller = (InicializaCuentasClientesController) applicationContext
.getBean("inicializaCuentasClientesController");

    controller.processRequest(request, response);
  }
}

Nada que decir de este servicio, es similar a los anteriores, solo que utiliza otro controlador y que se ejecuta antes la URL /InicializarCuentasClientes, debemos agregar esta URL al filtro para disponer de sesiones Hibernate.

Agregamos la URL al filtro

HibernateContextListenerAndFilter

@WebFilter(urlPatterns = { "/TestHibernateConSpring", "*.xhtml", "/InicializarCuentasClientes" }) 


Creando las cuentas


Ahora solo debemos loguearnos al sistema y una vez en la pantalla principal cargar la URL: http://localhost:8080/pf/InicializarCuentasClientes

La primera vez veremos el siguiente resultado:

Si lo ejecutamos nuevamente veremos:


Si chequeamos el estado de las tablas en MySQL (en este caso usando MySQL Query Browser):

Podemos ver que ya disponemos (de forma automática) de la clave foránea en la tabla Clientes.

Y se ha creado la tabla Usuarios:

Eso es todo por ahora, espero les sea de utilidad.


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



Próximo tutorial:

Saludos

Mariano




viernes, abril 12, 2013

Pentaho como demonio (servicio) linux

Estimad@s,

en este caso escribo para compartir un script que sirve para instalar Pentaho BI Server CE como demonio linux. Agradezco a Jorge Colmenarez por la colaboración y el ánimo de compartir.


El archivo script

Primero creamos un archivo script llamado pentaho con el siguiente contenido:


#!/bin/sh -e
### BEGIN INIT INFO
# Provides:          Pentaho Server
# Required-Start:    mountkernfs $local_fs
# Required-Stop:     $local_fs
# Should-Start:      ifupdown
# Should-Stop:       ifupdown
# Default-Start:     S
# Default-Stop:      0 6
# Short-Description: Starts and stops the Pentaho Server.
### END INIT INFO

PENTAHO_HOME=/home/mariano/pentaho/suite5-trunk/biserver-ce

# Source function library.
if [ -f /etc/rc.d/functions ];
then
    . /etc/init.d/functions
fi

# Pentaho Server Service script for Linux

start()
{
echo $"Starting Pentaho..."
sh $PENTAHO_HOME/start-pentaho.sh
}

stop()
{
echo $"Stopping Pentaho..."
sh $PENTAHO_HOME/data/stop_hypersonic.sh
sh $PENTAHO_HOME/stop-pentaho.sh
}

restart()
{
echo $"Stopping Pentaho..."
sh $PENTAHO_HOME/data/stop_hypersonic.sh
sh $PENTAHO_HOME/stop-pentaho.sh
echo $"Starting Pentaho..."
sh $PENTAHO_HOME/start-pentaho.sh
}

reload()
{
echo $"No se soporta. "
}

# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        restart
        ;;
  reload)
reload
;;
  status)
        ;;
  *)
        echo $"Usage: {start|stop|restart|condrestart|status}"
        exit 1
esac

Solo deberán tener en cuenta colocar el path correcto en la línea:PENTAHO_HOME=/home/mariano/pentaho/suite5-trunk/biserver-ce

Luego copiamos el script a la carpeta /etc/init.d, por ejemplo:
sudo cp /home/mariano/pentaho /etc/init.d

Luego le damos permisos de ejecución:sudo chmod +x /etc/init.d/pentaho

Finalmente configuramos la prioridad de orden de ejecución del demonio:
cd /etc/init.d/
sudo update-rc.d pentaho defaults 99

Manejar el orden de ejecución del demonio es importante, ya que si por ejemplo tenemos el repositorio en MySQL, el servidor MySQL debe arrancar primero. Podemos considerarlo como una especie de manejo de dependencia de servicios muy simple.


Como usarlo

Por un lado, cuando arranquemos nuevamente nuestro sistema operativo, ya dispondremos del servicio, pero también podemos:

Parar el servicio:
sudo /etc/init.d/pentaho stop

Iniciar el servicio:
sudo /etc/init.d/pentaho start

Reiniciar el servicio:
sudo /etc/init.d/pentaho restart

Como eliminar el demonio (fuente: Supernatural  :) )

Si no necesitamos más el servicio, pordemos eliminarlo con el siguiente comando:

sudo update-rc.d -f pentaho remove



Espero les sea de utilidad.

Saludos

Mariano

martes, abril 09, 2013

Migrando la lista de ventas a Hibernate en nuestra aplicación PrimeFaces/JSF/Spring/Hibernate

Hola gente,

seguimos con nuestra aplicación, en esta ocasión migraremos la obtención de la lista de ventas para que sea manejada por Hibernate.

Objetivo

Utilizar la capacidad de trabajar con consultas sql nativas parametrizadas de Hibernate.

Desarrollo

En esta ocasión no es mucho el trabajo, comenzaremos configurando:

ar.com.magm.web.primefaces.VentasBean

...
...

SessionFactory sessionFactory; 

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

...
...


Solo agregamos una instancia de SessionFactory para poder trabajar con Hibernate en las instancias de esta clase.
Luego debemos modificar el método que genera la lista de ventas:


private void processList(Object args[]) {
  meses = bundle.getString("lbl.months").split(",");
  ventas = new ArrayList<Venta>();
  zonas = new ArrayList<String>();
  Session session = sessionFactory.getCurrentSession();
  Query query = session.createSQLQuery(sql);
  if (args != null) {
    for (int t = 0; t < args.length; t++) {
      query.setParameter(0, args[t]);
    }
  }
  List<Object[]> vtaTmp = query.list();
  for (Object[] vo : vtaTmp) {
    Venta venta = new Venta(vo[2].toString(), 
                            vo[3].toString(),
                            Integer.parseInt(vo[0].toString()),
                            Integer.parseInt(vo[1].toString()),
                            meses[Integer.parseInt(vo[1].toString()) - 1],
                            Double.parseDouble(vo[4].toString()));
    ventas.add(venta);
    if (!zonas.contains(venta.getZona()))
      zonas.add(venta.getZona());
  }

  /*
  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();
  }
  */
}


Lo primero que haremos en este método es inhabilitar todo el código antiguo, en aquel en que utilizábamos JDBC. Se puede ver que hemos utilizado comentarios de bloque: /* .. */
Luego en la línea
Query query = session.createSQLQuery(sql);
Obtenemos un objeto Query, el cual nos permite trabajar con sentencias SQL nativas, el método recibe como parámetro la consulta, que no es ni más ni menos que la que ya estábamos utilizando:
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
En el siguiente fragmento establecemos los valores para los parámetros, la diferencia más importante con el método anterior es que los parámetros comienzan desde 0, en JDBC es a partir de 1.
if (args != null) {
  for (int t = 0; t < args.length; t++) {
    query.setParameter(0, args[t]);
  }
}
En la líneaList<Object[]> vtaTmp = query.list();
obtenemos una lista que en cada elemento contiene un arreglo de objetos que representa una fila, en nuestro caso una entidad de ventas. El orden de los elementos del array coinciden con lo definido en la consulta SQL, en nuestro caso: 
[0]=año
[1]=mes
[2]=zona
[3]=cliente
[4]=ventas
El código que sirve para poblar la lista (el ciclo while) casi no ha cambiado, solo se reemplazó la línea que instancia la venta, ahora se utilizan los elementos del array y también se reutiliza la zona ya obtenida, por ello se quitó la primera línea en la cual se obtenía.

Esta es toda la modificación que hay que realizar en cuanto a código, ahora solo falta crear sesiones para que puedan ser utilizadas en este componente para lo cual modificaremos HibernateContextListenerAndFilter como ya lo hemos hecho antes:

...
@WebFilter(urlPatterns = { "/TestHibernateConSpring", "*.xhtml" }) 
...

Solo agregamos el patrón "*.xhtml" para que se creen sesiones ante peticiones de componentes JSF.

Bien, ya podemos probar y no notaremos ninguna diferencia, todo seguirá funcionando como antes del lado del cliente, solo que ahora  Hibernate maneja algo más. En la consola podemos ver las sentencias SQL que Hibernate envía al motor.

Espero les sea de utilidad



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



Saludos

Mariano

viernes, abril 05, 2013

Mejorando el uso de Hibernate en nuestra aplicación PrimeFaces/JSF/Spring

Hola Estimad@s,

en esta ocasión intentaremos mejorar el uso de Hibernate en nuestra aplicación web.

Objetivos

  • Utilizar una versión mejorada de HibernateUtil (fuente
  • Crear una sesión Hibernate por petición web y encapsularla en su propio hilo para ser coherentes con lo requerido por la documentación oficial y poder implementar el patrón Open Session In View
  • Unificar y simplificar el tratamiento de excepciones (fuente)
  • Implementar el patrón DAO 

Desarrollo

En esta instancia no descargaremos más librerías, ya tenemos todas las necesarias para seguir adelante. Si utilizaremos nuevas clases, muchas de las cuales no se explicarán en profundidad porque pertenecen a otro curso/tutorial el cual está muy bien explicado y desarrollado con mucho detalle. Las clases base que utilizaremos son:

ar/com/magm/persistencia/hibernate/util/GenericEventListenerImpl.java
ar/com/magm/persistencia/hibernate/util/GenericIntegratorImpl.java
ar/com/magm/persistencia/hibernate/util/HibernateUtil.java
ar/com/magm/persistencia/hibernate/util/SessionFactoryImplThreadLocal.java
ar/com/magm/persistencia/hibernate/util/HibernateUtilInternalState.java
Las clases anteriores contienen la lógica para crear sesiones Hibernate parametrizables y proporciona ayuda para implementar el patrón Open Session In View.

ar/com/magm/persistencia/dao/GenericDAO.java
Interface que define las operaciones de persistencia para los DAO.

ar/com/magm/persistencia/dao/hibernateimpl/GenericDAOImplHibernate.java
Implementación DAO que utiliza Hibernate

ar/com/magm/persistencia/exception/Caption.java
ar/com/magm/persistencia/exception/BussinessException.java
ar/com/magm/persistencia/exception/BussinessMessage.java
Clases que permiten Simplificar y Unificar el manejo de Excepciones, de hecho solo se utilizará BussinessException sin por ello perder el origen de las excepciones.

ar/com/magm/persistencia/hibernate/web/HibernateContextListenerAndFilter.java
Clase que es un WebLIstener y un WebFilter a la vez.
El WebListener se encarga al inicio de la aplicación de inicializar la fábrica de sesiones de Hibernate y al final la cierra.
El WebFilter asegura que exista una sesión Hibernate en un hilo local por petición, de esta forma se puede implementar el patrón Open Session In View.

Y se encuentran en este archivo:
https://github.com/magm3333/workspace-pftuto/blob/master/paquetePersistencia.zip?raw=true

Además crearemos una serie de clases propias de la aplicación.

Implementación del patrón Data Access Object (DAO)

El patrón DAO nos permite crear una capa de abstracción entre nuestros objetos y algún sistema de almacenamiento.
Se trata de declarar las operaciones de persistencia que podremos realizar con nuestros objetos y luego tener las implementaciones particulares que deseemos. Trabajar con Spring nos ayudará a realizar la última tarea ya que mediante la inyección de dependencias nos abstraemos del problema de la implementación particular.

Comenzaremos creando la interface GenericDAO, la cual contendrá las operaciones básicas comunes a nuestros DAO, el código es el siguiente:

package ar.com.magm.persistencia.dao;

import java.io.Serializable;
import java.util.List;
import ar.com.magm.persistencia.exception.BussinessException;

public interface GenericDAO<T, ID extends Serializable> {
T create() throws BussinessException;
void saveOrUpdate(T entity) throws BussinessException;
T get(ID id) throws BussinessException;
void delete(ID id) throws BussinessException;
List<T> findAll() throws BussinessException;
}

Como se observa las operaciones son 5:

create: creará una nueva instancia de una entidad que puede persistirse.
saveOrUpdateasegura la persistencia de una entidad, si no existe la inserta y si existe la actualiza
get: obtiene una instancia de una entidad desde el sistema de almacenamiento por medio de su clave.
delete: elimina una entidad por medio de su clave.
findAll: obtiene todas las entidades de un tipo desde el almacenamiento persistente.

Como puede observarse, tanto la entidad como su identificador son genéricos, en el próximo paso crearemos  las versiones particulares para Cliente y Zona

ClienteDAO.java


package ar.com.magm.persistencia.dao;
import ar.com.magm.model.Cliente;
public interface ClienteDAO extends GenericDAO<Cliente, Integer> {
}

ZonaDAO.java

package ar.com.magm.persistencia.dao;
import ar.com.magm.model.Zona;
public interface ZonaDAO extends GenericDAO<Zona, Integer> {
}

Bien simple las versiones particulares no?
Para completar, nos resta realizar al menos una implementación y será para Hibernate.

Implementación DAO para Hibernate

Ya existe una clase llamada ar.com.magm.persistencia.dao.hibernateimpl.GenericDAOImplHibernate, la cual contiene la implementación de los métodos genéricos definidos en GenericDAO, el análisis de esta clase escapa a este tutorial y además ya existe una buena explicación aquí, nos remitiremos solo a crear las implementaciones particulares para ClienteDAO y ZonaDAO.

ClienteDAOImplHibernate.java

package ar.com.magm.persistencia.dao.hibernateimpl;
import ar.com.magm.model.Cliente;
import ar.com.magm.persistencia.dao.ClienteDAO;
public class ClienteDAOImplHibernate extends
GenericDAOImplHibernate<Cliente, Integer> implements ClienteDAO {
}

ZonaDAOImplHibernate.java

package ar.com.magm.persistencia.dao.hibernateimpl;
import ar.com.magm.model.Zona;
import ar.com.magm.persistencia.dao.ZonaDAO;
public class ZonaDAOImplHibernate extends
GenericDAOImplHibernate<Zona, Integer> implements ZonaDAO {
}

Buen enfoque el uso de Genéricos no?, teniendo una buena implemetación genérica, las implementaciones particulares son triviales.

Usando Spring para independizarnos de la implementación

Ya tenemos nuestros DAO definidos y una implementación particular para Hibernate, pero podríamos tener cuantas implementaciones deseemos, si ese fuera el caso, sería muy bueno no tener que reescribir clases ni recompilarlas cuando cambiemos de implementación, aquí es donde Spring "nos da una mano".
A continuación crearemos un controlador que procesará peticiones requiriendo información de clientes, en futuros tutoriales formalizaremos el concepto de controlador y veremos como utilizar el framework Modelo Vista Controlador (MVC) de Spring.

El controlador

ClienteTestController.java


package ar.com.magm.model.dao.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import ar.com.magm.model.Cliente;
import ar.com.magm.persistencia.dao.ClienteDAO;
import ar.com.magm.persistencia.exception.BussinessException;

@Component("clienteController")
@Scope("request")
public class ClienteTestController {
  @Autowired
  private ClienteDAO clienteDAO;

  public void processRequest(HttpServletRequest request,
                             HttpServletResponse response) throws IOException {
    String idCl = request.getParameter("idCliente");
    int idCliente = 33;
    if (idCl != null)
      idCliente = Integer.parseInt(idCl);
    String salida;
    try {
      Cliente cl = clienteDAO.get(idCliente);
      StringBuilder str = new StringBuilder();
      if (cl != null) {
        str.append("Cliente: " + cl.getCliente() + " (Id: "
             + cl.getIdCliente() + ") - Cta Habilitada: "
        str.append("\tZona: " + cl.getZona().getZona() + " (Id: "
             + cl.getZona().getIdZona() + ")");
      } else {
        str.append("No existe el cliente con id=" + idCliente);
      }
      salida = str.toString();
    } catch (BussinessException ex) {
      salida = "Error el obtener el cliente\n" + ex.getMessage();
    }
    response.getWriter().print(salida);
    response.getWriter().flush();
  }
}

Esta clase controlador es un bean manejado por Spring, solo que no se ha utilizado el archivo xml (applicationContext.xml) para configurarlo, en vez de ello se han utilizado anotaciones, en particular:

@Component("clienteController")
@Scope("request")
sería lo mismo que colocar:
<bean id="clienteController" class="ar.com.magm.model.dao.controller.ClienteTestController" scope="request"/>el archivo xml (applicationContext.xml).
El scope = "request" indica que se creará una instancia del controlador por cada petición del cliente, es un tiempo de vida muy corto y solo dura lo que dura la petición.

Respecto a la línea anotada con:

@Autowired
private ClienteDAO clienteDAO;

si analizamos la clase con detenimiento, podremos notar que nunca se instancia de forma explícita (new), esta es la línea que nos abstrae de la implementación, será Spring el encargado de inyectar aquí el código (o dependencia) correcto, Spring buscará alguna clase que implemente alguna clase que implemente la interface ClienteDAO, más adelante, en el archivo de configuración (applicationContext.xml), le diremos a Spring cuales son las posibilidades que tiene, en otras palabras, cuales son las clases que implementan esta interfaz.
También notemos que con solo Cliente cl = clienteDAO.get(idCliente); obtenemos la instancia de la entidad Cliente que deseamos.
Podemos ver el manejo simplificado de excepciones, solo capturamos BussinessException, sin embargo siempre tendremos la posibilidad de obtener toda la información de la excepción particular cuando así lo deseemos.

Configurando la detección de anotaciones y los beans candidatos para la inyección de dependencias

Como he adelantado, debemos realizar un par de tareas de configuración en el archivo applicationContext.xml.


<?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">

  <context:annotation-config/>
  <context:component-scan base-package="ar.com.magm.model.dao.controller"/>
    
  <bean class="ar.com.magm.persistencia.dao.hibernateimpl.ClienteDAOImplHibernate" />
  <bean class="ar.com.magm.persistencia.dao.hibernateimpl.ZonaDAOImplHibernate" />

  <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>

La línea <context:annotation-config/>, habilita el uso de anotaciones en Spring, con esta línea podemos usar anotaciones como @Autowired
Con la línea<context:component-scan base-package="ar.com.magm.model.dao.controller"/>
le indicamos a Spring a partir de que jerarquía de paquete deberá buscar los componentes marcados con  la anotación @Component
Por último las líneas
<bean class="ar.com.magm.persistencia.dao.hibernateimpl.ClienteDAOImplHibernate" />
<bean class="ar.com.magm.persistencia.dao.hibernateimpl.ZonaDAOImplHibernate" />
Hacen que Spring cree una instancia de cada implementación particular de DAO para Hibernate, uno de estos beans será inyectado en:
@Autowired
private ClienteDAO clienteDAO;
Por ello, si quisiéramos  cambiar de implementación, solo deberíamos modificar estas líneas y agregar las nuevas implementaciones al classpath.

Una prueba inyteresante podría ser agregar la siguiente línea a ClienteTestController:
...
Cliente cl = clienteDAO.get(idCliente);
System.out.println(clienteDAO.getClass().toString());
StringBuilder str = new StringBuilder();
...


Si lo ejecutamos veremos en la consola la siguiente salida:
class ar.com.magm.persistencia.dao.hibernateimpl.ClienteDAOImplHibernate

Con esto queda confirmado que la inyección de dependencia funciona correctamente.

Un servlet para probar que todo funcione

Crearemos ahora un servlet llamado TestHibernateConSpring, nos servirá, como el anterior, para probar que todo funcione correctamente. El código del servlet es el siguiente:


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.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import ar.com.magm.model.dao.controller.ClienteTestController;

@WebServlet("/TestHibernateConSpring")
public class TestHibernateConSpring extends HttpServlet {
  protected void doGet(HttpServletRequest request,
         HttpServletResponse response) throws ServletException, IOException {

    ApplicationContext applicationContext = WebApplicationContextUtils
       .getWebApplicationContext(this.getServletContext());
    ClienteTestController controller = (ClienteTestController) applicationContext
       .getBean("clienteController");
    controller.processRequest(request, response);

  }

}


En las líneas
ApplicationContext applicationContext = WebApplicationContextUtils
   .getWebApplicationContext(this.getServletContext());
ClienteTestController controller = (ClienteTestController) applicationContext
   .getBean("clienteController");
Obtenemos la instancia del controlador, recordemos que se crea una instancia por petición, por ello podemos decir que esta instancia es exclusiva para este requerimiento.


Luego la línea
controller.processRequest(request, response);
le cede el control a la instancia del controlador, de esta forma organizamos un poco mejor nuestra aplicación  aunque todavía falta un tramo por recorrer.

Creando una sesión Hibernate cuando se requiere

Aún nos resta una cosa para poder probar nuestra aplicación. Modificaremos la clase:
ar/com/magm/persistencia/hibernate/web/HibernateContextListenerAndFilter.java
La cual hemos explicado brevemente al comienzo de este texto.
Esta clase actúa entre otras cosas como filtro, los filtros se ejecutan antes que cualquier componente en un requerimiento, por otro lado se debe definir ante que requerimientos se debe ejecutar el filtro, esto se hace mediante la definición de patrones de URL. Esto hará que ante algunas URLs en particular se ejecute el filtro y ante otras no, por ejemplo no sería conveniente que este filtro cree sesiones Hibernate cuando la URL requiere algún recurso estático, por ejemplo un archivo .jpg o .gif.
La modificación que debemos realizar es:
antes:
@WebFilter(urlPatterns = { "*.html" })
ahora:
@WebFilter(urlPatterns = { "/TestHibernateConSpring" })
De esta forma solo se crearán recursos necesarios para la persistencia cuando sea necesario y no en otro momento, de esta forma ahorramos recursos en el server. En el futuro modificaremos nuevamente este componente.

Probando

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/TestHibernateConSpring, presionamos enter y veremos un resultado similar al siguiente:


El cliente id=33 es el cliente por defecto (puede verse en el código del controlador) para cambiar el cliente que queremos ver debemos enviar un parámetro GET llamado idCliente, por ejemplo:
Cambiamos la url por: http://localhost:8080/pf/TestHibernateConSpring?idCliente=12, veremos lo siguiente:

Si requerimos un id que no existe veremos lo siguiente:

Esto 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 de utilidad.

Próximo tutorial: http://jmagm.blogspot.com.ar/2013/04/migrando-la-lista-de-ventas-hibernate.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