miércoles, 30 de junio de 2010

Interceptores con Spring AOP

Hola a todos de nuevo.. estos días me he encontrado bastante ocupado por los parciales en la U y viendo trámites para sacar mi licencia de conducir (brevete) que ia sale :d. Ahora que todo está tranquilo en el trabajo me he animado por escribir un poco más acerca de Spring AOP, en este caso con sus Interceptores.  Estoy también analizando las propuestas que hacen en sus comentarios para futuros post.

Bueno había ya realizado un artículo en donde explicaba la forma de realizar métodos transaccionales con Spring AOP en transacciones-con-hibernate-y-Spring-AOP .
Ahora más bien veremos de lo que se tratan los interceptores y su utilidad en la bitácora de las aplicaciones en conjunto con log4j. Para empezar teóricamente definiremos el concepto de interceptor.


AOP son las siglas en ingles de Programación orientada al aspecto (Aspect Oriented Programming). Además, como sabemos la definición más simple de AOP es “una manera de eliminar codigo duplicado”. Un ejemplo práctico para la utilidad del AOP, es generar un interceptor, que inspeccionará el codigo que se va a ejecutar, permitiendo por lo tanto realizar ciertas acciones como : trazas cuando el método es llamado (bitácora), modificar los objetos devueltos o envio de notificaciones.


¿Qué es un interceptor?

Vimos hasta ahora que con Spring podemos delegar la creación de objetos, e inyectarles dependencias. Además, sabemos que usamos interfaces para castear los objetos que nos devuelve o inyecta Spring, de forma tal de poder lograr una real independencia de las implementaciones.
¿Qué pasaría entonces si, en tiempo de ejecución, se pudieran crear clases que intercepten los llamados y realicen acciones adicionales? Es decir, el poder agregar comportamiento en tiempo de ejecución, sin tocar el código.
Este es el concepto de los interceptores. Un interceptor es una clase que cumple con cierta interfaz, y su objetivo es "interponerse" en la ejecución de un método. Así, un interceptor es capaz de ejecutarse antes de un método dado, realizar acciones, y luego continuar con la ejecución normal (o abortarla).
Este tipo de concepto se conoce como Programación Orientado a Aspectos.

Un interceptor muy útil, por ejemplo, podría ser de log: algo que intercepte todas las llamadas a los objetos de Negocio y deje registrado la hora de la invocación, los parámetros, el valor de retorno, etc.


Configurando Nuestro Interceptor
Para configurar el Spring AOP con interceptor, nuestra aplicación deberá contar con las siguientes librerías:
  • spring.jar
  • log4j-1.2.14.jar
  • commons-logging-1.1.jar
  • cglib-2.1.jar
  • aopalliance.jar
  • aspectjrt.jar
  • aopalliance.jar
Todos estos y algunos más los puedes obtener en la siguiente dirección  download lib .

Configurando el applicationContext.xml

<beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
              ">
 
<!-- *********** INI :  DATASOURCE CONFIGURATION ************ -->    
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="/WEB-INF/jdbc.properties"/>
    </bean>
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">        
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>       
    </bean>   

 <!-- *********** INI : IBATIS CONFIGURATION  ************* -->   
   
     <!-- SqlMap setup for iBATIS Database Layer -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation">
            <value>classpath:/gob/minedu/capacitacion/persistency/ibatis/sql-map-config-cxa.xml</value>
        </property>
        <property name="dataSource"><ref local="dataSource"/></property>     
    </bean> 

<!-- ************ INI: INTERCEPTOR DAO  ************** -->
<aop:config  proxy-target-class="true"> 
      <aop:pointcut id="businessOperation" expression="execution(* gob.minedu.capacitacion.*.dao.ibatisImpl.*.*(..))"/>
      <aop:advisor advice-ref="logInterceptor" pointcut-ref="businessOperation"/> 
</aop:config>

<bean id="logInterceptor" class="gob.minedu.shared.common.util.LogInterceptor"/>

<import resource="application-service.xml"/>

</beans>

Explicaremos lo relevante para este artículo.. las otras partes del applicationContext.xml han sido explicadas en anteriores artículos. Analizando la configuración del interceptor.

  • En vista de que no existe un TransactionManager es necesario poner proxy-target-class="true".

  • aop:pointcut : Punto de corte definido por patrones de nombres o expresiones regulares. La estructura de la expresión depende de la estructura de nuestra aplicación.

  • aop:advisor advice-ref="logInterceptor", referencia a la clase que implementa el aspecto. Se insertan en la aplicación en los Puntos de Corte.
Implementación de la clase interceptora LogInterceptor.java

package gob.minedu.shared.common.util;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;


/**
 * @author Pedro.Rios
 *
 */
public class LogInterceptor implements MethodInterceptor {
 private static final Logger log = Logger.getLogger(LogInterceptor.class);
 
 public Object invoke (MethodInvocation metodo) throws Throwable {
  
 log.debug("INI DAO: Clase Invocada "+metodo.getMethod().getDeclaringClass().getSimpleName() +" Metodo solicitado "+metodo.getMethod().getName());
 
 //Generamos la lista de argumentos que recibe el metodo separados por una coma   
    String arguments = new String();               
    for (int i = 0; i < metodo.getArguments().length; i++) {   
        arguments += metodo.getArguments()[i] + " ,";                
    }   
       
    // el metodo recibe al menos un argumento quitamos el espacio y la coma del final   
    if (arguments.length()> 0) {   
        arguments = arguments.substring(0, arguments.length() - 2);   
    }   
    log.debug("Argumentos: "+arguments);
 
 Object obj = metodo.proceed();
 log.debug ("FIN DAO: El método ha devuelto : "+obj);
 return obj;
 }
}
Definición del archivo log4j.properties
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.stdout.threshold=DEBUG

log4j.logger.org.ibatis=INFO
log4j.logger.org.apache=INFO
log4j.logger.org.springframework=INFO

#Sql: log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Connection=INFO
#Resultado sql: log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.ResultSet=INFO

Si no sabes como configurar el log4j y donde ubicar el archivo.properties puedes ver el web.xml del artículo  http://periospino.blogspot.com/2010/03/integrando-struts-spring-ibatis.html 
Con eso es suficiente para integrar Spring con Log4j.
De todas formas ya se viene un artículo explicando al detalle el uso de Log4j.

Para mayor informacíón:

Bueno amigos me despido y hasta la próxima publicación... Espero sus comentarios.

jueves, 3 de junio de 2010

Usando javaMail - API de envio de correos en Java

En el mundo del desarrollo de Sistemas de Información siempre existe la necesidad de enviar correos de distintas formas y maneras. En mi experiencia  profesional he necesitado de este medio en distintas ocasiones, como por ejemplo:


  • Al registrarse un usuario en el sistema de ventas, en el acto se le envía un email al usuario pidiéndole que confirme la existencia de su correo electrónico o también se le puede enviar el catálogo con los distintos productos que ofrece la empresa.

  •  Correos masivos a los distintos usuarios de nuestra base de datos.

  • Programar el envío automático de correos, en una especie de campaña de ventas.
Como podemos observar, en la actualidad, la importancia de el envío de correos desde el aplicativo tiene una importancia vital para la ejecución de las distintas estrategias de marketing y fidelización de clientes trazadas por las diferentes empresas.


Por esta razón, la siguiente pregunta cae por su propio peso : ¿Cómo podemos enviar correos electrónicos desde Java?

correos.jpg

Empezaremos este artículo definiendo al API JavaMail.

JavaMail se trata de una librería desarrollada por SUN encaminada al envío de correos electrónicos directamente desde tu aplicación Java. El uso de ésta librería es muy sencillo pero detallaremos paso a paso como realizar la instalación y uso de ella.
JavaMail implementa el protocolo SMTP (Simple Mail Transfer Protocol) así como los distintos tipos de conexión con servidores de correo -TLS, SSL, autentificación con usuario y password, etc.

JavaMail no se incluye en la JDK ni en la JRE, sino que debe conseguirse como un paquete externo.


Empezando a armar la Aplicación

Una vez descargado el API, añadimos el archivo mail.jar  v1.4.x a nuestro proyecto y de ser necesario también añadir activation.jar  v1.0.2 .

Pues bien, ahora procederemos a implementar un aplicativo que permita el envío de correos usando el protocolo SMTP de nuestra cuenta en gmail; sin embargo, si contamos con un servidor de correos personal será mucho mejor usar ese (dominio personal).


* Clases y métodos a utilizar

Clase Properties: Ésta clase es la encargada de almacenar las propiedades de la conexión que vamos a establecer con el servidor de correo Saliente SMTP.
Propiedades a utilizar:
  1. Protocolo a utilizar para el envío de correos.
  2. Dirección del servidor de correo.
  3. Indicar si se necesita autenticar o no.
  4. Puerto SMTP.
  5. Valor booleano de habilitación TLS.
  6. Email del emisor del mensaje.
  7. Usuario de acceso al servidor SMTP
Clase Session: De alguna forma representa la conexión con el servidor gmail de correo. Hay que obtenerla pasándole los parámetros de configuración para el servidor de correo - gmail en nuestro caso-.

Clase MimeMessage: Aquí formaremos el mensaje que deseamos enviar.


Clase Transport: Clase para el envío de mensajes.

 
* Aplicativo para el envío de correos

Archivo smtp.properties : Archivo que contiene los parámetros de configuración.Ubicado en el paquete src/resource

#SMTP configuration
mail.transport.protocol=smtp
mail.host=smtp.gmail.com
mail.smtp.auth=true
mail.smtp.port=465
mail.smtp.socketFactory.port=465
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.socketFactory.fallback=false
mail.smtp.quitwait=false
mail.smtp.mail.sender=tuCorreo@gmail.com
mail.user=tuCorreo@gmail.com
mail.password=tuClaveGmail
mail.email=tuCorreo@gmail.com

Clase SMTPConfig.java : Clase reutilizable para el envío de email.
package pedro.rios.mail;

import java.security.Security;
import java.util.Properties;
import java.util.ResourceBundle;

import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;


/**
 * @author pedro Rios
 * @date 10/12/09
 * 
 */
public class SMTPConfig {

/**
  * @param titulo : titulo del mensaje
  * @param mensaje : Cuerpo del Mensaje
  * @param paraEmail : Email receptor del mensaje
  * @return true si el envío es conforme y false si no es así.
  */
 
 public static synchronized  boolean sendMail(String titulo, String mensaje, String paraEmail) { 
  boolean envio=false;
  
  try {
   
   //carga del archivo smtp.properties
   final ResourceBundle  props = ResourceBundle.getBundle("resource.smtp");
   
   Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
   
   //Propiedades de la conexion
   Properties propiedades = new Properties();
   propiedades.setProperty("mail.transport.protocol",  props.getString("mail.transport.protocol"));
   propiedades.setProperty("mail.host",props.getString("mail.host"));
   propiedades.put("mail.smtp.auth",props.getString("mail.smtp.auth"));
   propiedades.put("mail.smtp.port",props.getString("mail.smtp.port"));
   propiedades.put("mail.smtp.socketFactory.port",props.getString("mail.smtp.socketFactory.port"));
   propiedades.put("mail.smtp.socketFactory.class",props.getString("mail.smtp.socketFactory.class"));
   propiedades.put("mail.smtp.socketFactory.fallback",props.getString("mail.smtp.socketFactory.fallback"));
   propiedades.put("mail.smtp.mail.sender",props.getString("mail.smtp.mail.sender"));
   
   propiedades.setProperty("mail.smtp.quitwait",props.getString("mail.smtp.quitwait"));
   
   //Preparamos la Sesion autenticando al usuario
   Session session = Session.getDefaultInstance(propiedades,new javax.mail.Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication(){ 
     return new PasswordAuthentication(props.getString("mail.user"),props.getString("mail.password"));
    }
   });
   
   //Preparamos el Mensaje
   MimeMessage message = new MimeMessage(session);
   message.setSender(new InternetAddress(props.getString("mail.email")));
   message.setSubject(titulo);
   message.setContent(mensaje, "text/html; charset=utf-8");
   message.setFrom(new InternetAddress(props.getString("mail.smtp.mail.sender")));
   message.setReplyTo(InternetAddress.parse(props.getString("mail.smtp.mail.sender")));
  
  
   if (paraEmail.indexOf(',') > 0) 
    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(paraEmail));
   else
    message.setRecipient(Message.RecipientType.TO, new InternetAddress(paraEmail));
   
   //envío del mensaje
   Transport.send(message);
   envio = true;
  
  } catch (Throwable e) {
   envio = false;
   System.out.println(e.getMessage());
   e.printStackTrace();
  
  }finally{
   return envio;
  }
 }
}


Clase de Prueba de envío de correo PruebaMail.java
package pedro.rios.mail;

public class PruebaMail {

 public static void main(String[] args) {
  
  if(SMTPConfig.sendMail("Asunto del Mensaje"," Cuerpo del Mensaje.","destino@gmail.com")){
  
   System.out.println("envío Correcto");
   
  }else System.out.println("envío Fallido");

 }

}


Listo eso es todo. Solo nos queda ejecutar la clase PruebaMail.java ...

Para mayor información les dejo el link para descargar el libro Java Mail en Ejemplos de la Universidad de Málaga. .... Bueno amigos espero sus comentarios.. se despide de ustedes edisonjc7.