JDC Tech Tips (23 de Octubre del 2001)

Bienvenido a los Consejos Técnicos de la Conexión del Desarrollador Java (JDC), del 23 de Octubre de 2001. Esta edición cubre:

  • Ordenar Listas.
  • Enviar e-mail con el API JavaMaiñ.

Estos consejos fueron desarrollados usando Java(tm) 2 SDK, Standard Edition v 1.3

Se puede ver esta edición (en su original en inglés) de los Consejos Técnicos en formato html en http://java.sun.com/jdc/JDCTechTips/2001/tt1023.html

Ordenar Listas

Las implementaciones de List encontradas en la Java Collections Framework son Vector, ArrayList, y LinkedList. Estas colecciones ofrecen acceso indexado para grupos de objetos. Proporcionan soporte para añadir y eleminar elementos. Sin embargo no ofrecen soporte interno para ordenar elementos.

Para ordenar elementos List podemos usar el método sort() de la clase java.util.Collections. Podemos pasarle al método un objeto List, o podemos pasarle una List y un Comparator. Si los elementos de la List son todos del mismo tipo de clase y esa clase implementa el interface Comparable, simplemente podemos llamar a Collections.sort(). Si, sin embargo, la clase no implementa Comparator, podemos pasarle al método sort() un Comparator para hacer la ordenación. También podríamos querer pasar al método sort() un Comparator si no queremos uar el orden de ordenación por defefecto para la clase. Si los elementos de la List no son del mismo tipo de clase, no estámos de suerte en cuanto a la ordenación -- a menos que escribamos una clase Comparator especial.

¿Qué pasa con el orden de ordenación? Si los elementos son objetos String, el orden de ordenación por defecto está basado en los códigos de los caracteres, básicamente el valor ASCII/Unicode de cada caracter. Si trabajamos sólamente en inglés, el orden por defecto es suficiente porque ordenará primero desde A-Z, seguido por las letrás minúsculas a-z. Sin embaego, si trabajamos con palabras no inglesas, o sólo queremos un orden diferente de ordenación, aquí es donde viene la segunda variedad de Collections.sort(). Por ejemplo, digamos que queremos ordenar los elementos de la List en el orden inverso natural de los strings. Para hacer esto, podemos obtener un Comparator inverso de la clase Collections con reverseOrder(). Luego, le pasamos el Comparator inverso al método sort(). En otras palabras, hacemos lo siguiente:

    List list = ...;
    Comparator comp = Collections.reverseOrder();
    Collections.sort(list, comp);

Si la lista contiene los términos (Man, man, Woman, y woman), la lista ordenada sería Man, Woman, man, woman. Nada demasiado complicado hasta aquí. Una cosa importante a recordar es que Collections.sort() hace una ordenación en el lugar. Si necesitamos retener el orden original, hacemos una copia de la colección primero, y luego la ordenamos, de esta forma:

    List list = ...;
    List copyOfList = new ArrayList(list);
    Collections.sort(copyOfList);

Aquí, la lista ordenada es Man, Woman, man, woman, pero la lista original (Man, man, Woman, y woman) es retenida.

Hasta ahora la ordenación ha sido sensible a las mayúsculas. ¿Cómo podríamos hacer una ordenación insensible a las mayúsculas? Una forma es implementar un Comparator como este:

    public static class CaseInsensitiveComparator 
        implements Comparator {
      public int compare(Object element1, 
          Object element2) {
        String lower1 = 
          element1.toString().toLowerCase();
        String lower2 = 
          element2.toString().toLowerCase();
        return lower1.compareTo(lower2);
      }
    }

Realmente no tenemos que crear manualmente esta clase. En su lugar podemos usar el Comparator preexistente, CASE_INSENSITIVE_ORDER de la clase String.

Hay un ligero problema con esta aproximación. El algoritmo sort() proporciona ordenación estable, manteniendo los elementos iguales en el orden original. Esto significia que una lista que contenga dos elementos "woman" y "Woman" ordenaría de forma diferente dependiendo de cúal de los dos elementos apareciera primero en la lista.

¿Qué hay sobre los diferentes idiomas? El paquete java.text proporciona las clases Collator y CollationKey para realizar ordenaciones sensibles al idioma. Aquí tenemos un ejemplo:

    public static class CollatorComparator 
        implements Comparator {
      Collator collator = Collator.getInstance();
      public int compare(Object element1, 
          Object element2) {
        CollationKey key1 = collator.getCollationKey(
          element1.toString());
        CollationKey key2 = collator.getCollationKey(
          element2.toString());
        return key1.compareTo(key2);
      }
    }

Observa que si nuestro texto es para una localidad distinta a la localidad por defecto, neesitamos pasar la localidad al método getInstance(), de esta forma:

    Collator collator = Collator.getInstance(Locale.JAPANESE);

En lugar de ordenar sobre el string actual, ordenamos sobre una clave collation. Esto no sólo proporciona ordenación consistente insensible a las mayúsculas, sino que también funciona con distintos idiomas. En otras palabras, si ordenamos una combinación de palabras españolas y no españolas, la palabra "mañana" irá antes de "mantra". Si no usamos un Collator, "mañana" irá después de "mantra".

Aquí está el programa que hace varios tipos de ordenaciones (por defecto, insensible a las mayúsculas, e insensible al idioma) de una lista:

import java.awt.BorderLayout;
import java.awt.Container;
import java.io.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

public class SortIt {
    
  public static class CollatorComparator 
      implements Comparator {
    Collator collator = Collator.getInstance();
    public int compare(Object element1, 
        Object element2) {
      CollationKey key1 = collator.getCollationKey(
        element1.toString());
      CollationKey key2 = collator.getCollationKey(
        element2.toString());
      return key1.compareTo(key2);
    }
  }

  public static class CaseInsensitiveComparator 
      implements Comparator {
    public int compare(Object element1, 
        Object element2) {
      String lower1 = element1.toString().
        toLowerCase();
      String lower2 = element2.toString().
        toLowerCase();
      return lower1.compareTo(lower2);
    }
  }

  public static void main(String args[]) {
    String words[] = 
      {"man", "Man", "Woman", "woman", 
       "Manana", "manana", "mañana", "Mañana",
       "Mantra", "mantra", "mantel", "Mantel"
      };

    // Create frame to display sortings
    JFrame frame = new JFrame("Sorting");
    frame.setDefaultCloseOperation(
      JFrame.EXIT_ON_CLOSE);
    Container contentPane = frame.getContentPane();
    JTextArea textArea = new JTextArea();
    JScrollPane pane = new JScrollPane(textArea);
    contentPane.add(pane, BorderLayout.CENTER);

    // Create buffer for output
    StringWriter buffer = new StringWriter();
    PrintWriter out = new PrintWriter(buffer);

    // Create initial list to sort
    List list = new ArrayList(Arrays.asList(words));
    out.println("Original list:");
    out.println(list);
    out.println();

    // Perform default sort
    Collections.sort(list);
    out.println("Default sorting:");
    out.println(list);
    out.println();

    // Reset list 
    list = new ArrayList(Arrays.asList(words));

    // Perform case insensitive sort
    Comparator comp = new CaseInsensitiveComparator();
    Collections.sort(list, comp);
    out.println("Case insensitive sorting:");
    out.println(list);
    out.println();

    // Reset list
    list = new ArrayList(Arrays.asList(words));

    // Perform collation sort
    comp = new CollatorComparator();
    Collections.sort(list, comp);
    out.println("Collator sorting:");
    out.println(list);
    out.println();

    // Fill text area and display
    textArea.setText(buffer.toString());
    frame.pack();
    frame.show();
  }
}

Si nuestro problema principal es el acceso ordenado, quizás una List no es la mejor estructura de datos para nosotros. Mientras nuestra colección no tenga duplicados, podemos mantener los elementos en un TreeSet (con o sin proporcionar un Comparator). Luego, los elementos siempre estarán ordenados.

Para más información sobre ordenar Lists, puedes ver "Introduction to the Collections Framework"

Enviar e-mail con el API JavaMail

El API JavaMail es una parte estándard de la plataforma Java 2, Enterprise Edition (J2EE). El API es descargable desde http://java.sun.com/products/javamail/. Como su nombre sugiere, el API JavaMail proporciona soporte para enviar y recibir mensajes. Todavía tendremos que conectar con nuestro servidor de correo para enviar y recibir mensajes porque el API JavaMail no es un servidor de correo, pero el API si proporciona el marco para hacer esa conexión.

Enviar e-mail implica entender al menos cuatro clases:

Session
Message
Address
Transport

Y, si estámos interesados en incluir un attachment con nuestro mensaje, necesitamos conocer unas pocas más:

Multipart 
BodyPart
DataSource
DataHandler

La clase DataSource y sus implementaciones FileDataSource y URLDataSource son parte del JavaBeans Activation Framework. La clase DataHandler también forma parte del marco de trabajo. El JavaBeans Activation Framework está incluido con el J2EE, pero también es descargable desde http://java.sun.com/products/javabeans/glasgow/jaf.html. El API JavaMail requieres la librería del JavaBeans Activation Framework.

Buscando las clases especifiadas en el API JavaMail, primero está la clase Session. Ésta representa una sesión de mail básica con el servidor de correo. Todas las operaciones requeieren conectarse a un servidor de correo, por eso debemos crear la conexión. Obtenermos la sesión con los métodos getDefaultInstance() o getInstance() de la clase Session. El método getDefaultInstance() obtiene un ejemplar se sesión compartido. En comparación, el método getInstance() obtiene un nuevo ejemplar de sesión cada vez que se le llama. Cuando se obtiene una sesión, debemos pasar un ejemplar de Properties, que contiene las selecciones apropiadas como el servidor de correo a utilizar para la conexión. En el siguiente ejemplo, la propiedad mail.smtp.host es pasada a Session.getDefaultInstance. La propiedad contiene el nombre del servidor de correo para la conexión (smtp.example.com):

    Properties props = new Properties();
    props.put("mail.smtp.host", 
      "smtp.example.com");
    Session session = 
      Session.getDefaultInstance(props, null);

Después de tener la Session, podemos crear un Message desde una Session. Como su nombre implica, la clase Message representa el mensaje de e-mail. Es abstracta, por eso debemos crear un ejemplar de una subclase. Esta subclase es MimeMessage:

    MimeMessage message = new MimeMessage(session);

Luego, necesitamos rellenar el contenido del mensaje. Cada una de las partes del mensaje tiene un método diferente para seleccionar su contenido:

  • setFrom() se usa para la dirección From
  • addRecipient() se usa para seleccionar las direcciones To, CC, y BCC.
  • setSubject() se usa para seleccionar el Subject
  • setText() se usa para seleccionar el contenido.

Para seleccionar los campos From o To del mensaje, necesitamos una Address, que creamos con la clase InternetAddress. Creamos una Address desde un string, y le pasamos la Address al método apropiado. Podemos proporcionar sólo la dirección e-mail o una dirección y un nombre, por ejemplo:

    Address address = new InternetAddress(
      "[email protected]");
    Address address = new InternetAddress(
      "[email protected]", "Just Me");

Para seleccionar el campo From, simplemente pasamos la Address a setFrom(), es decir, setFrom(address).

El método addRecipient() implica un poco más de trabajo que usar setFrom(). Con addRecipient(), necesitamos especificar si la dirección es para un campo To, CC, o BCC. Esto se hace con la ayuda de la clase RecipientType:

Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC

Esto significa que si queremos seleccionar el campo To, proprocionamos Message.RecipientType.TO y la Address al método addRecipient():

    message.addRecipient(Message.RecipientType.TO, 
      address);

Para un mensaje básico, esto es todo lo que necesitamos hacer. Los métodos setSubject() y setText() sólo aceptan el texto contenido en las partes respectivas del mensaje.

Después de que el mensaje está configurado, lo enviamos con la clase Transport:

    Transport.send(message);

Esta usa el servidor SMTP especifiado en las propiedades de la sesión.

Aquí tenemos un programa completo que usa el API JavaMail para enviar un mensaje. Para ejecutar el programa, ejecutamos el comando:

    JDCSend SMTP_host from_address to_address

Reemplazamos SMTP_host con el nombre de un host SMTP, from_address con una dirección para el campo From del mensaje y to_address con una dirección para el campo To del mensaje:

import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;

public class JDCSend {
  public static void main (String args[]) 
    throws Exception {
    String smtpHost = args[0];
    String from = args[1];
    String to = args[2];

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

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

    // 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, JDC");
    message.setText("Welcome to the JDC");

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

Si queremos incluir un attachment con nuestro mensaje, necesitamos construir las partes, bastante literalmente, porque el nombre del interface aplicable es Part. El contenido de nuestro Message constará de varias partes dentro de un objeto Multipart. La Part uno del mensaje es un BodyPart que contiene el contenido del mensaje. La Part dos del mensaje es un BodyPart que contiene el attachment. El propio attachment se especifica como una DataSource. Realmente no tenemos que leer el attachement.

Empezamos de la misma forma que lo hicimos para un mensaje sin attachment. Creamos el mensaje desde la sesión e inicializamos las cabeceras:

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

Sin embargo aquí necesitamos crear el objeto Multipart:

    Multipart multipart = new MimeMultipart();

Para la parte uno, creamos un BodyPart y seleccionamos el texto para ser un mensaje. Luego añadimos el BodyPart al Multipart recién creado.

    BodyPart messageBodyPart = new MimeBodyPart();
    messageBodyPart.setText("Here's the file");
    multipart.addBodyPart(messageBodyPart);

Para la parte dos, necesitamos crear otro BodyPart, pero esta vez, necesitamos crear un DataSource para el fichero:

    messageBodyPart = new MimeBodyPart();
    DataSource source = new FileDataSource(filename);

Usamos un objeto DataHandler para adjuntar los datos al mensaje. Simplemente creamos un DataHandler para la fuente y lo adjuntamos al mensaje:

    messageBodyPart.setDataHandler(
      new DataHandler(source));

Debemos recordar seleccionar el nombre de fichero para el attachment. Esto permite al receptor conocer el nombre (y el tipo) del fichero recibido.

    messageBodyPart.setFileName(filename);

Adjuntamos la parte dos de la misma forma que la parte uno:

    multipart.addBodyPart(messageBodyPart);

Y como paso final, antes de enviarlo, adjutamos el Multipart al Message:

    message.setContent(multipart);

Ahora podemos enviar el mensaje. Aquí tenemos un programa completo. Debemos asegurarnos de pasar los argumentos apropiados de la líena de comandos al programa, de esta forma:

    JDCAttach SMTP_host from_address to_address attach_file

Reemplazamos SMTP_host con el nombre de un host SMTP, from_address con una dirección para el campo From del mensaje, to_address con una dirección para el campo To del mensaje y attach_file con el nombre del fichero a adjuntar:

import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class JDCAttach {
  public static void main (String args[]) 
      throws Exception {
    String smtpHost = args[0];
    String from = args[1];
    String to = args[2];
    String filename = args[3];

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

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

    // 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, JDC Attachment");

    // Create the multi-part
    Multipart multipart = new MimeMultipart();

    // Create part one
    BodyPart messageBodyPart = new MimeBodyPart();

    // Fill the message
    messageBodyPart.setText("Here's the file");

    // Add the first part
    multipart.addBodyPart(messageBodyPart);

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

    // Add the second part
    multipart.addBodyPart(messageBodyPart);

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

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

Para más información sobre el API JavaMail, puedes ver "Fundamentals of the JavaMail API". Para una demostración del API JavaMail en una aplicación de uso real, puedes visitar "The Java Technologies Behind ICEMail: An Open-Source Project"

Copyright y notas de la traducción

Nota respecto a la traducción

El original en inglés de la presente edición de los JDC Tech Tips fue escrita por Glen McCluskey, la traducción no oficial fue hecha por Juan A. Palos (Ozito), cualquier sugerencia o corrección hágala al correo [email protected] , sugerencia respecto a la edición original a mailto:[email protected]

Nota (Respecto a la edición via email)

Sun respeta su tiempo y su privacidad. La lista de correo de la Conexión del desarrollador Java se usa sólo para propósitos internos de Sun Microsystems(tm). Usted ha recibido este email porque se ha suscrito a la lista. Para desuscribirse vaya a la página de suscripciones, desmarque casilla apropiada y haga clic en el botón Update.

Suscripciones

Para suscribirse a la lista de correo de noticias de la JDC vaya a la página de suscripciones, elija los boletines a los que quiera suscribirse, y haga clic en Update.

Realimentación

¿Comentarios?, envie su sugerencias a los Consejos Técnicos de la JDC a mailto:[email protected]

Archivos

Usted encontrará las ediciones de los Consejos Técnicos de la JDC (en su original en inglés) en http://java.sun.com/jdc/TechTips/index.html

Copyright

Copyright 2001 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA.

Este documento esta protegido por las leyes de autor. Para mayor información vea http://java.sun.com/jdc/copyright.html

Enlaces a sitios fuera de Sun

Los Consejos Técnicos de la JDC pueden dar enlaces a otros sitios y recursos. Ya que Sun no tiene control sobre esos sitios o recursos usted reconoce y acepta que Sun no es responsable por la disponibilidad de tales sitios o recursos, y no se responsabiliza por cualquier contenido, anuncios , productos u otros materiales disponibles en tales sitios o recursos. Sun no será responsable, directa o indirectamente, por cualquier daño o pérdida causada o supuestamente causada por o en relación con el uso de o seguridad sobre cualquier tal contenido, bienes o servicios disponibles en o através de cualquier sitio o recurso.

El original en Ingles de esta edición de los Consejos técnicos fue escrita por Glen McCluskey.

JDC Tech Tips October 23, 2001

Sun, Sun Microsystems, Java y Java Developer Connection (JDC) son marcas registradas de Sun Microsystems Incs. en los Estados Unidos y cualquier otro país.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP