API JavaMail

Ya hemos visto como trabajar con las partes principales del API JavaMail. En esta p�gina encontraremos aproximaciones de c�mo conectar las piezas para realizar tareas espec�ficas.

.�Enviar Mensajes

Enviar un mensaje de e-mail implica obtener una sesi�n, crear y rellenar un mensaje y enviarlo. Podemos especificar nuestro servidor SMTP configurando la propiedad mail.smtp.host del objeto Properties cuando se obtuvo la Session:

String host = ...;
String from = ...;
String to = ...;

// Get system properties
Properties props = System.getProperties();

// Setup mail server
props.put("mail.smtp.host", host);

// Get session
Session session = Session.getDefaultInstance(props, null);

// Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO, 
  new InternetAddress(to));
message.setSubject("Hello JavaMail");
message.setText("Welcome to JavaMail");

// Send message
Transport.send(message);

Deber�amos situar el c�digo dentro de un bloque try-catch, porque la configuraci�n del mensaje y su env�o pueden lanzar excepciones.

Aqu� tienes el c�digo fuente MailExample.java completo.

.�Leer Mensajes

Para leer mensajes, obtenemos una sesi�n, y nos conectamos con el store apropiado para nuestro mailbox, abrimos la carpeta apropiada, y obtenemos nuestros mensajes. Tambi�n, no debemos olvidarnos de cerrar la conexi�n cuando hayamos terminado.

String host = ...;
String username = ...;
String password = ...;

// Create empty properties
Properties props = new Properties();

// Get session
Session session = Session.getDefaultInstance(props, null);

// Get the store
Store store = session.getStore("pop3");
store.connect(host, username, password);

// Get folder
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);

// Get directory
Message message[] = folder.getMessages();

for (int i=0, n=message.length; i<n; i++) {
   System.out.println(i + ": " + message[i].getFrom()[0] 
     + "\t" + message[i].getSubject());
}

// Close connection 
folder.close(false);
store.close();

Lo que hagamos con cada mensaje es cosa nuestra. El bloque de c�digo anterior s�lo muestra de qui�n viene el mensaje y el subject. T�cnicamente hablando, la lista de direcciones "from" podr�a estar vac�a y la llamada a getFrom()[0] podr�a lanzar una excepci�n.

Para mostrar el mensaje completo, podemos ped�rselo al usuario despu�s de haya visto los campos subject, y luego llamar al m�todo writeTo() si quiere verlo.

BufferedReader reader = new BufferedReader (
  new InputStreamReader(System.in));

// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i<n; i++) {
  System.out.println(i + ": " + message[i].getFrom()[0] 
    + "\t" + message[i].getSubject());

  System.out.println("Do you want to read message? " +
    "[YES to read/QUIT to end]");
  String line = reader.readLine();
  if ("YES".equals(line)) {
    message[i].writeTo(System.out);
  } else if ("QUIT".equals(line)) {
    break;
  }
}

Aqu� tienes el c�digo fuente GetMessageExample.java completo.

.�Borrar Mensajes y Banderas

Borrar mensajes implica trabajar con lasFlags asociadas con los mensajes. Hay diferentes banderas para diferentes estados, algunas definidas por el sistema y otras definidas por el usuario. Las banderas predefinidas se definen en forma de clase interna Flags.Flag y se listan abajo:

  • Flags.Flag.ANSWERED
  • Flags.Flag.DELETED
  • Flags.Flag.DRAFT
  • Flags.Flag.FLAGGED
  • Flags.Flag.RECENT
  • Flags.Flag.SEEN
  • Flags.Flag.USER

S�lo porque una bandera exista no significa que sea soportada por todos los proveedores/servidores de correo. Por ejemplo, excepto la de mensaje borrado, el protocolo POP no soporta ninguna de ellas. Chequear por nuevo correo no es una tarea de POP pero si est� construidad en los clientes de correo. Para conocer las banderas soportadas, solicitamos la carpeta con getPermanentFlags().

Para borrar mensajes, seleccionamo la bandera DELETED del mensaje:

message.setFlag(Flags.Flag.DELETED, true);

