Return-Path: Received: from pacific-carrier-annex.mit.edu by po10.mit.edu (8.9.2/4.7) id MAA06051; Tue, 23 Oct 2001 12:54:07 -0400 (EDT) Received: from hermes.java.sun.com (hermes.java.sun.com [64.124.140.163]) by pacific-carrier-annex.mit.edu (8.9.2/8.9.2) with SMTP id MAA09910 for ; Tue, 23 Oct 2001 12:54:06 -0400 (EDT) Message-Id: <200110231654.MAA09910@pacific-carrier-annex.mit.edu> Date: Tue, 23 Oct 2001 09:54:06 PDT From: "JDC Tech Tips" To: alexp@mit.edu Subject: JDC Tech Tips October 23, 2001 Precedence: junk Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable X-Mailer: Beyond Email 2.2 J D C T E C H T I P S TIPS, TECHNIQUES, AND SAMPLE CODE WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips,=20 October 23, 2001. This issue covers: * Sorting Lists=20 * Sending Mail With the JavaMail(tm) API These tips were developed using Java(tm) 2 SDK, Standard Edition,=20 v 1.3. =20 This issue of the JDC Tech Tips is written by John Zukowski,=20 president of JZ Ventures, Inc. (http://www.jzventures.com). You can view this issue of the Tech Tips on the Web at http://java.sun.com/jdc/JDCTechTips/2001/tt1023.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -=20 SORTING LISTS The List implementations found in the Java Collections Framework are Vector, ArrayList, and LinkedList. These collections offer=20 indexed access to groups of objects. They provide support for=20 adding and removing elements. However they offer no built-in=20 support for sorting elements.=20 To sort List elements you can use the sort() method in the=20 java.util.Collections class. You can either pass the method=20 a List object, or you can pass it a List and a Comparator. If the=20 elements in the List are all the same class type and that class=20 implements the Comparable interface, you can simply call=20 Collections.sort(). If, however, the class doesn't implement=20 Comparator, you can pass the sort() method a Comparator to do the=20 sort. You might also want to pass the sort() method a Comparator if you don't want to use the default sort order for the elements=20 of the class. If the elements in the List are not all the same=20 class type, you're out of luck as far as sorting goes -- unless you write a special cross-class Comparator. What about sorting order? If the elements are String objects,=20 the default sort order is based on the character codes, basically=20 the ASCII/Unicode value for each character. If you strictly work=20 with English, the default sort order is usually sufficient=20 because it will sort A-Z first, followed by the lowercase letters=20 a-z. However, if you work with non-English words, or you just=20 want a different sorting order, that's where the second variety=20 of Collections.sort() comes in handy. For instance, say you want=20 to sort the elements of the List in the reverse natural order of=20 the strings. To do this, you can get a reverse Comparator from=20 the Collections class with reverseOrder(). Then, you pass the=20 reverse Comparator to the sort() method. In other words, you do=20 the following: List list =3D ...; Comparator comp =3D Collections.reverseOrder(); Collections.sort(list, comp); =20 If the list contained the terms Man, man, Woman, and woman, the=20 sorted list would be Man, Woman, man, woman. Nothing too=20 complicated here. An important thing to remember is=20 Collections.sort() does an in-place sort. If you need to retain=20 the original order, make a copy of the collection first, then=20 sort it, like this: List list =3D ...; List copyOfList =3D new ArrayList(list); Collections.sort(copyOfList); =20 Here, the sorted list is Man, Woman, man, woman, but the original=20 list (Man, man, Woman, and woman) is retained. =20 So far the sorting has been case-sensitive. How would you do a case-insensitive sort? One way to do it is to implement a =20 Comparator like this: public static class CaseInsensitiveComparator=20 implements Comparator { public int compare(Object element1,=20 Object element2) { String lower1 =3D element1.toString().toLowerCase(); String lower2 =3D element2.toString().toLowerCase(); return lower1.compareTo(lower2); } } There is a slight problem with this approach. The sort()=20 algorithm provides stable sorting, keeping equal elements in the=20 original order. This means that a list that contains the two elements "woman" and "Woman" would sort differently based upon=20 which of the two elements appear first in the list. What about language differences? The java.text package provides=20 Collator and CollationKey classes for doing language-sensitive=20 sorting. Here's an example: public static class CollatorComparator=20 implements Comparator { Collator collator =3D Collator.getInstance(); public int compare(Object element1,=20 Object element2) { CollationKey key1 =3D collator.getCollationKey( element1.toString()); CollationKey key2 =3D collator.getCollationKey( element2.toString()); return key1.compareTo(key2); } } Instead of sorting on the actual string, you sort on a collation=20 key. Not only does this provide consistent case-insensitive=20 sorting, but it also works across languages. In other words, if=20 you sort a combination of Spanish and non-Spanish words, the word=20 ma=F1ana (tomorrow) would come before mantra. If you don't use a=20 Collator, ma=F1ana would come after mantra. Here's a program that does various types of sorting (default,=20 case-insensitive, and language-sensitive) on a List: import java.awt.BorderLayout; import java.awt.Container; import java.io.*; import java.text.*; import java.util.*; import javax.swing.*; public class SortIt { =20 public static class CollatorComparator=20 implements Comparator { Collator collator =3D Collator.getInstance(); public int compare(Object element1,=20 Object element2) { CollationKey key1 =3D collator.getCollationKey( element1.toString()); CollationKey key2 =3D collator.getCollationKey( element2.toString()); return key1.compareTo(key2); } } public static class CaseInsensitiveComparator=20 implements Comparator { public int compare(Object element1,=20 Object element2) { String lower1 =3D element1.toString(). toLowerCase(); String lower2 =3D element2.toString(). toLowerCase(); return lower1.compareTo(lower2); } } public static void main(String args[]) { String words[] =3D=20 {"man", "Man", "Woman", "woman",=20 "Manana", "manana", "ma=F1ana", "Ma=F1ana", "Mantra", "mantra", "mantel", "Mantel" }; // Create frame to display sortings JFrame frame =3D new JFrame("Sorting"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container contentPane =3D frame.getContentPane(); JTextArea textArea =3D new JTextArea(); JScrollPane pane =3D new JScrollPane(textArea); contentPane.add(pane, BorderLayout.CENTER); // Create buffer for output StringWriter buffer =3D new StringWriter(); PrintWriter out =3D new PrintWriter(buffer); // Create initial list to sort List list =3D 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=20 list =3D new ArrayList(Arrays.asList(words)); // Perform case insensitive sort Comparator comp =3D new CaseInsensitiveComparator(); Collections.sort(list, comp); out.println("Case insensitive sorting:"); out.println(list); out.println(); // Reset list list =3D new ArrayList(Arrays.asList(words)); // Perform collation sort comp =3D 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(); } } If your primary concern is ordered access, perhaps a List isn't=20 the best data structure for you. As long as your collection=20 doesn't have duplicates, you can keep the elements in a TreeSet=20 (with or without providing a Comparator). Then, the elements will=20 always be in sorted order. For more information about sorting Lists, see "Introduction to=20 the Collections Framework"=20 (http://java.sun.com/jdc/onlineTraining/collections/index.html). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SENDING MAIL WITH THE JAVAMAIL API The JavaMail API is a standard part of the Java(tm) 2 Platform, Enterprise Edition (J2EE(tm)). The API is downloadable from http://java.sun.com/products/javamail/. As its name suggests, the JavaMail API provides support for sending and receiving mail=20 messages. You still must connect to your mail server for sending=20 and receiving the messages because the JavaMail API is not a mail=20 server, but the API does provide the framework for making that=20 connection. Sending mail involves understanding at least four classes: Session Message Address Transport And, if you are interested in including an attachment with your=20 message, you must know a few more: Multipart=20 BodyPart DataSource DataHandler The DataSource class and its implementations FileDataSource and=20 URLDataSource are part of the JavaBeans Activation Framework.=20 The DataHandler class is a part of that framework too. The=20 JavaBeans Activation Framework is included with J2EE. The=20 Framework is downloadable from http://java.sun.com/products/javabeans/glasgow/jaf.html. The JavaMail API requires the JavaBeans Activation Framework=20 library. Looking at the specific classes in the JavaMail API, first there=20 is the Session class. This represents the basic mail session=20 with the mail server. All operations require connecting to a=20 mail server, so you must create the connection. You get the=20 session with either getDefaultInstance() or getInstance()=20 methods of the Session class. The getDefaultInstance() method=20 gets a shared session instance. By comparison, the getInstance()=20 method gets a new session instance each time it's called. When=20 getting the session, you must pass a Properties instance, this=20 contains property settings such as the mail server to be used for the connection. In the following example, the mail.smtp.host=20 property is passed to Session.getDefaultInstance. The property=20 contains the name of the mail server for the connection=20 (smtp.mail.yahoo.com): Properties props =3D new Properties(); props.put("mail.smtp.host", "smtp.mail.yahoo.com"); Session session =3D=20 Session.getDefaultInstance(props, null); After you have the Session, you can create a Message from the=20 Session. As its name implies, the Message class represents the=20 email message. It is abstract, so you must create an instance of=20 a subclass. That subclass is MimeMessage: MimeMessage message =3D new MimeMessage(session); Next, you need to fill the message's content. Each of the pieces=20 of the message has a different method to set the content: setFrom() is used to set the From address addRecipient() is used to set the To, CC, and BCC addresses setSubject() is used to set the Subject setText() is used to set the content In order to set the From or To fields of the message, you need an Address, which you create with the InternetAddress class.=20 You create an Address from a string, and pass the Address to the=20 appropriate method. You can provide just the email address or an=20 address and a name string. For example: Address address =3D new InternetAddress( "me@sun.com"); Address address =3D new InternetAddress( "me@sun.com", "Just Me"); To set the From field, simply pass the Address to setFrom(), that=20 is, setFrom(address).=20 The addRecipient() method involves a little more work than using setFrom(). With addRecipient(), you need to specify whether the=20 Address is for the To field, the CC field, or the BCC (Blind=20 carbon copy) field. This is done with the help of the=20 RecipientType class: Message.RecipientType.TO=20 Message.RecipientType.CC=20 Message.RecipientType.BCC=20 This means that if you want to set the To field, you provide=20 Message.RecipientType.TO and the Address to the addRecipient()=20 method: message.addRecipient(Message.RecipientType.TO,=20 address); For a basic message, that's all you need to do. The setSubject()=20 and setText() methods just accept the text content of their=20 respective message pieces. After your message is set, send it with the Transport class: Transport.send(message); This uses the SMTP server specified in the properties for the=20 session.=20 Here's a complete program that uses the JavaMail API to send=20 a message. To run the program, issue the command: JDCSend SMTP_host from_address to_address =20 Replace SMTP_host with the name of an SMTP host, from_address with an address for the From field of the message, and to_address=20 with an address for the To field of the message. import java.util.Properties; import javax.mail.*; import javax.mail.internet.*; public class JDCSend { public static void main (String args[]) throws Exception { String smtpHost =3D args[0]; String from =3D args[1]; String to =3D args[2]; // Get system properties Properties props =3D System.getProperties(); // Setup mail server props.put("mail.smtp.host", smtpHost); // Get session Session session =3D Session.getDefaultInstance(props, null); // Define message MimeMessage message =3D new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO,=20 new InternetAddress(to)); message.setSubject("Hello, JDC"); message.setText("Welcome to the JDC"); // Send message Transport.send(message); } } If you want to include an attachment with your message, you need=20 to build up the parts, quite literally, because the name of the applicable interface is Part. The content of your Message will=20 consist of multiple parts within a Multipart object. Part one of=20 the message is a BodyPart that contains the message content. Part=20 two of the message is a BodyPart that contains the attachment.=20 The attachment itself is specified as a DataSource. You don't=20 have to actually read the attachment.=20 You start in the same way as you do for a message without an attachment. Create the message from the session and initialize=20 the headers: Message message =3D new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO,=20 new InternetAddress(to)); message.setSubject("JDC Attachment"); However here you need to create the Multipart object: Multipart multipart =3D new MimeMultipart(); For part one, create a BodyPart and set the text to be a message.=20 Then, add the BodyPart to the Multipart you just created. BodyPart messageBodyPart =3D new MimeBodyPart(); messageBodyPart.setText("Here's the file"); multipart.addBodyPart(messageBodyPart); For part two, you need to create a BodyPart again, but this time=20 you need to create a DataSource for the file. messageBodyPart =3D new MimeBodyPart(); DataSource source =3D new FileDataSource(filename); Use a DataHandler object to attach the data source to the=20 message. Simply create a DataHandler for the source and attach it=20 to the message: messageBodyPart.setDataHandler( new DataHandler(source)); Remember to set the filename of the attachment. This permits the=20 recipient to know the name (and type) of the received file. messageBodyPart.setFileName(filename); Attach part two in the same way as part one: multipart.addBodyPart(messageBodyPart); And as a final step before sending, attach the Multipart to the=20 Message: message.setContent(multipart); You can now send the message. Here's a complete program.=20 Make sure to pass the proper command-line arguments to the=20 program, as follows: JDCAttach SMTP_host from_address to_address attach_file =20 Replace SMTP_host with the name of an SMTP host, from_address=20 with an address for the From field of the message, to_address=20 with an used for the To field of the message, and attach_file with the filename of the attachment. import java.util.Properties; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class JDCAttach { public static void main (String args[])=20 throws Exception { String smtpHost =3D args[0]; String from =3D args[1]; String to =3D args[2]; String filename =3D args[3]; // Get system properties Properties props =3D System.getProperties(); // Setup mail server props.put("mail.smtp.host", smtpHost); // Get session Session session =3D Session.getDefaultInstance( props, null); // Define message MimeMessage message =3D new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO,=20 new InternetAddress(to)); message.setSubject("Hello, JDC Attachment"); // Create the multi-part Multipart multipart =3D new MimeMultipart(); // Create part one BodyPart messageBodyPart =3D new MimeBodyPart(); // Fill the message messageBodyPart.setText("Here's the file"); // Add the first part multipart.addBodyPart(messageBodyPart); // Part two is attachment messageBodyPart =3D new MimeBodyPart(); DataSource source =3D 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); } } For more information about the JavaMail API, see "Fundamentals of=20 the JavaMail API"=20 (http://java.sun.com/jdc/onlineTraining/JavaMail/). For a demonstration of the JavaMail API in use in a real application, see "The Java Technologies Behind ICEMail: An Open-Source Project" (http://java.sun.com/jdc/technicalArticles/javaopensource/icemail/) . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Terms of Use and Privacy policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/=20 * FEEDBACK Comments? Send your feedback on the JDC Tech Tips to:=20 jdc-webmaster@sun.com * SUBSCRIBE/UNSUBSCRIBE - To subscribe, go to the subscriptions page, (http://developer.java.sun.com/subscription/), choose the newsletters you want to subscribe to and click "Update". - To unsubscribe, go to the subscriptions page, (http://developer.java.sun.com/subscription/), uncheck the appropriate checkbox, and click "Update". - To use our one-click unsubscribe facility, see the link at=20 the end of this email: =20 - ARCHIVES You'll find the JDC Tech Tips archives at: 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. This document is protected by copyright. For more information, see: http://java.sun.com/jdc/copyright.html JDC Tech Tips=20 October 23, 2001 Sun, Sun Microsystems, Java, Java Developer Connection, J2EE, and=20 JavaMail are trademarks or registered trademarks of Sun=20 Microsystems, Inc. in the United States and other countries. To use our one-click unsubscribe facility, select the following URL: http://hermes.java.sun.com/unsubscribe?3303729-270426076