Enviar un correo electrónico con Java es algo muy sencillo, ya existe una librería que nos facilita la tarea y es la que usaremos hoy.  Las librerías necesarias son las siguientes:

  • javax.mail-api 1.6.2
  • javax.mail 1.6.0

Ya sea que descarguemos dichas librerías o que las incluyamos en nuestro archivo pom.xml (en caso de usar un proyecto Maven). Si optamos por lo segundo debemos incluir el siguiente código:

<dependencies>
	<dependency>
		<groupId>javax.mail</groupId>
		<artifactId>javax.mail-api</artifactId>
		<version>1.6.2</version>
	</dependency>
	<dependency>
		<groupId>com.sun.mail</groupId>
		<artifactId>javax.mail</artifactId>
		<version>1.6.0</version>
	</dependency>
</dependencies>

Ahora construimos nuestro proyecto para que descargue las librerías.

Ya que haya terminado la construcción, creamos la clase que tendrá la lógica para el envío de correos, la cual llamaremos EnviarCorreo, esta clase debe extender de Thread para que la ejecución se haga en segundo plano.

public class EnviarCorreo  extends Thread implements Serializable {
	//TODO
}

Para manejar la información que recibimos podemos crear una clase pojo que contenga los parámetros necesarios para enviar un correo, los cuales son:

  • Destinatario(s).
  • Asunto.
  • Contenido.
  • Adjunto(s).

Dicha clase debe contener las propiedades anteriormente mencionadas, como se muestra a continuación:

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class CorreoDTO {

	private List<String> destinatarios;
    private String titulo;
    private String contenido;
    private List<File> adjuntos;

    public CorreoDTO() {
        destinatarios = new ArrayList<>();
        adjunto = new ArrayList<>();
    }

    public List<String> getDestinatarios() {
        return destinatarios;
    }

    public void setDestinatarios(List<String> destinatarios) {
        this.destinatarios = destinatarios;
    }

    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    public String getContenido() {
        return contenido;
    }

    public void setContenido(String contenido) {
        this.contenido = contenido;
    }

    public List<File> getAdjuntos() {
        return Adjuntos;
    }

    public void setAdjuntos(List<File> adjuntos) {
        this.adjunto = adjunto;
    }
}

En nuestra clase EnviarCorreo debemos recibir una instancia de nuestro CorreoDto, en mi caso la voy a recibir desde el constructor y la asignare a una variable global de la misma.

public EnviarCorreo(CorreoDTO arg0) {
	super("procesoEnvioEmail");
	dto = arg0;
}

En nuestra clase EnviarCorreo adicionalmente tendremos tres métodos más, sin contar el método run que heredamos de la clase Thread:

  • connectServer: el cual establece la conexión con el servidor de correos y retorna una sesión.
  • getDirecciones: Nos va a convertir nuestra lista de correos a una cadena separada por coma (email@correo.com, email1@correo.com) .
  • sendEmail: Método que se encarga del envío del correo.

Comenzamos con el método conectar, a continuación, el código completo:

private Session connectServer() {
	// propiedades de conexion
	Properties props = new Properties();
	props.put("mail.smtp.auth", true);
	props.put("mail.smtp.starttls.enable", "true");
	props.put("mail.smtp.host", "smtp.gmail.com");
	props.put("mail.smtp.port", "25");
	props.put("mail.smtp.ssl.trust", "smtp.gmail.com");
	
	return Session.getInstance(props, new Authenticator() {
	
		@Override
		protected PasswordAuthentication getPasswordAuthentication() {
			return new PasswordAuthentication(username, password);
		}
	});
}

Dónde el username y password son propiedades globales y deben tener los datos de nuestra cuenta de Gmail.

Ahora en nuestro método getDirecciones veremos cómo generar la cadena separada por coma, es algo realmente sencillo de hacer, solo debemos recorrer la lista e ir concatenando a un String los valores.

private String getDirecciones(CorreoDTO dto) {
	StringBuilder direcciones = new StringBuilder();
	boolean primero = true;
	for (String correo : dto.getDestinatarios()) {
		if (primero) {
			direcciones.append(correo);
			primero = !primero;
		} else {
			direcciones.append(",");
			direcciones.append(correo);
		}
	}
	return direcciones.toString();
}

Si usamos Java 8 o superior podemos hacerlo mediante un stream, como se muestra a continuación:

private String getDirecciones(CorreoDTO dto) {
	return dto.getDireccionCorreo().stream().collect(Collectors.joining(","));
}

Cualquiera de las dos formas es funcional, solo que una es más limpia a simple vista.

Ahora debemos construir el mensaje y enviarlo haciendo uso de nuestros métodos anteriores, esto lo haremos dentro del método sendEmail.

private void sendEmail(CorreoDTO dto) {
	// Obtenemos las direcciones/destinatarios
	String direcciones = getDirecciones(dto);
	// Obtenemos la conexion al servidor de correos
	Session session = connectServer();
	try {
		MimeMessage msg = new MimeMessage(session);
		//Agregamos los headers necesarios
		msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
		msg.addHeader("format", "flowed");
		msg.addHeader("Content-Transfer-Encoding", "8bit");
		// Asignamos el asunto del correo
		msg.setSubject(dto.getTitulo(), "UTF-8");
		// Asignamos la hora de creacion del correo
		msg.setSentDate(new Date());

		Multipart multiparte = new MimeMultipart();
	
		//Creamos el cuerpo del mensaje
		BodyPart cuerpoMensaje = new MimeBodyPart();
		cuerpoMensaje.setContent(dto.getContenido(), "text/html");
		multiparte.addBodyPart(cuerpoMensaje);
		
		//Validamos si tenemos adjuntos
		if (dto.getAdjunto() == null ? false : !dto.getAdjunto().isEmpty()) {
		// Si tenemos adjuntos los agregamos al mensaje
			for (File file : dto.getAdjunto()) {
				cuerpoMensaje = new MimeBodyPart();
				cuerpoMensaje.setDataHandler(new DataHandler(new FileDataSource(file)));
				cuerpoMensaje.setFileName(file.getName());
				multiparte.addBodyPart(cuerpoMensaje);
			}
		}
		msg.setContent(multiparte);
		msg.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(direcciones));
		
		//Agregamos el origen del mensaje (nuestro email)
		msg.setFrom(new InternetAddress(username));
		
		//Enviamos el mensaje
		Transport.send(msg);
	} catch (MessagingException e) {
		e.printStackTrace();
	}
}

Por último, debemos mandar a llamar este método en algún lado. Si recordamos, nuestra clase extiende de Thread, por lo que la llamada la debemos realizar desde el método run, para ello debemos sobrescribirlo.

public void run() {
	sendEmail(dto);
}

Para terminar, mostraré un ejemplo de como mandar a llamar a nuestra clase, esto puede ser desde un método main o en su defecto en donde lo requiera nuestra lógica.

CorreoDTO dto = new CorreoDTO();
dto.getDireccionCorreo().add(“correo@domain.com”);
dto.setContenido(“Contenido del correo”);
dto.setTitulo(“Asunto del correo”);
dto.setAdjunto(new ArrayList<File>());
dto.getAdjunto().add(new File("PATH_FILE"));

EnviarCorreo enviarCorreo = new EnviarCorreo(dto);
enviarCorreo.start();

*Nota: si queremos simplificar el envío del correo, nos podemos crear otra clase para envolver todo lo anterior y simplificar el llamado.

import java.io.File;
import java.util.List;

public class MailSender {
	
	public static void sendEmail(String to, String subject, String body) {
		sendEmail(to, subject, body, null);
	}
	
	public static void sendEmail(String to, String subject, String body, List<File> adjuntos) {
		 
		try {
			CorreoDTO dto = new CorreoDTO();
			dto.getDestinatarios().add(to);
			dto.setContenido(body);
			dto.setTitulo(subject);
			if(adjuntos != null && !adjuntos.isEmpty())
				dto.setAdjuntos(adjuntos);
			
			EnviarCorreo enviarCorreo = new EnviarCorreo(dto);
			enviarCorreo.start();
		} catch (Exception e) {
			logger.error("", e);
		}
	}
    
}

Con esto el llamado se vuelve más sencillo, basta con usar el nombre de la clase y apuntar al método correspondiente.

//Envio de email sin archivos
MailSender.sendEmail("mail@domail.com", "Asunto", "Contenido del mensaje");
//Envio de email con archivos
MailSender.sendEmail("mail@domail.com", "Asunto", "Contenido del mensaje", Collections.singletonList(new File("PATH_TO_FILE")));