Integración de Spring con el envío de emails

4
33615

Integración de Spring con el envío de emails.

0. Índice de contenidos.

1. Introducción

Siguiendo con la saga de tutoriales sobre Spring y el soporte a la integración con otras tencologías que proporciona, vamos a analizar la integración
con un servicio de correo electrónico.

Dentro del amplio catálogo de escenarios en el que podemos integrar Spring en nuestros desarrollos, solemos decir que no es una solución «todo o nada»,
de modo que nos podemos beneficiar de todo el conjunto de módulos que contiene para darnos soporte a la generación de una aplicación JEE o sólo de una parte
de ellos.

En este tutorial vamos a implementar un servicio de envío de emails con attachments, al que tendremos acceso:

  • desde cualquier módulo de nuestra aplicación gestionado por Spring, haciendo uso de su inyección de depedencias, o
  • desde cualquier módulo de nuestra aplicación, que sin estar gestionado por Spring, pueda acceder al contenedor de IoC de Spring para recuperar el servicio.

Para comprobar el funcionamiento del servicio haremos un test de JUnit que levantará el contexto de Spring.

La redacción de este tutorial se realiza dando por hecho que el lector tiene conocimientos suficientes sobre Spring IoC, Maven y JUnit 4.4.

2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Sobremesa Dell Dimension 6400, 2.13 Ghz, 2 Gb RAM
  • Sistema operativo: Windows XP Media center Edition
  • JDK 1.5.0_15
  • Eclipse 3.4.

3. Maven: el pom.xml.

El soporte que proporciona Spring para el envió de emails no es más que un wrapper sobre las librerías javax.mail y javax.activation, con lo que, además de las dependencias típicas para Spring y el entorno de test incluimos la dependencia a estas dos librerías.


	4.0.0
	com.autentia.training.spring
	SpringEmail
	jar
	1.0-SNAPSHOT
	SpringEmail
	Ejemplo de uso de un servicio de Email en una aplicación con Spring.
	http://maven.apache.org
	
		
		
			org.springframework
			spring
			2.5.5
		
		
		
			javax.annotation
			jsr250-api
			1.0
		
		
		
			javax.mail
			mail
			1.4
		
		
		    javax.activation
		    activation
		    1.1
		
		
		
			junit
			junit
			4.4
			test
		
		
			org.springframework
			spring-test
			2.5.5
			test
		
	
	
		SpringEmail
		
			
				maven-compiler-plugin
				
					1.6
					1.6
					UTF-8
				
			
		
	

4. Creación de nuestro servicio de envío de emails.

Comenzamos con la implementación del servicio, creando una interfaz, para obligarnos a implementar una serie de métodos, los típicos para el envío de correo electrónico.

package com.autentia.training.spring.mail;

import java.io.File;

public interface MailService {

	public void send(String to, String subject, String text);
	
	public void send(String to, String subject, String text, File... attachments);

}

Ahora creamos una clase que implemente la interaz creada

package com.autentia.training.spring.mail;

import java.io.File;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.util.Assert;

/**
 * Servicio de envío de emails
 */
public class MailServiceImpl implements MailService {

	private static final Log log = LogFactory.getLog(MailServiceImpl.class);

	/** wrapper de Spring sobre javax.mail */
	private JavaMailSenderImpl mailSender;

	public void setMailSender(JavaMailSenderImpl mailSender) {
		this.mailSender = mailSender;
	}

	/** correo electrónico del remitente */
	private String from;

	public void setFrom(String from) {
		this.from = from;
	}

	public String getFrom() {
		return from;
	}

	/** flag para indicar si está activo el servicio */
	public boolean active = true;

	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	private static final File[] NO_ATTACHMENTS = null;

	/** envío de email 
	 * @param to correo electrónico del destinatario
	 * @param subject asunto del mensaje
	 * @param text cuerpo del mensaje
	 */
	public void send(String to, String subject, String text) {
		send(to, subject, text, NO_ATTACHMENTS);
	}

