19.7 The Message Class

     

The javax.mail.Message class is the abstract superclass for all individual emails, news postings, and similar messages:

 public abstract class Message extends Object implements Part 

There's one concrete Message subclass in the standard JavaMail API, javax.mail.internet.MimeMessage . This is used for both email and Usenet news messages. Service providers are free to add classes for their own message formats. For instance, IBM might provide a NotesMessage class for Lotus Notes.

The Message class mainly declares abstract getter and setter methods that define the common properties of most messages. These properties include the addressees of the message, the recipients of the message, the subject and content of the message, and various other attributes. You can think of these as properties of the envelope that contains the message.

Furthermore, the Message class implements the Part interface. The Part interface mostly handles the body of an email message. It declares methods for getting and setting the content type of the message body, getting and setting the actual message body content, getting and setting arbitrary headers from the message, and getting input streams that are fed by the message body. The main body part of a message can contain other parts . This is used to handle attachments, message bodies that are available in multiple formats, and other multipart emails. Since the Message class is abstract and needs to be subclassed by concrete classes such as MimeMessage , most of these methods are not actually redeclared in Message but can be invoked by any actual instance of Message . We'll begin by discussing the methods actually declared in Message , then move on to those declared in Part .

19.7.1 Creating Messages

The Message class has three constructors:

 protected Message( ) protected Message(Folder folder, int messageNumber) protected Message(Session session) 

Since all the constructors are protected, they are primarily for the use of subclasses such as MimeMessage . If you're sending a message, you'll use one of the constructors in the subclass instead. If you're reading messages, the Folder or Session you're reading from will create the Message objects and pass them to you.

19.7.1.1 Replying to messages

If you already have a Message object, one way to create a new Message object is to reply to the existing one using the reply( ) method:

 public abstract Message reply(boolean replyToAll)   throws MessagingException 

This method creates a new Message object with the same subject prefixed with "Re:", and addressed to the sender of the original message. If replyToAll is true , the message is addressed to all known recipients of the original message. The content of the message is empty. If you want to quote the original message, you'll have to do that yourself.

19.7.1.2 Getting messages from folders

You've already seen that when you're reading email, the JavaMail API creates Message objects to represent the messages it finds on the server. The primary means of doing this are the getMessage() and getMessages() methods in the Folder class:

 public abstract Message getMessage(int messageNumber)   throws MessagingException public Message[] getMessages(int start, int end)   throws MessagingException public Message[] getMessages(int[] messageNumbers)   throws MessagingException public Message[] getMessages( ) throws MessagingException 

The first three methods require the caller to specify which messages it wants. The last simply returns all messages in the folder. What's actually returned are stubs holding the places of the actual messages. The text and headers of the message won't necessarily be retrieved until some method of the Message class is invoked that requires this information.

19.7.2 Basic Header Info

A typical RFC 822 message contains a header that looks something like this:

 From levi@blazing.sunspot.noao.edu Fri Aug  5 10:57:08 1994 Date: Fri, 27 Aug 2004 10:57:04 +0700 From: levi@blazing.sunspot.noao.edu (Denise Levi) To: volleyball@sunspot.noao.edu Subject: Apologies Content-Length: 517 Status: RO X-Lines: 13 

The exact fields in the header can vary, but most messages contain at least a From: field, a To: field, a Date: field, and a Subject: field. Other common fields include Cc: (carbon copies) and Bcc: (blind carbon copies). In general, these will be accessible through getter and setter methods.

19.7.2.1 The From address

These four methods get and set the From: field of a message:

 public abstract Address[] getFrom( ) throws MessagingException public abstract void setFrom( ) throws MessagingException,   IllegalWriteException, IllegalStateException  public abstract void setFrom(Address address)   throws MessagingException, IllegalWriteException, IllegalStateException public abstract void addFrom(Address[] addresses)   throws MessagingException, IllegalWriteException, IllegalStateException 

The getFrom( ) method returns an array of Address objects, one for each address listed in the From: header. (In practice, it's rare for a message to be from more than one address. It's quite common for a message to be addressed to more than one address.) It returns null if the From: header isn't present in the message. It throws a MessagingException if the From: header is malformed in some way.

