Spring Mail & Apache Velocity
Muitas vezes, quando se desenvolve um serviço de e-mail por um aplicativo, existe a necessidade de se trabalhar com templates. Estes templates, são como páginas HTML que são enviadas para os destinatários. O Spring Framework oferece uma implementação da especificação Java, para envio de email, que torna essa ação bem mais simples. Já o Apache Velocity, chamado de Template Engine, é uma biblioteca Java que adiciona dinamicamente objetos Java dentro do conteúdo de uma página HTML. Neste tutorial você vai ver como integrar o Velocity ao Spring e como enviar os dados referentes ao conteúdo do seu e-mail para o template HTML que será enviado por você.
1. Preparando o projeto
As bibliotecas para criar o projeto, serão apresentadas conforme um projeto Maven, e são as seguintes dependências:
<dependencies> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-mail</artifactId> <version>4.0.3.RELEASE</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency> </dependencies>
2. Iniciando o projeto
Neste tutorial vamos criar uma classe chamada MailMessage que será a classe de domínio. Os atributos adicionados a esta classe serão aqueles que vão armazenar as informações para o envio de uma mensagem por e-mail.
package com.mballem.tutorial.domain;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Marcio Ballem on 05/08/2014.
*/
public class MailMessage {
private String from;
private String personal;
private String text;
private String subject;
private List<String> attachments = new ArrayList<String>();
private String to;
// gerar os métodos getters e setters.
//adicionar os anexos da mensagem
public void addAttachment(String... files) {
for (String file : files) {
this.attachments.add(file);
}
}
}
Na Listagem 1 você pode ver os atributas da classe MailMessage. Entre estes atributos há o attachments, o qual vai armazenar uma lista de arquivos que podem ser enviados pela mensagem como anexo. O atributo from recebe o e-mail de quem está enviando a mensagem, e o atributo personal, um nome qualquer dado a quem envia a mensagem. O atributo text vai receber o texto a ser enviado na mensagem e o subject é o assunto. Por fim, o atributo to endereça a mensagem a um destinatário.
Antes de apresentar outras classes, veja na Listagem 2 o conteúdo do arquivo spring-config.xml com a configuração do bean mailSender e do bean velocityEngine. O bean mailSender recebe as propriedades necessárias para configuração da conta de e-mail que vai enviar as mensagens.
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.mballem.tutorial" />
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="seu_email@gmail.com" />
<property name="password" value="sua_senha" />
<property name="javaMailProperties">
<props>
<prop key="mail.transport.protocol">smtp</prop>
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties">
<value>
resource.loader=class
class.resource.loader.class=
org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
</value>
</property>
</bean>
</beans>
Já o bean velocityEngine é uma configuração necessária para integrar o Apache Velocity ao Spring Framework. A configuração adicionada a velocityProperties informa ao Velocity onde ele deve buscar o template, neste caso, será no classpath da aplicação.
Na Listagem 3, você pode visualizar o código HTML que será usado como template para o envio dos e-mails. Este arquivo, embora tenha conteúdo HTML, deve ser do tipo .vm e não .html. Observe que entre o código HTML existem algumas instruções como na tag <title>${to}</title>. Estas instruções tem por objetivo adicionar ao código, o conteúdo do objeto MailMessage.
<!DOCTYPE HTML>
<html>
<head>
<meta content="text/html" http-equiv="content-type">
<title>${to}</title>
</head>
<body style="padding-top: 20px; padding-bottom: 40px;">
<div style="background-color: #ebebeb; border: 1px solid #CCC; margin-bottom: 8px;
margin-top: 10px; margin-right: 8px; margin: 0 auto; max-width: 760px;">
<div style="background-color:#9ACD32; border: 1px solid #CCC; margin-bottom: 10px;
margin-top: 10px; margin-left: 8px; margin-right: 8px;
font-family: Consolas; padding-bottom:1px; padding-top:1px;">
<h2 style="padding-left: 2%">${subject}</h2>
</div>
<div style="background-color:#fff; border: 1px solid #CCC; margin-bottom: 10px;
margin-left: 8px; margin-right: 8px; font-family:Arial;
font-size:12px; line-height:18px; text-align:left;">
<h3 style="padding-left: 2%">
<strong> Date: </strong>${date}
</h3>
<h3 style="padding-left: 2%">
<strong> From: </strong>${personal} - ${from}
</h3>
<p style="padding-left: 2%; text-align: left;
font-family: 'Courier New', Monospace; font-size: 12pt">
${text}
</p>
</div>
</div>
</body>
</html>
Observe ainda o restante do código e verá as instruções ${subject}, ${from}, ${date}, ${personal}, e ${text}. É desta forma que o Velocity adiciona o conteúdo do objeto MailMessage ao código HTML. Para que os valores do objeto MailMessage chegue a estas instruções, é necessário usar o método mergeTemplateIntoString() da classe VelocityEngineUtils. Na classe VelocityService – Listagem 4 – o bean velocityEngine é injetado pelo Spring via método construtor.
Nesta mesma classe, a propriedade model, do tipo java.util.Map é adiciona para que os valores do objeto MailMessage sejam atribuidos ao método mergeTemplateIntoString(). As atribuições necessárias a este método são, o obejto velocityEngine, o nome do arquivo .vm, o encoding que o HTML será enviado, e o objeto model.
package com.mballem.tutorial.service;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.velocity.VelocityEngineUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Marcio Ballem on 05/08/2014.
*/
@Service
public class VelocityService {
private static final String ENCODING = "UTF-8";
private static final String TEMPLATE_URL = "velocity/mail.vm";
private VelocityEngine velocityEngine;
@Autowired
public VelocityService(VelocityEngine velocityEngine) {
this.velocityEngine = velocityEngine;
}
private Map<String, Object> model = new HashMap<String, Object>();
public void setModel(Map<String, Object> model) {
this.model = model;
}
public String getVelocityBody() {
return VelocityEngineUtils.mergeTemplateIntoString(
this.velocityEngine,
TEMPLATE_URL,
ENCODING,
this.model
);
}
}
O método mergeTemplateIntoString() retorna um objeto do tipo java.lang.String que deverá ser atribuído ao corpo da mensagem de e-mail que será enviada ao destinatário. Para enviar a mensagem de e-mail, veja a classe MailService na Listagem 5.
package com.mballem.tutorial.service;
import com.mballem.tutorial.domain.MailMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Marcio Ballem on 05/08/2014.
*/
@Service
public class MailService {
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private VelocityService velocityService;
public void sendMail(MailMessage message) {
Date date = new Date();
message.setFrom( ((JavaMailSenderImpl) javaMailSender).getUsername() );
//objeto que recebe os dados a serem enviados na mensagem.
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
//quando trabalhamos com anexos, devemos usar um objeto
// do tipo MimeMessageHelper.
MimeMessageHelper helper;
//adiciona ao template do velocity
Map<String, Object> model = new HashMap<String, Object>();
model.put("subject", message.getSubject());
model.put("text", message.getText());
model.put("to", message.getTo());
model.put("personal", message.getPersonal());
model.put("from", message.getFrom());
model.put("date", date);
velocityService.setModel(model);
try {
//inicializamos o obejto helper.
helper = new MimeMessageHelper(mimeMessage, true);
//inserimos dados de quem está enviando a mensagem.
helper.setFrom(message.getFrom(), message.getPersonal());
//inserimos o destinatario
helper.setTo(message.getTo());
//inserimos a data de envio
helper.setSentDate(date);
//inserimos o texto da mensagem,
// o atributo true indica que será em html
// e adicionamos o velocity no corpo da mensagem.
helper.setText(velocityService.getVelocityBody(), true);
//inserimos o assunto da mensagem.
helper.setSubject(message.getSubject());
//inserimos os anexos adicionados a lista de anexos.
// Fazemos um for() na lista para adicionar um anexo por vez.
for (String anexo : message.getAttachments()) {
File attach = new File(anexo);
helper.addAttachment("Attachment: " + attach.getName(), attach);
}
//objeto de envio da mensagem.
javaMailSender.send(mimeMessage);
System.out.println("Envio com Sucesso!");
} catch (MailException e) {
System.out.println("Email não pode ser eviado!n" + e.getMessage());
} catch (MessagingException e) {
System.out.println("Email não pode ser eviado.n" + e.getMessage());
} catch (IOException e) {
System.out.println("Anexo não encontradon" + e.getMessage());
}
}
}
Dois beans são injetados na classe MailService, um deles é o bean mailSender, do arquivo spring-confi.xml, e ou outro é referente a classe VelocityService. O objeto mailSender vai fornecer acesso ao método send(), para enviar o e-mail. Nesta parte do envio, leia os comentários adicionados ao código fonte.
O que tem de mais importante nesta classe é o método setText() do objeto helper. Este método recebe como valor o objeto velocityService.getVelocityBody(), o qual tem como retorno a String do método mergeTemplateIntoString(). Deste modo, o código HTML do arquivo mail.vm será adicionado ao corpo da mensagem, e nele já teremos os valores do objeto MailMessage adicionados via método velocityService.setModel().
As chaves do objeto model são as instruções (${}), presentes no código HTML. Assim, a instrução no código HTML deve ter o mesmo nome da chave do objeto model.
Veja na Figura 1, você pode ver a estrutura do projeto apresentado:
Figura 1 – Estrutura do projeto.
Analisando a imagem da Figura 1, veja que o arquivo de configuração spring-config.xml foi adicionado no diretório resources do projeto Maven. O arquivo template mail.vm, está inserido no diretório resources/velocity. Na Listagem 4, a variável TEMPLATE_URL é atribuída com o valor velocity/mail.vm. O resources não precisa ser atribuído como valor porque no arquivo spring-config.xml foi informado ao Velocity que ele deveria procurar pelo template lá no resources (classpath).
As classes SpringAccess e Application são necessárias para executar a aplicação. Na Listagem 6 temos a classe SpringAccess que inicializa os serviços do Spring Framework.
package com.mballem.tutorial;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* Created by Marcio Ballem on 05/08/2014.
*/
public class SpringAccess {
private static ApplicationContext springContext;
private SpringAccess() {
}
public static synchronized ApplicationContext getSpringContext() {
if (springContext == null) {
springContext = new FileSystemXmlApplicationContext(
"classpath:spring-config.xml"
);
}
return springContext;
}
}
Já a classe Application tem como objetivo testar o envio de e-mail. Para isso, um código simples via modo console foi adicionado para receber os dados referentes ao envio da mensagem. Veja mais na Listagem 7:
package com.mballem.tutorial;
import com.mballem.tutorial.domain.MailMessage;
import com.mballem.tutorial.service.MailService;
import org.springframework.context.ApplicationContext;
import javax.swing.*;
import java.util.Scanner;
/**
* Created by Marcio Ballem on 05/08/2014.
*/
public class Application {
private MailService mailService;
private JFileChooser chooser;
public Application(MailService mailService) {
this.mailService = mailService;
}
public static void main(String[] args) {
ApplicationContext ctx = SpringAccess.getSpringContext();
Application app = new Application(ctx.getBean(MailService.class));
app.sendMail();
}
private void sendMail() {
Scanner scanner = new Scanner(System.in);
MailMessage message;
int option;
do {
System.out.println("1. Send mail");
System.out.println("0. Exit");
System.out.print("> ");
option = Integer.parseInt(scanner.nextLine());
if (option == 1) {
message = new MailMessage();
System.out.print("Sender name: ");
message.setPersonal(scanner.nextLine());
System.out.print("To: ");
message.setTo(scanner.nextLine());
System.out.print("Subject: ");
message.setSubject(scanner.nextLine());
System.out.print("Message: ");
message.setText(scanner.nextLine());
System.out.print("Attachments (Y or N): ");
if (scanner.nextLine().equalsIgnoreCase("Y")) {
String[] attachments = addAttachemts();
message.addAttachment(attachments);
}
mailService.sendMail(message);
}
} while (option != 0);
}
private String[] addAttachemts() {
chooser = new JFileChooser();
chooser.setMultiSelectionEnabled(true);
String[] paths = new String[chooser.getSelectedFiles().length];
int retorno = chooser.showSaveDialog(null);
if (retorno == JFileChooser.APPROVE_OPTION) {
paths = chooser.getSelectedFile().getAbsolutePath().split(",");
}
return paths;
}
}
Na Figura 2 você pode visualizar o processo de envio de uma mensagem por meio da classe Application e na Figura 3, veja como esse e-mail é recebido pelo destinatário.
Figura 2 – Exemplo do envio de e-mail.
Na Figura 3 os campos em destaque mostram de onde os dados foram adicionados a partir do template mail.vm.
Figura 3 – Exemplo de e-mail recebido.
Este tutorial abordou o básico para a configuração e envio de mensagens com Spring Mail e o Apache Velocity. Em relação ao Velocity, você pode ainda utilizar outras instruções, como lógicas (if/else) ou mesmo fazer loop em listas uti