	/** envío de email con attachments
	 * @param to correo electrónico del destinatario
	 * @param subject asunto del mensaje
	 * @param text cuerpo del mensaje
	 * @param attachments ficheros que se anexarán al mensaje 
	 */
	public void send(String to, String subject, String text, File... attachments) {
		// chequeo de parámetros 
		Assert.hasLength(to, "email 'to' needed");
		Assert.hasLength(subject, "email 'subject' needed");
		Assert.hasLength(text, "email 'text' needed");

		// asegurando la trazabilidad
		if (log.isDebugEnabled()) {
			final boolean usingPassword = !"".equals(mailSender.getPassword());
			log.debug("Sending email to: '" + to + "' [through host: '" + mailSender.getHost() + ":"
					+ mailSender.getPort() + "', username: '" + mailSender.getUsername() + "' usingPassword:"
					+ usingPassword + "].");
			log.debug("isActive: " + active);
		}
		// el servicio esta activo?
		if (!active) return;

		// plantilla para el envío de email
		final MimeMessage message = mailSender.createMimeMessage();

		try {
			// el flag a true indica que va a ser multipart
			final MimeMessageHelper helper = new MimeMessageHelper(message, true);
			
			// settings de los parámetros del envío
			helper.setTo(to);
			helper.setSubject(subject);
			helper.setFrom(getFrom());
			helper.setText(text);

			// adjuntando los ficheros
			if (attachments != null) {
				for (int i = 0; i < attachments.length; i++) {
					FileSystemResource file = new FileSystemResource(attachments[i]);
					helper.addAttachment(attachments[i].getName(), file);
					if (log.isDebugEnabled()) {
						log.debug("File '" + file + "' attached.");
					}
				}
			}

		} catch (MessagingException e) {
			new RuntimeException(e);
		}
		
		// el envío
		this.mailSender.send(message);
	}

}

El código está comentado, pero añadimos lo siguiente:

  • líneas 23 a 27: la inclusión del servicio de envío propio de Spring y la espera de ser inyectado mediante el método set,
  • líneas 29 a 38: la asignación de un remitente de correo por defecto para todos nuestros emails, será también inyectado por Spring,
  • línea 86: creación de la plantilla para el envío de emails. La clase MimeMessage tiene lo mínimo para el envío de emails a través del servicio de Spring.
  • línea 90: creación de la plantilla para el envío de emails con attachments, permite multipart.
  • líneas 92 a 107: poblamos las pantilla con los parámetros del método y el remitente por defecto,
  • línea 114: el envío propiamente dicho a través del servicio de Spring.

5. Configuración del applicationContext.xml.

Publicamos nuestro servicio en el contexto del applicationContext.xml, inyectándole la dependencia al servicio de envío propio de Spring y el remitente por defecto.
Para la configuración del servicio de Spring nos apoyamos en un fichero de configuración administrable: app.properties, que tendrá los datos de conexión con el servidor de correo.



	

	

	
	
		
			true
		
		
			
				classpath:app.properties
			
		
	
	
	
	
	  
	  
	  
	  
	  
	

	
	
	  
	  
	  
	
	

Destacamos lo siguiente:

  • líneas 12 y 14: configuración del contexto de Spring para el uso de anotaciones,
  • líneas 16 a 26: configuración de un servicio de tipo PropertyPlaceholderConfigurer para la lectura de ficheros de propiedades externos, de modo que,
    como si accediesemos a variables, obtener el valor de las claves declaradas en el fichero de properties,
  • líneas 29 a 35: configuración del servicio de Spring para el envío, al que inyectamos la configuración sobre el servidor de correo, obtenida del fichero de propiedades. Para comprobar qué podemos configurar del servicio basta con revisar el javadoc de la clase de modo que cada método set de la clase coincide con uno de nuestros <property (inyección de dependencias a través de propiedades),
  • líneas 38 a 42: configuración de nuestro servicio de envío inyectándole el servicio de Spring y el remitente por defecto.

El código de nuestro fichero de propiedades app.properties:

mail.host=smtp.unknown.com
mail.port=25
mail.username=user
mail.password=passw