The noargs setFrom( ) and addFrom( ) methods set and modify the From: headers of outgoing email messages. The noargs setFrom( ) method sets the header to the current value of the mail. user property or, as a fallback, the user. name property. The setFrom() method with arguments sets the value of the From: header to the listed addresses. The addFrom( ) method adds the listed addresses to any addresses that already exist in the header. All three of these methods can throw a MessagingException if one of the addresses they use isn't in the right format. They can also throw an IllegalWriteException if the From: field of the given Message object cannot be changed or an IllegalStateException if the entire Message object is read-only.

19.7.2.2 The Reply-to address

Some messages contain a Reply-to: header indicating that any replies should be sent to a different address than the one that sent the message. There are two methods to set and get these addresses:

 public Address[] getReplyTo( ) throws MessagingException public void setReplyTo(Address[] addresses) throws MessagingException,   MethodNotSupportedException, IllegalWriteException,   IllegalStateException 

The semantics of these methods are the same as for the equivalent getFrom( ) and setFrom( ) methodsin fact, the default implementation of getReplyTo( ) simply returns getFrom( ) with the single caveat that an implementation that doesn't support separate Reply-to: addresses may throw a MethodNotSupportedException when setReplyTo( ) is invoked.

19.7.2.3 The recipient addresses

Whereas the sender of the message is generally found only in the From: header, the recipients of the message are often split across the To:, Cc:, and Bcc: fields. Rather than providing separate methods for each of these fields, the various getRecipients( ) and setRecipients( ) methods rely on a Message.RecipientType argument to determine which field's value is desired. RecipientType is a public inner class in javax.mail.Message whose private constructor limits it to exactly these three static objects:

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

There are two methods to find the addressees of the Message :

 public abstract Address[] getRecipients(Message.RecipientType type)   throws MessagingException public Address[] getAllRecipients( ) throws MessagingException 

The getRecipients( ) method returns an array of Address objects, one for each address listed in the specified header. It returns null if the specified header isn't present in the message. It throws a MessagingException if the specified header is malformed in some way. The getAllRecipients( ) method does the same thing, except that it combines the contents of the To:, Cc:, and Bcc: headers.

There are two methods to set the recipients of the message while replacing any previous recipients and two methods to add recipients to the message:

 public abstract void setRecipients(Message.RecipientType type,   Address[] addresses) throws MessagingException, IllegalWriteException,   IllegalStateException public void setRecipient(Message.RecipientType type, Address address)   throws MessagingException, IllegalWriteException public abstract void addRecipients(Message.RecipientType type,   Address[] addresses) throws MessagingException,    IllegalWriteException, IllegalStateException public void addRecipient(Message.RecipientType type, Address address)   throws MessagingException, IllegalWriteException 

All four of these methods can throw a MessagingException , typically because one of the addresses isn't in the right format. They can also throw an IllegalWriteException if the specified field of the given Message object cannot be changed or an IllegalStateException if the entire Message object is read-only.

19.7.2.4 The subject of the message

Since the subject is simply a single string of text, it's easy to set and get with these two methods:

 public abstract String getSubject( ) throws MessagingException public abstract void   setSubject(String subject) throws   MessagingException, IllegalWriteException, IllegalStateException 

As with earlier setter methods, null is returned if the subject field isn't present in the message. An IllegalWriteException is thrown if the program isn't allowed to set the value of the Subject: field and an IllegalStateException is thrown if the program isn't allowed to change the message at all.

19.7.2.5 The date of the message

Messages also have sent and received dates. Three methods allow programs to access these fields:

 public abstract Date getSentDate( ) throws MessagingException public abstract void setSentDate(Date date) throws MessagingException,   IllegalWriteException, IllegalStateException public abstract Date getReceivedDate( ) throws MessagingException 

The underlying implementation is responsible for converting the textual date format found in a message header like "Fri, 20 Aug 2004 10:57:04 +0700" to a java.util.Date object. As usual, a MessagingException indicates some problem with the format of the underlying message, an IllegalWriteException indicates that the field cannot be changed, and an IllegalStateException indicates that the entire message cannot be changed.