Primero debemos abrir la carpeta en modo READ_WRITE:

folder.open(Folder.READ_WRITE);

Luego, cuando hayamos procesado todos los mensajes, cerramos la carpeta, pasando un valor true para purgar todos los mensajes borrados.

folder.close(true);

Hay un m�todo expunge() de Folder que puede usarse para borrar los mensajes. Sin embargo, no funciona con el proveedor POP3 de Sun.

Otros proveedores podr�an o no podr�an implementar estas capacidades. Ser�an m�s que las implementadas por los proveedores de IMAP. Como POP s�lo soporta acceso al mailbox, nosotros tenemos que cerrar la carpeta para borrar los mensajes con el proveedor de Sun.

Para deseleccionar una bandera, s�lo pasando false al m�todo setFlag(). Para ver si una bandera est� seleccionada, lo comprobamos con isSet().

.�Autentificaci�n

Aprendimos anteriormente que podemos usar un Authenticator para pedir un nombre de usuario y una password cuando sea necesario, en vez de pasarlo en strings. Aqu� veremos como hacer un uso m�s �til de la autentificaci�n.

En lugar de conectar al Store con el host, el username, y la password, configuramos las Properties que tiene el host, y le decimos a la Session el ejemplar del Authenticator personalizado:

// Setup properties
Properties props = System.getProperties();
props.put("mail.pop3.host", host);

// Setup authentication, get session
Authenticator auth = new PopupAuthenticator();
Session session = Session.getDefaultInstance(props, auth);

// Get the store
Store store = session.getStore("pop3");
store.connect();

Luego subclasificamos Authenticator y devolvemos un objeto PasswordAuthentication desde el m�todo getPasswordAuthentication(). Abajo tenemos una implementaci�n de dicho m�todo, con un s�lo campo para �mbos. S�lo debemos introducir las dos partes en un campo, separadas por una coma.

import javax.mail.*;
import javax.swing.*;
import java.util.*;

public class PopupAuthenticator extends Authenticator {

  public PasswordAuthentication getPasswordAuthentication() {
    String username, password;

    String result = JOptionPane.showInputDialog(
      "Enter 'username,password'");

    StringTokenizer st = new StringTokenizer(result, ",");
    username = st.nextToken();
    password = st.nextToken();

    return new PasswordAuthentication(username, password);
  }

}

Como el PopupAuthenticator trata con Swing, arrancar� el thread de manejo de eventos para el AWT. Esto b�sicamente requiere que a�adamos una llamada a System.exit() en nuestro c�digo para parar el programa.

.�Responder a Mensajes

La clase Message incluye un m�todo reply() para configurar un nuevo Message con el recipiente apropiado, a�adiendo "Re: " al subject si no est� ya. Esto no a�ade nada al contenido del mensaje, s�lo copia las cabeceras from o reply-to al nuevo recipiente. El m�todo toma un par�metro booleano indicando si la respuesta va aser a uno (false) o a todos (true).

MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("[email protected]"));
reply.setText("Thanks");
Transport.send(reply);

Para configurar la direcci�n reply-to cuando se env�a un mensaje, utilizamos el m�todo setReplyTo().

Aqu� tienes el c�digo fuente ReplyExample.java completo.

.�Re-Enviar Mensajes

Re-enviar mensajes es un poco m�s complicado. No hay una s�la llamada a m�todo, y construimos el mensaje a re-enviar trabajando con las partes que componen un mensaje.

Un mensaje de correo puede estar compuesto de varias partes, cada parte es un BodyPart, o m�s espec�fiamente, un MimeBodyPart cuando se trabaja con mensajes MIME. Las diferentes partes del cuerpo se combinan en un contenedor llamado Multipart o, de nuevo, m�s especificamente un MimeMultipart.

Para re-enviar un mensaje, creamos una parte para el texto de nuestro mensaje y una segunda parte con el mensaje a re-enviar, y las combinamos dentro de un multipart.

Luego a�adimos el multipart a un mensaje direccionado apropiadamente y lo enviamos.

Es esto esencialmente. Para copiar el contenido de un mensaje a otro, s�lo lo copiamos sobre su DataHandler, una clase del JavaBeans Activation Framework.