Con la implementación de la interfaz y esta configuración ya podemos hacer uso de nuestro servicio de envío desde cualquiera de nuestras clases de negocio gestionadas por
Spring mediante la anotación @Resource.

6. Creación de un test de JUnit.

Vamos a probar su funcionamiento a mediante un test de JUnit. El test comprobará un error en el envío, puesto que la configuración de nuestro host es incorrecta,
pero nos sirve para ilustrar su funcionamiento y cómo realizar la inyección.

package com.autentia.training.spring.mail;

import javax.annotation.Resource;

import junit.framework.Assert;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/applicationContext.xml"})
public class MailServiceTest {
	
	private static final Log log = LogFactory.getLog(MailServiceTest.class);

	@Resource
	private MailService mailService;

	/**
	 * Probamos el envío
	 */
	@Test
	public void cantSendMails() {
		try {
			mailService.send("jmsanchez@autentia.com", "Test de envío de email.", "Prueba del envío de correo electrónico.");
			Assert.fail("No debería realizar el envío puesto que el host no está correctamente configurado en el entorno de test.");
		}
		catch(Exception e){
			log.trace("Excepción controlada, normal en el entorno de test",e);
		}
	}
  	
}

Para la comprobación del correcto funcionamiento tendríamos que retocar el código de nuestro test.

Destacamos las anotaciones de la clase (líneas 14 y 15) que nos permiten levantar desde la ejecución del test el contexto de Spring, leyéndo la configuración del fichero applicationContext.xml.


7. Conclusiones.

Sencilla la configuración de un servicio de envío de emails con Spring, ¿verdad?. Si además podemos hacer uso de la inyección de dependencias tendremos el servicio
disponible desde cualquier clase de negocio.

El hecho de tener la configuración del servicio desacoplada mediante Spring, nos permitirá llevar a cabo una configuración del servidor smtp en función del entorno y, sobre todo, en función del entorno de tests.

Un saludo.

Jose

mailto:jmsanchez@autentia.com

4 COMENTARIOS

  1. Buenas.
    Para empezar gracias por el tutorial, explica todo de una manera muy correcta, pero tengo un pequeño problema con el.

    Estoy tratatando de hacer funcionar el ejemplo, pero pese a configurar los parametros del app.properties con los de una cuenta de correo gmail me encuentro con la siguiente traza de error:

    INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@17ee8b8: defining beans [org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,propertyConfigurer,mailSender,mailService]; root of factory hierarchy
    Exception in thread \\\»main\\\» org.springframework.mail.MailSendException; nested exception details (1) are:
    Failed message 1:
    com.sun.mail.smtp.SMTPSendFailedException: 530 5.7.0 Must issue a STARTTLS command first. b10sm3796567wer.17

    at com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:1388)
    at com.sun.mail.smtp.SMTPTransport.mailFrom(SMTPTransport.java:959)
    at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:583)
    at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:403)
    at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:342)
    at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:338)
    at implementacion.MailServiceImpl.send(MailServiceImpl.java:119)
    at implementacion.MailServiceImpl.send(MailServiceImpl.java:64)
    at test0001.Main.enviarCorreo(Main.java:28)
    at test0001.Main.main(Main.java:35)

    Sabrías decirme de que se trata.

    Gracias de antemano.

  2. el ejemplo me funciono bien!!! … el problema es que tengo definido una Template para en envio de mail a cuentas hotmail(outlook), al momento de leer el mensaje desde hotmail… quita todos los hiperlinks , formularios que tiene el mensaje enviado, es decir, o y solo lo visualiza como texto plano, hize porque tenia la necesidad de tener algun enlace para redireccionar a otra pagina…
    Otra cosa cuando adiciono style/css para el Template de Mail la pagina en hotmail los quita y lo muestra como archivo adjunto….????
    Esto me pasa al enviar un mail desde una cuenta Gmail a una Hotmaill…..
    La verdad no entiendo porque pasa eso!!! :-/
    necesito ayuda…. les agradeceria su respuesta!!!

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad