Spring Batch VI: Envío de un EMail

En la presente entrada, Spring Batch VI: Envío de un EMail, realizaré la descripción de cómo se realiza el envío de un email mediante Spring Batch. El envío de un email se puede realizar de dos formas posibles: utilizando la clase SimpleMailMessageItemWriter de Spring, o bien, no usándola.

Las entradas anteriores, para el nuevo lector a la serie, son las siguientes:

Los ejemplos que he ido realizando son de procesos en cuyos pasos se realiza una lectura, un procesamiento y una salida; este patrón, no siempre es requerido ya que se puede dar el caso de definir tareas que no tengan que realizar ninguna operación de entrada y salida. Estos pasos no contienen un elemento chunk, solo contienen un elemento tasklet. A nivel de código, supone la definición de una clase que implemente el interfaz Tasklet. En el proceso de ejemplo, definiré un proceso con dos pasos: el primero, realizará el envío de un correo sin elemento chunk, es decir, definiré un componente Tasklet; y, el segundo paso, realizará el envío de un correo con lectura, procesamiento y escritura, siendo la escritura, el componente que realiza el envío.

Con esta entrada, al utilizar una clase de envío de correo, amplía los tipos que presente en la entrada Spring Batch III: Escritores (Writer).

Definición conceptual del proceso

El proceso a desarrollar es aquel proceso que realiza el envío de un correo de dos formas diferentes; cada forma de envío, es representado por dos pasos distintos. El primer paso, mediante un componente que implementa el interfaz Tasklet; y, el segundo, con un componente que realiza la lectura de los datos de envío de un fichero y utiliza la clase SimpleMailMessageItemWriter de SpringBatch.

Implementación del proceso

La implementación del proceso es muy sencilla, simplemente, se define un proceso con dos pasos en donde se referencia el elemento Tasklet y los elementos de entrada, salida y procesamiento. El snippet de la definición es la siguiente:

<batch:job id="jobTasklet">
 <batch:step id="sendMailForma1" next="sendMailForma2">
   <batch:tasklet ref="sendEmailTasklet" />
 </batch:step>
 <batch:step id="sendMailForma2">
   <batch:tasklet>
     <batch:chunk reader="txtCorreoFileItemReader" writer="xmlCorreoItemWriter"
        processor="itemCorreoProcessor" commit-interval="1">
     </batch:chunk>
   </batch:tasklet>
  </batch:step>
 </batch:job>

FORMA 1

La primera forma de realizar el envío es con un componente Tasklet referenciado en el primer paso cuyo identificador es “sendMailForma1”. El contenido de este elemento referencia a la clase SendEmailTasklet con el identificador “sendEmailTasklet”. El snippet de la clase es la siguiente:

@Component("sendEmailTasklet")
public class SendEmailTasklet implements Tasklet {
 ...
 @Autowired
 private UtilMailSender utilMailSender;

 public RepeatStatus execute(StepContribution contribution,
  ChunkContext chunkContext) throws Exception {
   SimpleMailMessage message = new SimpleMailMessage();
   message.setTo("mailTo@dominio.es");
   message.setFrom("mailFrom@dominio.es");
   message.setSubject("Ejemplo envío correo");
   message.setSentDate(new Date());
   message.setText("Prueba envío correo");
   this.logger.debug("SimpleMailMessage creado.");
   try {
     this.utilMailSender.send(message);
   } catch (MailException ex) {
      throw ex;
   }
   return RepeatStatus.FINISHED;
 }
}

El método principal es el método execute en donde se realiza la creación del mensaje del correo y se utiliza la clase UtilMailSender para realizar el envío. Al terminar la tarea, se retorna el estado FINISHED para que el proceso pueda continuar su ejecución.

La clase UtilMailSender es una clase de utilidad para ejecutar el envío utilizando la clase JavaMailSenderImpl de Spring. Esta clase de utilidad será utilizado en las dos formas que presento. El snippet de la clase es el siguiente:

@Component("mailSender")
public class UtilMailSender implements MailSender {
 ...
 @Autowired
 private JavaMailSenderImpl javaMailSender;
 ...
 public void send(SimpleMailMessage simpleMessage) throws MailException {
 send(new SimpleMailMessage[] { simpleMessage });
 }
 public void send(final SimpleMailMessage[] simpleMessages)
 throws MailException {
   this.lstEnviados = new ArrayList<String>();
   for (SimpleMailMessage simpleMessage : simpleMessages) {
     this.lstEnviados.add(simpleMessage.getSubject());
     this.javaMailSender.send(simpleMessage);
     this.logger.debug("Enviando mensaje...");
   }
 }
 ...
}

La configuración de la entidad de envío de correos es la siguiente:

<bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
 <property name="host" value="${mailServer.host}" />
 <property name="port" value="${mailServer.port}" />
 <property name="username" value="${mailServer.username}" />
 <property name="password" value="${mailServer.password}" />
 <property name="javaMailProperties">
 <props>
 <prop key="mail.smtp.auth">${mailServer.mail.smtp.auth}</prop>
 </props>
 </property>
