Java Mail: Criando um módulo para envio de Email em Java

Veja neste artigo, em detalhes, como criar um módulo eficaz para envio de e-mail em qualquer situação necessária utilizando a linguagem Java.

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.

Confira nossos cursos de java ou se preferir pode acompanhar nossa formação Java.

Em nosso artigo faremos o uso de dois Helpers:

  1. 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.
  2. 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”.

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; } }
Listagem 1. Classe ParametroSistema

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.

Ex: Codigo = 'PORTA_CONEXAO', Valor Campo 1 = 451, Descricao Campo 1 = Porta de Conexão no Servidor SMTP

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; } }
Listagem 2. Classe ParametroSistemaHelper

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:

  1. public List getParametroByCodigo(String codigo): Este método nos retorna todos os parâmetros que contém determinado CODIGO, podendo ser 1 ou mais.
  2. 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; } }
Listagem 3. Estrutura da Classe EmailHelper

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
Listagem 4. Configurações do SMTP_CONFIG

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; }
Listagem 5. Método criarSessionMail comentado

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("__________________________________________________"); }
Listagem 6. Método enviarEmail comentado

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.

Artigos relacionados