Dada a grande necessidade de manter usuários de um sistema informados periodicamente com atualizações sobre relatórios, avisos importantes (estoque, notas fiscais, compras e etc.), surge então um propósito para envio de e-mails automatizados para estes tipos de usuários. Imagine, por exemplo, um sistema de gerenciamento de estoque que tem por finalidade gerir todo o estoque de uma empresa, avisando a determinados usuários (por e-mail ou SMS) quais produtos devem ser comprados afim de atender a demanda solicitada.
Vamos muito além disso, atualmente já existem sistemas que realizam todo o pedido de compra quando um estoque atinge o mínimo configurado, enviando um e-mail direto ao fornecedor solicitando a compra de produtos.
Enfim, nosso artigo não vai tratar de estoque, nota fiscais ou quaisquer outras regras específicas de negócio. Iremos criar um módulo eficaz para envio de e-mail em Java.
Antes de iniciarmos vamos apresentar as técnicas que utilizaremos para criar nosso módulo.
Criação de Helpers
Um Helper, como o próprio nome já sugere, é uma classe que irá nos auxiliar nas tarefas comumente utilizadas durante todo o sistema, como por exemplo: Formatação de Datas, Formatação de Números, Validação de valores (CPF, Cartão, CNPJ e etc.) e entre outros.
Em nosso artigo faremos o uso de dois Helpers:
- O primeiro Helper será chamado de ParametroSistemaHelper que será responsável por armazenar informações relativas a configurações do sistema, que em nosso caso, serão configurações relativas ao nosso módulo de e-mail, tais como: porta, e-mail de autenticação, usuário, senha, servidor SMTP e etc.
- O segundo Helper é o foco principal do nosso artigo, será o EmailHelper. Este terá como principal objetivo prover todos os métodos e funcionalidades necessárias para envio de e-mail de forma simples, rápida e ágil. Sem que precisemos ficar codificando parâmetros enormes e difíceis para enviar um simples e-mail, toda a complexidade do envio ficará acoplada nessa classe. Será como um “caixa preta” para quem está enviando um e-mail através do “EmailHelper”.
- Log para monitoramento: Faremos uso do log4j para monitorar os passos do envio de nosso e-mail. Não iremos explicar neste artigo como configurar o log4j, apenas estamos alertando que utilizaremos o mesmo para criar logs da nossa aplicação e realizar posteriores auditorias caso necessário. Você pode optar por ignorar tais recursos ou mesmo utilizar outro de sua preferência.
- SpringFramework para injeção de dependências: Como Framework para injeção de dependências utilizaremos o SpringFramework, que nos ajudará a manipular nossos Helpers e classes responsáveis por consultas no banco de dados.
- JPA e Hibernate: Utilizamos como framework para persistência de dados o JPA juntamente com o Hibernate. Este nos ajudará os parâmetros de sistema onde iremos armazenar os dados para conexão do e-mail.
Estruturando nosso Módulo
Dada as apresentações anteriores, onde mostramos as principais tecnologias e recursos utilizados em nosso artigo, iremos começar a estruturas nosso módulo com a construção dos “Beans”, que nada mais são do que nossas classes Java que irão relacionar-se com nossa tabela no banco de dados e serão utilizadas em toda a aplicação.
Nesse módulo utilizaremos apenas uma classe chamada “ParametroSistema”, onde armazenaremos todas as informações necessárias para conexão com o servidor de e-mail, conforme a Listagem 1.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@Entity
@NamedQueries(value = {
@NamedQuery(name = "ParametroSistema.findByCodigo", query =
"SELECT c FROM ParametroSistema c WHERE c.codigo = :codigo"),
@NamedQuery(name = "ParametroSistema.findAll", query =
"SELECT c FROM ParametroSistema c") })
@Table(name = "parametro_sistema")
public class ParametroSistema {
public static final String FIND_BY_CODIGO = "ParametroSistema.findByCodigo";
public static final String FIND_ALL = "ParametroSistema.findAll";
@Id
@GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof AbstractBean) && (this.id != null) ? id
.equals(((AbstractBean) obj).getId()) : (obj == this);
}
@Column(length = 255, nullable = false)
private String codigo;
@Column(length = 255)
private String descricao;
@Column(length = 255, nullable = false)
private String descricao_campo1;
@Column(length = 255, nullable = false)
private String valor_campo1;
@Column(length = 255)
private String descricao_campo2;
@Column(length = 255)
private String valor_campo2;
@Column(length = 255)
private String descricao_campo3;
@Column(length = 255)
private String valor_campo3;
public String getCodigo() {
return codigo;
}
public void setCodigo(String codigo) {
this.codigo = codigo.toUpperCase().trim();
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public String getDescricao_campo1() {
return descricao_campo1;
}
public void setDescricao_campo1(String descricao_campo1) {
this.descricao_campo1 = descricao_campo1;
}
public String getValor_campo1() {
return valor_campo1;
}
public void setValor_campo1(String valor_campo1) {
this.valor_campo1 = valor_campo1;
}
public String getDescricao_campo2() {
return descricao_campo2;
}
public void setDescricao_campo2(String descricao_campo2) {
this.descricao_campo2 = descricao_campo2;
}
public String getValor_campo2() {
return valor_campo2;
}
public void setValor_campo2(String valor_campo2) {
this.valor_campo2 = valor_campo2;
}
public String getDescricao_campo3() {
return descricao_campo3;
}
public void setDescricao_campo3(String descricao_campo3) {
this.descricao_campo3 = descricao_campo3;
}
public String getValor_campo3() {
return valor_campo3;
}
public void setValor_campo3(String valor_campo3) {
this.valor_campo3 = valor_campo3;
}
}
No classe acima, o campo CODIGO nos ajudará a achar este parâmetro no banco de dados, sem precisar guardar o ID ou algo mais complexo e difícil de lembrar, além disso temos o valor do campo com a sua descrição, podendo contem até três valores no mesmo parâmetro.
Então poderíamos buscar qual a porta de conexão no servidor SMTP apenas sabendo que o código do parâmetro é PORTA_CONEXAO. Veremos mais a frente como fazer isso, através do Helper.
Criando ParametroSistemaHelper
Iremos pular etapas de configuração de conexões com o banco de dados, método de busca, execução de queries ou quaisquer outros recursos que não estejam nesse artigo. É importante apenas saber que temo um classe responsável por realizar todo e qualquer tipo de operação com o banco de dados, deixando nosso módulo mais simples e elegante (Listagem 2).
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component(value = "parametroSistemaHelper")
public class ParametroSistemaHelper {
@Autowired
private BasicDAO dao;
public List<ParametroSistema> getParametroByCodigo(String codigo) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("codigo", codigo);
List<ParametroSistema> parametros = (List<ParametroSistema>) dao
.findByQuery(
"SELECT c FROM ParametroSistema c WHERE c.codigo = :codigo",
map);
if (parametros.size() > 0)
return parametros;
else
return null;
}
public ParametroSistema getUniqueParametroByCodigo(String codigo) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("codigo", codigo);
List<ParametroSistema> parametros = (List<ParametroSistema>) dao
.findByQuery(
"SELECT c FROM ParametroSistema c WHERE c.codigo = :codigo",
map);
if (parametros.size() > 0)
return parametros.get(0);
else
return null;
}
public BasicDAO getDao() {
return dao;
}
public void setDao(BasicDAO dao) {
this.dao = dao;
}
}
Como nossa classe “DAO” que realiza as operações com o banco de dados é injetada e gerenciada pelo Spring, então precisamos deixar que o Spring gerencie também nosso Helper, por isso anotamos o mesmo com a anotação @Component.
Nosso Helper é bem simples mas muito eficaz, ele é composto por 2 métodos básicos que são explicados abaixo:
- public List getParametroByCodigo(String codigo): Este método nos retorna todos os parâmetros que contém determinado CODIGO, podendo ser 1 ou mais.
- public ParametroSistema getUniqueParametroByCodigo(String codigo): Sabendo que 1 CODIGO é único no sistema, então podemos usar esse método que já nos retorna apenas 1 Parâmetro de Sistema.
Nosso Helper criado nessa seção, será utilizado em nosso EmailHelper e você entenderá, se ainda ficou alguma dúvida, como utilizar tal recurso.
Helper Principal – EmailHelper
Chegamos ao ponto principal do nosso artigo. A criação do EmailHelper que será utilizado em qualquer parte do sistema.
Como trata-se de nossa classe principal, iremos detalhar a mesma de forma distinta das mostradas acima, mostrando primeiro a estrutura desta sem nenhum conteúdo nos métodos. Veja a Listagem 3.
@Component(value = "emailHelper")
public class EmailHelper {
private static Logger logger = Logger.getLogger(EmailHelper.class);
@Autowired
private ParametroSistemaHelper parametroSistemaHelper;
public void enviarEmail(String email, String assunto, String msg)
private Session criarSessionMail()
public ParametroSistemaHelper getParametroSistemaHelper() {
return parametroSistemaHelper;
}
public void setParametroSistemaHelper(
ParametroSistemaHelper parametroSistemaHelper) {
this.parametroSistemaHelper = parametroSistemaHelper;
}
}
Deixamos a cargo do Spring, novamente, para gerenciar a instância da nossa classe EmailHelper, através da anotação @Component. Criamos um objeto “logger” para utilizar dentro dos métodos que serão explicados mais a frente em detalhes.
O @Autowired na propriedade parametroSistemaHelper nos garante que o Spring injete a instancia atual da nossa classe ParametroSistemaHelper dentro do nosso EmailHelper, assim poderemos utilizar os métodos deste sem correr o risco de lançar um NullPointerException.
Os métodos get e set são apenas para que o Spring consiga realizar a injeção de dependência de forma correta.
Vamos agora explicar o uso de cada método, começando pelo de mais baixa hierarquia que será utilizado apenas dentro do próprio Helper, o criarSessionMail.
Método criarSessionMail()
Antes de iniciar a explicação desses métodos, você precisa definir alguns parâmetros de sistema para que nosso módulo conecte-se de forma correta ao servidor SMTP. Para isso utilizaremos o já conhecido ParametroSistema, que criamos nas seções anteriores.
O código do nosso parâmetro será SMTP_CONFIG, ou seja, toda vez que buscarmos a lista de parâmetros que possuem o código SMTP_CONFIG, teremos acesso a todas as configurações necessárias para conexão correta no Servidor SMTP. Segue na Listagem 4 as configurações que precisaremos.
SMTP_CONFIG|smtp.gmail.com|mail.smtp.host
SMTP_CONFIG|465|mail.smtp.socketFactory.port
SMTP_CONFIG|javax.net.ssl.SSLSocketFactory|mail.smtp.socketFactory.class
SMTP_CONFIG|true|mail.smtp.auth
SMTP_CONFIG|465|mail.smtp.port
Na Listagem 4 temos lista de configurações que devem ser adicionadas ao ParametroSistema no banco de dados, sendo a coluna 1 o campo CODIGO, a coluna 2 o campo valor_campo1 e a coluna 3 o campo descricao_campo1.
Estamos usando configurações para conexão utilizando o SMTP do Gmail, mas você pode optar por configurar um outra de sua escolha.
Após a configuração da Listagem 4, podemos iniciar a demonstração do método criarSessionMail que fará uso destas. Veja a Listagem 5.
private Session criarSessionMail() {
/*
* Criação do objeto Properties requerido pelo método 'getDefaultInstance'
do objeto Session.
* O nosso objeto 'props' irá armazenar todos os parâmetros de conexão ao
servidor SMTP. Estes parâmetros
* serão recuperados diretamento do banco de dados, como explicamos nas
seções anteriores.
* */
Properties props = new Properties();
/*
* São retornados todos os parâmetros do banco de dados e inseridos no
nosso objeto 'props'.
* Como se fizemos o código abaixo de forma manual, com todos os
parâmetros necessários:
*
* props.put('mail.smtp.host','smtp.gmail.com');
* */
List<ParametroSistema> params = parametroSistemaHelper
.getParametroByCodigo("SMTP_CONFIG");
for (ParametroSistema param : params) {
props.put(param.getDescricao_campo1(), param.getValor_campo1());
}
/*
* Aqui é onde toda a complexidade se concentra e a mágica acontece.
* O objeto Session tem um método chamado 'getDefaultInstance()' que
retorna a instancia padrão do Session
* recebendo como parâmetro 2 argumentos:
* 1 - O primeiro argumento é um objeto Properties que contém a
lista de propriedades para conexão no servidor SMTP
* 2 - O segundo argumento é um objeto Authenticator que contém a
autenticação padrão que será utilizada para conexão no servidor SMTP.
*
* Criamos um objeto Authenticator com a implementação do método
'getPasswordAuthentication()' retornando um objeto PasswordAuthentication.
* Isto é, toda vez que for chamado o método getPasswordAuthentication
do objeto Authenticator, ele irá criar um novo objeto
* PasswordAuthentication com os parâmetros de login e senha de acesso
ao servidor SMTP. *
* */
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
ParametroSistema paramAuthMail = parametroSistemaHelper
.getUniqueParametroByCodigo("EMAIL_AUTENTICACAO");
return new PasswordAuthentication(paramAuthMail.getValor_campo1(),
paramAuthMail.getValor_campo2());
}
});
//Criada nossa Session, ativamos o modo de DEBUG para poder ver o que está
sendo feito em detalhes no envio do email
session.setDebug(true);
//Retornamos a session que será usada para conexão SMTP
return session;
}
Optamos por colocar comentários em todo o método acima para melhorar a explicação do mesmo, visto que ele é um pouco mais complexo do que os outros. Perceba também que temos um parâmetro de código EMAIL_AUTENTICACAO que nada mais é do que um outro parâmetro de sistema que contém o 'e-mail' e senha de conexão ao servidor SMTP.
Onde, valor 1 = e-mail de conexão e valor 2 = senha de conexão.
Com nosso objeto Session criado, vamos iniciar o método responsável por enviar o e-mail.
Método enviarEmail
Enfim chegamos ao “coração” do nosso módulo de envio de e-mails. Onde o método enviarEmail() irá realizar se encarregar de trabalhar com todos os recursos necessários para envio de e-mail de forma adequada. Este irá chamar o método criarSessionMail() explicado na seção anterior. Veja a Listagem 6.
/*
* Devem ser passados 3 argumentos para esse método:
* 1 - email: Email para onde será enviada a mensagem
* 2 - assunto: Qual assunto do email
* 3 - msg: Mensagem completa do email com a formatação desejada (pode conter HTML)
* */
public void enviarEmail(String email, String assunto, String msg)
throws AddressException,
MessagingException {
/*
* Parametrizamos no sistema o Email do Remetente, mas você pode
ficar a vontade para fixar um email direto no código.
* */
String remetente = parametroSistemaHelper
.getUniqueParametroByCodigo("EMAIL_REMETENTE")
.getValor_campo1();
//Trabalhamos usando o objeto 'logger' para identificar que o
nosso método está fazendo
logger.info("__________________________________________________");
logger.info("Enviando email DE: " + remetente + " PARA: " + email);
logger.info("Assunto: " + assunto);
/*
* Chamamos o método 'criarSessionMail()' que retrona um objeto
Session para criarmos um outro objeto do tipo Message
* */
Message message = new MimeMessage(criarSessionMail());
//Configuramos o remetente do email
message.setFrom(new InternetAddress(remetente));
//Configuramos o destinatario do email, que podem ser 1 ou mais
Address[] toUser = InternetAddress.parse(email.trim().toLowerCase());
//Adicionamos os destinatarios ao objeto 'message'
message.setRecipients(Message.RecipientType.TO, toUser);
//adicionamos o assunto da mensagem
message.setSubject(assunto);
//configuramos que a mensagem poderá conter HTML
message.setContent(msg, "text/html");
//envia a mensagem
Transport.send(message);
logger.info("Email enviado com sucesso !");
logger.info("__________________________________________________");
}
Agora temos nosso módulo implementando e criado. Lembre-se que para utilização da nossa classe EmailHelper é necessário a injeção da mesma via Spring no local que você deseja utilizá-la.