</bean>

FORMA 2

La segunda forma consiste en definir un chunk. La fuente de datos es un fichero con los datos del correo; el procesador, realizará la creación del elemento SimpleMailMessage; y, la fuente de salida, realiza el envío del correo mediante la clase SimpleMailSenderImpl.

El lector lo representa el bean con identificador “txtCorreoFileItemReader”, en donde realiza la lectura del fichero datos-entrada-correo.txt y utiliza la clase mapeadora CorreoFieldSetMapper. El snippet con la definición del lector es la siguiente:

<bean id="txtCorreoFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
 <property name="resource" value="classpath:/entrada/datos-entrada-correo.txt" />
 <property name="lineMapper">
   <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
     <property name="lineTokenizer">
       <bean
         class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
         <property name="names" value="nombre,correo" />
       </bean>
     </property>
     <property name="fieldSetMapper">
       <bean class="es.directoandroid.mapper.CorreoFieldSetMapper" />
     </property>
   </bean>
 </property>
</bean>

El procesador realiza la creación del objeto SimpleMailMessage con los datos del objeto mapeado del fichero DatosCorreo. El método principal es el siguiente:

public SimpleMailMessage process(DatosCorreo datosCorreo) throws Exception {
 logger.debug("Procesando un elemento, valor=" + datosCorreo);
 SimpleMailMessage message = new SimpleMailMessage();
 message.setTo( datosCorreo.getMail() );
 message.setFrom( "communications@thecompany.com" );
 message.setSubject( datosCorreo.getNombre() + " información del proceso" );
 message.setSentDate( new Date() );
 message.setText( "Hola " + datosCorreo.getNombre() );
 return message;
}

La escritura la realiza la entidad SimpleMailMessageItemWriter identificado como “xmlCorreoItemWriter”. El snippet de su definición es el siguiente:

<bean id="xmlCorreoItemWriter"
 class="org.springframework.batch.item.mail.SimpleMailMessageItemWriter">
 <property name="mailSender" ref="mailSender" />
 <property name="mailErrorHandler" ref="loggingMailErrorHandler" />
</bean>

Definición de test del proceso

Para la realización de las pruebas he utilizado un mock para la simulación de un servidor de correo. El nombre del mock es greenmail en su versión 1.3.1b. La definición de la dependencia en el fichero pom es el siguiente:

<dependency>
 <groupId>com.icegreen</groupId>
 <artifactId>greenmail</artifactId>
 <version>${mockMailServer.greenmail.version}</version>
 <scope>test</scope>
</dependency>

La utilización de este servidor de test es muy sencilla: primero, se define una asociación en la clase de test; segundo, definimos un método setUp con la anotación @Before para la creación;y, en cada método de test, se arranca el mock y se para.

Los métodos de prueba de los dos pasos son idénticos, solo se diferencian en el nombre del paso, y, para el proceso, se lanza la tarea. El snippet de la clase de test es el siguiente:

...
 @Before
 public void setUp() {
   this.servidorCorreo = new GreenMail(ServerSetupTest.SMTP);
 }
 ...
 @Test
 public void testSendMailForma1() {
   try {
    // precondición
    this.servidorCorreo.start();
    // test
    JobExecution jobExecution = jobLauncherTestUtils
     .launchStep("sendMailForma1");
    // post-condición
    assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
    MimeMessage[] receivedMessages = this.servidorCorreo
      .getReceivedMessages();
    assertNotNull(receivedMessages);
    assertTrue(receivedMessages.length == 1);
   } catch (Exception e) {
     fail(e.getMessage());
   } finally {
     this.servidorCorreo.stop();
   }
 }
 ...
 @Test
 public void testJob() {
  try {
    // precondición
    this.servidorCorreo.start();
    // test
    JobExecution jobExecution = jobLauncherTestUtils
      .launchJob();
    // post-condición
    assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
    MimeMessage[] receivedMessages = this.servidorCorreo
     .getReceivedMessages();
    assertNotNull(receivedMessages);
    assertTrue(receivedMessages.length == 2);
   } catch (Exception e) {
     fail(e.getMessage());
   } finally {
     this.servidorCorreo.stop();
   }
 }
 ...

Si se desea el código ejemplo se puede acceder aquí.

Finalizo la serie de Spring Batch, esperando que haya sido del interés del lector y haya sido de provecho. Seguramente,habré dejado algo en el tintero pero, considero, que las seis entradas, presentan Spring Batch y dan a entender la forma de trabajo con esta tecnología. La sencillez, la forma declarativa de definir los procesos y sus elementos, y, el ahorro de trabajo que nos supone su utilización, implica que cuando tengamos que definir un proceso o procesos, pensemos en la opción de Spring Batch.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s