// Create the message to forward
Message forward = new MimeMessage(session);

// Fill in header
forward.setSubject("Fwd: " + message.getSubject());
forward.setFrom(new InternetAddress(from));
forward.addRecipient(Message.RecipientType.TO, 
  new InternetAddress(to));

// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(
  "Here you go with the original message:\n\n");

// Create a multi-part to combine the parts
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);

// Create and fill part for the forwarded content
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message.getDataHandler());

// Add part to multi part
multipart.addBodyPart(messageBodyPart);

// Associate multi-part with message
forward.setContent(multipart);

// Send message
Transport.send(forward);

.�Trabajar con Attachments

Los Attachments son recursos asociados con un mensaje e-mail, normalmente mantenidos fuera del mensaje, como un fichero de texto, una hoja de c�lculo o una imagen. Al igual que con los programas de e-mail normales como Eudora y Pine, podemos adjuntar recursos a nuestros mensajes con el API JavaMail y obtener dichos attachments cuando recibamos el mensaje.

.�Enviar Attachments

Enviar attachments es bastante sencillo. Construimos las partes de un mensaje completo. Despu�s de la primera parte, el texto del mensaje, a�adimos otras partes donde el DataHandler para cada una es nuestro attachment, en vez de compartir el handler como en el caso de reenvio de mensajes. Si estamos leyendo el attachment desde un fichero, nuestra fuente de datos es un FileDataSource.

Si leemos desde una URL, es una URLDataSource.

Una vez que tenemos nuestro DataSource, se lo pasamos al constructor de DataHandler, y finalmente lo adjutamos al BodyPart con setDataHandler().

Asumiendo que queremos retener el nombre del fichero original para el attachment, la �ltima cosa a hacer es seleccionar el nombre de fichero asociado con el attachment con el m�todo setFileName() de BodyPart. Todo esto lo podemos ver aqu�:

// Define message
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO, 
  new InternetAddress(to));
message.setSubject("Hello JavaMail Attachment");

// Create the message part 
BodyPart messageBodyPart = new MimeBodyPart();

// Fill the message
messageBodyPart.setText("Pardon Ideas");

Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);

// Part two is attachment
messageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);

// Put parts in message
message.setContent(multipart);

// Send the message
Transport.send(message);

Cuando incluimos attachments con nuestros mensajes, si nuestro programa es un servlet, los usuarios deben subir el fichero cuando nos digan donde enviar el mensaje. La subida de cada fichero puede ser manejada con un tipo de formulario codificado multipart/form-data.

<FORM ENCTYPE="multipart/form-data" 
    method=post action="/myservlet"> 
  <INPUT TYPE="file" NAME="thefile">
  <INPUT TYPE="submit" VALUE="Upload">
</FORM>

Nota: El tama�o del mensaje est� limitado por el servidor SMTP, no por el API JavaMail. Si tenemos problemas podemos incrementar el tama�o de la pila seleccionado los par�metros ms y mx.

Aqu� tienes el c�digo fuente MailExample.java completo.

.�Obtener Attachments

Obtener attachments de nuestros mensajes es un poco m�s complicado que enviarlos, ya que MIME no tiene la sencilla noci�n de Attachments. El contenido de nuestro mensaje es un objeto Multipart cuando tiene attachments. Entonces necesitamos procesar cada Part, para obtener el contenido principal del attachment(s). Las partes marcadas con una disposici�n de Part.ATTACHMENT desde part.getDisposition() son claramente attachments. Sin embargo, los attachments tambi�n pueden venir sin disposici�n (y un tipo MIME no texto) o una disposici�n de Part.INLINE. Cuando la disposici�n es Part.ATTACHMENT o Part.INLINE, podemos grabar el contenido de esa parte del mensaje.

Obtenemos el nombre del fichero original con getFileName() y el stream de entrada con getInputStream().

Multipart mp = (Multipart)message.getContent();

