Programación en castellano
-Tutoriales

API JavaMail


Utilización del 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("president@whitehouse.gov"));
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.

 
Patrocinados
 

Copyright © 1999-2007 Programación en castellano. Todos los derechos reservados.
Formulario de Contacto - Datos legales - Publicidad
Mantenida por: Claudio y Dani.

Hospedaje web y servidores dedicados linux por Ferca Network

red internet: jugar gratis | amor | navidad 2009 | registro de dominios | servidores dedicados
más internet: comprar | gratis | posicionamiento en buscadores | decoración libre | gifs animados