Example 19-8 is a simple example program that follows the basic pattern of the last several mail-reading programs. However, this one no longer uses writeTo( ) . Instead, it uses the methods in this section to print just the headers. Furthermore, it prints them in a particular order regardless of their order in the actual message on the server. Finally, it ignores the less important headers such as X-UIDL: and Status:. The static InternetAddress.toString( ) method converts the arrays that most of these methods return into simple, comma-separated strings.

Example 19-8. A program to read mail headers
 import javax.mail.*; import javax.mail.internet.*; import java.util.*; public class HeaderClient {   public static void main(String[] args) {     if (args.length == 0) {       System.err.println(        "Usage: java HeaderClient protocol://username@host/foldername");       return;      }          URLName server = new URLName(args[0]);     try {       Session session = Session.getDefaultInstance(new Properties( ),         new MailAuthenticator(server.getUsername( )));       // Connect to the server and open the folder       Folder folder = session.getFolder(server);       if (folder == null) {         System.out.println("Folder " + server.getFile( ) + " not found.");         System.exit(1);       }         folder.open(Folder.READ_ONLY);              // Get the messages from the server       Message[] messages = folder.getMessages( );       for (int i = 0; i < messages.length; i++) {         System.out.println("------------ Message " + (i+1)           + " ------------");         // Here's the big change...         String from = InternetAddress.toString(messages[i].getFrom( ));         if (from != null) System.out.println("From: " + from);         String replyTo = InternetAddress.toString(          messages[i].getReplyTo( ));         if (replyTo != null) System.out.println("Reply-to: "           + replyTo);         String to = InternetAddress.toString(          messages[i].getRecipients(Message.RecipientType.TO));         if (to != null) System.out.println("To: " + to);         String cc = InternetAddress.toString(         messages[i].getRecipients(Message.RecipientType.CC));         if (cc != null) System.out.println("Cc: " + cc);         String bcc = InternetAddress.toString(          messages[i].getRecipients(Message.RecipientType.BCC));         if (bcc != null) System.out.println("Bcc: " + to);         String subject = messages[i].getSubject( );         if (subject != null) System.out.println("Subject: " + subject);         Date sent = messages[i].getSentDate( );         if (sent != null) System.out.println("Sent: " + sent);         Date received = messages[i].getReceivedDate( );         if (received != null) System.out.println("Received: " + received);                  System.out.println( );       }        // Close the connection        // but don't remove the messages from the server       folder.close(false);            }      catch (Exception ex) {       ex.printStackTrace( );     }                  // Since we may have brought up a GUI to authenticate,     // we can't rely on returning from main( ) to exit     System.exit(0);             } } 

Here's some typical output. Several of the requested strings were null because the fields simply weren't present in the messages in the INBOX; for instance, Cc: and Bcc:. HeaderClient checks for the fields and simply omits them if they're not present.

 %  java HeaderClient pop3://eharold@utopia.poly.edu/INBOX  ------------ Message 1 ------------ From: Elliotte Harold <eharold@utopia.poly.edu> Reply-to: Elliotte Harold <eharold@utopia.poly.edu> To: eharold@utopia.poly.edu Subject: test Sent: Tue Nov 30 13:14:29 PST 1999 ------------ Message 2 ------------ From: Elliotte Rusty Harold <elharo@macfaq.com> Reply-to: Elliotte Rusty Harold <elharo@macfaq.com> To: eharold@utopia.poly.edu Subject: New system Sent: Wed Dec 01 10:55:40 PST 1999 ------------ Message 3 ------------ From: Dr. Mickel <Greatsmiles@mail.com> Reply-to: Dr. Mickel <Greatsmiles@mail.com> To: eharold@utopia.poly.edu Subject: Breath RX Products now available Online! Sent: Thu Dec 02 03:45:52 PST 1999 

Notice that none of these messages have received dates. That's because the receive time is not part of the message envelope itself. It has to be provided by the server, and POP servers don't provide it. An IMAP server would be much more likely to include a received date, as will be shown in Example 19-9.

19.7.2.6 Saving changes

When you invoke one of the previous set or add methods, some implementations store the changes immediately. Others, however, may not. The saveChanges() method commits the changes made to a Message object:

 public abstract void saveChanges( ) throws MessagingException,  IllegalWriteException, IllegalStateException 

This is not quite a flush. The actual changes may not be committed to disk until the folder containing the message is closed. However, this method does ensure that the changes are stored in the folder and will be saved when the folder is saved.

19.7.3 Flags

Mail programs can save extra information about the messages that are not part of the messages themselves . For instance, Pine lets me know whether I've replied to or read a message, and so on. As Figure 19-5 shows, this information is indicated by symbols and letters in the lefthand column. D means a message has been deleted; A means it's been answered ; N is a new message that hasn't been read yet; and so forth. In the JavaMail API, these are all represented as flags . A flag is an instance of the javax.mail.Flags class:

 public class Flags extends Object implements Cloneable 

Seven flags are predefined as instances of the public static inner class Flags.Flag . These are:

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

In addition, some implementations may allow arbitrary user-defined flags. If so, the USER flag is set.

Figure 19-5. Pine shows flags as letters in the lefthand column
figs/jnp3_1905.gif

The getFlags( ) method returns the flags of a particular message:

 public abstract Flags getFlags( ) throws MessagingException 

The isSet( ) method tests whether a specified flag is set for the given message:

 public boolean isSet(Flags.Flag flag) throws MessagingException 

Finally, the setFlags() and setFlag( ) methods set or unset (depending on the second argument) the flag indicated by the first argument:

 public abstract void setFlags(Flags flag, boolean set)   throws MessagingException, IllegalWriteException,   IllegalStateException  public void setFlag(Flags.Flag flag, boolean set) throws   MessagingException, IllegalWriteException, IllegalStateException 

You delete messages by setting their Flags.Flag.DELETED flag to true . For example, to delete message :

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

This only marks the message as deleted. It does not actually expunge it from the file on the server. Until the message is expunged, it can still be undeleted by setting Flags.Flag.DELETED back to false .

Example 19-9 is a slight modification of Example 19-8, HeaderClient , which prints the flags as well. As a general rule, POP servers won't report flags. Only a protocol that stores messages and forwards them, such as IMAP or mbox, will report flags.

Example 19-9. A program to read mailbox flags
 import javax.mail.*; import javax.mail.internet.*; import java.util.*; public class FlagsClient {   public static void main(String[] args) {          if (args.length == 0) {       System.err.println(        "Usage: java FlagsClient protocol://username@host/foldername");       return;      }          URLName server = new URLName(args[0]);     try {       Session session = Session.getDefaultInstance(new Properties( ),         new MailAuthenticator(server.getUsername( )));       // Connect to the server and open the folder       Folder folder = session.getFolder(server);       if (folder == null) {         System.out.println("Folder " + server.getFile( ) + " not found.");         System.exit(1);       }         folder.open(Folder.READ_ONLY);              // Get the messages from the server       Message[] messages = folder.getMessages( );       for (int i = 0; i < messages.length; i++) {         System.out.println("------------ Message " + (i+1)           + " ------------");         // Get the headers         String from = InternetAddress.toString(messages[i].getFrom( ));         if (from != null) System.out.println("From: " + from);         String replyTo = InternetAddress.toString(          messages[i].getReplyTo( ));         if (replyTo != null) System.out.println("Reply-to: "           + replyTo);         String to = InternetAddress.toString(          messages[i].getRecipients(Message.RecipientType.TO));         if (to != null) System.out.println("To: " + to);         String cc = InternetAddress.toString(         messages[i].getRecipients(Message.RecipientType.CC));         if (cc != null) System.out.println("Cc: " + cc);         String bcc = InternetAddress.toString(          messages[i].getRecipients(Message.RecipientType.BCC));         if (bcc != null) System.out.println("Bcc: " + to);         String subject = messages[i].getSubject( );         if (subject != null) System.out.println("Subject: " + subject);         Date sent = messages[i].getSentDate( );         if (sent != null) System.out.println("Sent: " + sent);         Date received = messages[i].getReceivedDate( );         if (received != null) System.out.println("Received: " + received);                  // Now test the flags:         if (messages[i].isSet(Flags.Flag.DELETED)) {           System.out.println("Deleted");         }         if (messages[i].isSet(Flags.Flag.ANSWERED)) {           System.out.println("Answered");         }         if (messages[i].isSet(Flags.Flag.DRAFT)) {           System.out.println("Draft");         }         if (messages[i].isSet(Flags.Flag.FLAGGED)) {           System.out.println("Marked");         }         if (messages[i].isSet(Flags.Flag.RECENT)) {           System.out.println("Recent");         }         if (messages[i].isSet(Flags.Flag.SEEN)) {           System.out.println("Read");         }         if (messages[i].isSet(Flags.Flag.USER)) {           // We don't know what the user flags might be in advance            // so they're returned as an array of strings           String[] userFlags = messages[i].getFlags( ).getUserFlags( );           for (int j = 0; j < userFlags.length; j++) {                System.out.println("User flag: " + userFlags[j]);           }         }                        System.out.println( );       }        // Close the connection        // but don't remove the messages from the server       folder.close(false);            }      catch (Exception ex) {       ex.printStackTrace( );     }                  // Since we may have brought up a GUI to authenticate,     // we can't rely on returning from main( ) to exit     System.exit(0);             } } 

Here's a sample run. The first message has been read and deleted. The second message has no set flags; it hasn't been read, deleted, or answered. The third message has been read and answered but not deleted. Notice that I'm using an IMAP server instead of a POP server:

 %  java FlagsClient imap://elharo@mail.metalab.unc.edu/INBOX  ------------ Message 1 ------------ From: Mike Hall <mikehall@spacestar.com> Reply-to: Mike Hall <mikehall@spacestar.com> To: mrj-dev@public.lists.apple.com Subject: Re: dialog box, parents & X-platform Sent: Mon Dec 13 05:24:38 PST 1999 Received: Mon Dec 13 06:33:00 PST 1999 Deleted Read ------------ Message 2 ------------ From: Kapil Madan <kapil.madan@MIT-MISYS.COM> Reply-to: XML-INTEREST@JAVA.SUN.COM To: XML-INTEREST@JAVA.SUN.COM Subject: Re: first mail to the list! Sent: Mon Dec 13 06:19:46 PST 1999 Received: Mon Dec 13 06:40:00 PST 1999 ------------ Message 3 ------------ From: Jim Jackl-Mochel <jmochel@foliage.com> Reply-to: Jim Jackl-Mochel <jmochel@foliage.com> To: elharo@metalab.unc.edu Subject: CPreProcessorStream Sent: Mon Dec 13 07:14:00 PST 1999 Received: Mon Dec 13 07:08:00 PST 1999 Answered Read 

19.7.4 Folders

Messages received from the network (as opposed to sent to the network) generally belong to some Folder . The getFolder( ) method returns a reference to the Folder object that contains this Message :

 public Folder getFolder( ) 

It returns null if the message isn't contained in a folder.

Within a folder, messages are organized from first (message 1) to last. The getMessageNumber() method returns the relative position of this Message in its Folder :

 public int getMessageNumber( ) 

Messages that aren't in any folder have number 0. Message numbers may change while a program is running if other messages are added to or deleted from a folder.

There's also a protected setMessageNumber() method, but it's only for service providers, not for user code:

 protected void setMessageNumber(int number) 

We'll talk more about folders and what they can do at the end of this chapter. One of the things you can do with a folder is expunge messages from it. This physically deletes the message if it's already been marked as deleted. (A merely deleted message can be "undeleted", whereas an expunged message cannot be.) If a message is expunged, there may still be a Message object pointing to the message, but almost all methods on the message will throw a MessagingException . Thus, it may be important to check whether a message has been expunged before working with it. The isExpunged( ) method does that:

 public boolean isExpunged( ) 

There's also a protected setExpunged() method, but it's only for service providers, not for user code:

 protected void setExpunged(boolean expunged) 

19.7.5 Searching

The final method left in the Message class is match( ) . The match( ) method determines whether a Message satisfies particular search criteria. We'll discuss this more in a bit when we talk about searching folders:

 public boolean match(SearchTerm term) throws MessagingException 



Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net