for (int i=0, n=multipart.getCount(); i<n; i++) {
  Part part = multipart.getBodyPart(i));

  String disposition = part.getDisposition();

  if ((disposition != null) && 
      ((disposition.equals(Part.ATTACHMENT) || 
       (disposition.equals(Part.INLINE))) {
    saveFile(part.getFileName(), part.getInputStream());
  }
}

El m�todo saveFile() s�lo crea un File desde el nombre del fichero, lee los bytes desde el stream de entrada y los escribe en el fichero. En el caso de que el fichero ya exista, se a�ade un n�mero al final del nombre del fichero hasta que se encuentre uno que no exista.

// from saveFile()
File file = new File(filename);
for (int i=0; file.exists(); i++) {
  file = new File(filename+i);
}

El c�digo de arriba cubre el caso m�s sencillo cuando las partes del mensaje se marcan apropiadamente. Para cubrir todos los casos. Debemos manejar cuando la disposici�n es null y obtener el tipo MIME de la parte para manejarla de forma apropiada.

if (disposition == null) {
  // Check if plain
  MimeBodyPart mbp = (MimeBodyPart)part;
  if (mbp.isMimeType("text/plain")) {
    // Handle plain
  } else {
    // Special non-attachment cases here of image/gif, text/html, ...
  }
...
}

.�Procesar Mensajes HTML

Enviar mensajes basados en HTML puede dar un poco m�s de trabajo que enviar mensaje s�lo de texto, aunque no demaisado. Todo depende de los requerimientos que especifiquemos.

.�Enviar Mensajes HTML

Si todo lo que necesitamos hacer es enviar el equivalente de un fichero HTML como el mensaje y dejar que el lector de correo se preocupe de colocar cualquier imagen embebida o piezas relacionadas, usamos el m�todo setContent() de Message, pasandole el contenido como un String y selecionando el tipo de contenido como text/html.

String htmlText = "<H1>Hello</H1>" + 
  "<img src=\"http://www.jguru.com/images/logo.gif\">";
message.setContent(htmlText, "text/html"));

En la parte del receptor, si procesamos el mensaje con el API JavaMail, no hay nada dentro del API para mostrar mensajes HTML. El API JavaMail s�lo ve un stream de bytes. Para mostrar el mensaje HTML, debemos usar un JEditorPane de Swing o cualquier otro componente visualizador de terceras partes.

if (message.getContentType().equals("text/html")) {
  String content = (String)message.getContent();
  JFrame frame = new JFrame();
  JEditorPane text = new JEditorPane("text/html", content);
  text.setEditable(false);
  JScrollPane pane = new JScrollPane(text);
  frame.getContentPane().add(pane);
  frame.setSize(300, 300);
  frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
  frame.show();
}

.�Includir Im�genes en Nuestos Mensajes

Por otro lado, si queremos que nuestro mensaje HTML sea completo, con imagenes embebidas incluidas como parte del mensaje, debemos tratar las im�genes como un attachment y referenciarlas con una URL especial cid, donde el cid es una referencia a la cabecera Content-ID de la imagen adjunta.

El proceso de embeber im�genes es muy similar a adjuntar un fichero a un mensaje, la �nica diferencia es que tenemos que decirle al MimeMultipart que las partes est�n relacionadas configurando su subtipo en el constructor (o con setSubType()) y configurando la cabecera Content-ID de la imagen a un string aleatorio que es usado como el src de la imagen en la etiqueta img.

Este c�digo explica todo lo anterior:

String file = ...;

// Create the message
Message message = new MimeMessage(session);

// Fill its headers
message.setSubject("Embedded Image");
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO, 
  new InternetAddress(to));

// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
String htmlText = "<H1>Hello</H1>" + 
  "<img src=\"cid:memememe\">";
messageBodyPart.setContent(htmlText, "text/html");

// Create a related multi-part to combine the parts
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(messageBodyPart);

// Create part for the image
messageBodyPart = new MimeBodyPart();

// Fetch the image and associate to part
DataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID","memememe");

// Add part to multi-part
multipart.addBodyPart(messageBodyPart);

// Associate multi-part with message
message.setContent(multipart);

Aqu� tienes el c�digo fuente HtmlImageExample.java completo.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR

SIGUIENTE ARTÍCULO