E-mails no Grails?

Este blog deixou de ser mantido, mas o autor continua escrevendo aqui. Não deixe de assinar o novo feed!

199757739_cc27904cc3_m.jpgMinhas experiências com o Grails não podiam ser melhores. Nas minhas brincadeiras aqui, eu achava tudo muito simples de ser feito, já que sempre existia algo prontinho para usar. Só que, na verdade, nem tudo é como eu pensava ser. Isso não é exatamente um problema, visto que o Grails está incorporando features muito legais a cada nova release.

Na minha aplicação “cobaia”, resolvi implementar a funcionalidade Esqueci minha senha, onde o usuário digita seu e-mail e a aplicação re-envia os dados de login do usuário. Como ainda não existe nada no Grails para facilitar o envio de e-mails, então tive de procurar uma outra forma de se fazer isso. Levando em conta a integração maravilhosa entre o Grails e o Spring, a forma mais simples que eu encontrei foi recorrer ao Spring Mail.

Além de mostrar como enviar e-mails text-plain com autenticação SMTP, este post também mostrará como utilizar o Groovy Templates para criar templates das mensagens de e-mail (bem no estilo Velocity).

Seja bem-vindo a mais um post sobre o Grails!

Trabalhando com templates

Quando criamos uma aplicação Grails, o “esqueleto” da mesma é gerado automaticamente. Dentre os arquivos gerados, existe um diretório grails-app/utils, onde devemos codificar as nossas “classes utilitárias”.

Pois bem. Segue abaixo o código de uma classe utilitária chamada MailTemplateUtils, que deve ser criada no diretório grails-app/utils. Esta classe será utilizada trabalhar com arquivos de template dos e-mails:

  1.  
  2. import groovy.text.*
  3.  
  4. class MailTemplateUtils {
  5.  
  6.     /* Processador de templates do Groovy Templates */
  7.     static final def engine = new SimpleTemplateEngine()
  8.  
  9.     /* Dados de localização dos templates */
  10.     def templateDir = "./grails-app/views/email/"
  11.     def extension = ".template"
  12.  
  13.     def parseTemplate(name, objectMap) {
  14.         def file = getTemplateFile(name)
  15.         def text
  16.  
  17.         if (file.exists()) {
  18.  
  19.             /* Processa o template */
  20.             def template = engine.createTemplate(file).make(objectMap)
  21.             text = template.toString()
  22.         }
  23.         text
  24.     }
  25.  
  26.     def getTemplateFile(name) {
  27.         new File("${templateDir}${name}${extension}")
  28.     }
  29. }
  30.  

O import na primeira linha é necessário para que possamos utilizar as classes Groovy Templates para gerar os templates.

Na linha 6 criamos um objeto SimpleTemplateEngine, que será responsável por pegar um template e processá-lo.

Nas linhas 9 e 10 definimos, respectivamente, o diretório onde os templates ficarão disponíveis e o sufixo dos arquivos de template. Vale lembrar que esses valores-padrão e podem ser facilmente alterados em situações divergentes.

Na linha 25 criamos um método que retorna um File que representa um determinado arquivo de template. Por exemplo, se este método for chamado com o parâmetro "newUser", então será criado um objeto File apontando para o arquivo "./grails-app/views/email/newUser.template".

Na linha 12 criamos um método que efetivamente obtém o arquivo correspondente ao template e faz o processamento do mesmo, retornando o resultado do processamento em seguida. Mais detalhes sobre o Groovy Templates podem ser obtidos na sua documentação.

Configurando o Spring

Vamos utilizar agora as classes que o Spring fornece para trabalhar com o envio de e-mails. Antes de mais nada, adicione o JAR do JavaMail no diretório lib da sua aplicação Grails. Em seguida, modifique o arquivo spring/resources.xml de acordo com a listagem abaixo:

  1.  
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
  4.  
  5. <beans>
  6.     <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  7.         <property name="host" value="seu host smtp" />
  8.         <property name="username" value="seu username" />
  9.         <property name="password" value="seu password" />
  10.         <property name="javaMailProperties">
  11.             <value>
  12.                 mail.smtp.auth = true
  13.             </value>
  14.         </property>
  15.     </bean>
  16.  
  17.     <bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage">
  18.         <property name="from" value="no-reply@seusite.com" />
  19.     </bean>
  20. </beans>
  21.  

Definimos dois beans, o mailSender e o mailMessage. O primeiro serviu para definirmos as configurações sobre o envio do e-mail, como host e dados para autenticação. O segundo é basicamente um protótipo de mensagem, já que, teoricamente, todos os e-mails enviados pela aplicação são parecidos em determinados aspectos. No meu caso, o remetente é sempre setado com o endereço no-reply@seusite.com.

Mais detalhes sobre como utilizar o Spring para envio e recebimento de e-mails podem ser conferidos na documentação do Spring.

Criando o serviço para envio de e-mail

Para finalizar a criação do nosso serviço de mailing, criaremos a classe MailerService, no diretório grails-app/services:

  1.  
  2. import org.springframework.mail.*
  3. import org.springframework.mail.javamail.*
  4. import java.util.*
  5.  
  6. class MailerService {
  7.  
  8.     boolean transactional = false // Desabilita contexto transacional
  9.  
  10.     /* Beans do Spring injetados automaticamente */
  11.     JavaMailSenderImpl mailSender
  12.     SimpleMailMessage mailMessage
  13.  
  14.     void send(destinations) {
  15.  
  16.         def messages = []
  17.  
  18.         for (dest in destinations) {
  19.             def msg = new SimpleMailMessage(mailMessage) // "Clona" o objeto
  20.             msg.to = dest.to
  21.             msg.subject = dest.subject
  22.             msg.text = dest.text
  23.  
  24.             if (msg.text == null) {
  25.                 throw new RuntimeException("Trying to send an e-mail with empty body!")
  26.             }
  27.             messages << msg
  28.         }
  29.         mailSender.send(messages as SimpleMailMessage[]) // Envia todos os e-mails de uma vez só
  30.     }
  31. }
  32.  
  33.  

Nas primeiras 3 linhas, definimos alguns imports de classes que são utilizadas pela classe MailerService.

Definimos este serviço como sendo não-transacional, com a declaração na linha 7.

Veja os atributos declarados nas linhas 10 e 11: não inicializamos tais atributos antes de utilizá-los, pois o Grails já o faz automaticamente utilizando os beans do Spring (via auto-wire por tipo). :D

O método declarado na linha 13 é o que faz o serviço sujo. Ele recebe como parâmetro um List de Maps com os dados dos e-mails a serem enviados. Cada objeto Map deve conter as chaves to, subject e text, que são, respectivamente, os destinatários, o assunto e o corpo do e-mail.

List de Maps? Como assim? Veja o código Groovy abaixo:

  1.  
  2. def myListOfMaps = [[key1:"value1", key2:"value2"], [key1:"value1", key2:"value2"]]
  3.  

Ou seja, é um List onde cada um dos elementos é um Map.

Enfim, continuando com a explicação… o método monta um List com os objetos SimpleMailMessage (e-mails a serem enviados) e os envia na instrução da linha 28.

Fazendo funcionar

Primeiramente, precisamos criar um template para que seja possível fazer um teste. Então crie a pasta grails-app/views/email e, dentro desta pasta, crie o arquivo test.template. Edite este arquivo com a linha abaixo:

  1.  
  2. Hello, ${name}!
  3.  

Então, dentro de um Controller qualquer, coloque o código abaixo dentro de uma das actions:

  1.  
  2. class MyController {
  3.  
  4.     /* Serviço é injetado automaticamente */
  5.     MailerService mailerService
  6.  
  7.     def myAction = {
  8.  
  9.         /* Processa o template e envia o resultado via e-mail */
  10.         def mailBody = new MailTemplateUtils()
  11.             .parseTemplate("test", [name:"Juvenal"])
  12.  
  13.         mailerService.send([[to:["seuemail@teste.com"],
  14.             subject:"Subject goes here",text:mailBody]])
  15.     }
  16. }
  17.  

Veja que legal: na linha 4 definimos um atributo mailerService que deverá armazenar uma instância do serviço MailerService. Essa instância é automaticamente injetada pelo Grails.

Na action da linha 6 processamos o template e o enviamos para um endereço de e-mail qualquer.

Para verificar se está tudo funcionando, basta rodar a aplicação e invocar a action em questão. Se tudo foi feito como mostrado, você deverá receber uma linda mensagem “Hello, Juvenal!”. :D

Bom.. então é isso. Fica aí a dica caso alguém precise implementar algo parecido em sua aplicação Grails.

Um abraço a todos!

Foto por: gseven

Tags: , , , , , , , , ,