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 The Since all the constructors are protected, they are primarily for the use of subclasses such as If you already have a This method creates a new You've already seen that when you're reading email, the JavaMail API creates 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 A typical RFC 822 message contains a header that looks something like this:
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.
These four methods get and set the From: field of a message:
The 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:
The semantics of these methods are the same as for the equivalent 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 There are two methods to find the addressees of the The All four of these methods can throw a Since the subject is simply a single string of text, it's easy to set and get with these two methods:
As with earlier setter methods, Messages also have sent and received dates. Three methods allow programs to access these fields:
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 Here's some typical output. Several of the requested strings were 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.
When you invoke one of the previous set or add methods, some implementations store the changes immediately. Others, however, may not. The 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. 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 Screenshot-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 Seven flags are predefined as instances of the public static inner class In addition, some implementations may allow arbitrary user-defined flags. If so, the USER flag is set.
The The Finally, the You delete messages by setting their 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 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:
Messages received from the network (as opposed to sent to the network) generally belong to some It returns 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 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 There's also a protected The final method left in the 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
.
Creating Messages
Message
class has three constructors:
protected Message( )
protected Message(Folder folder, int messageNumber)
protected Message(Session session)
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.
Replying to messages
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
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.
Getting messages from folders
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
Message
class is invoked that requires this information.Basic Header Info
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 From address
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
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.
The Reply-to address
public Address[] getReplyTo( ) throws MessagingException public void setReplyTo(Address[] addresses) throws MessagingException, MethodNotSupportedException, IllegalWriteException, IllegalStateException
getFrom( )
and setFrom( )
methods-in 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.
The recipient addresses
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
Message
:
public abstract Address[] getRecipients(Message.RecipientType type) throws MessagingException public Address[] getAllRecipients( ) throws MessagingException
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
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.
The subject of the message
public abstract String getSubject( ) throws MessagingException public abstract void setSubject(String subject) throws MessagingException, IllegalWriteException, IllegalStateException
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.
The date of the message
public abstract Date getSentDate( ) throws MessagingException public abstract void setSentDate(Date date) throws MessagingException, IllegalWriteException, IllegalStateException public abstract Date getReceivedDate( ) throws MessagingException
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); }
}
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@bugmenot.com>
Reply-to: Elliotte Rusty Harold <elharo@bugmenot.com>
To: eharold@utopia.poly.edu Subject: New system Sent: Wed Dec 01 10:55:40 PST 1999
------------ Message 3 ------------
From: Dr. Mickel <Greatsmiles@bugmenot.com>
Reply-to: Dr. Mickel <Greatsmiles@bugmenot.com>
To: eharold@utopia.poly.edu Subject: Breath RX Products now available Online!
Sent: Thu Dec 02 03:45:52 PST 1999
Saving changes
saveChanges()
method commits the changes made to a Message
object:
public abstract void saveChanges( ) throws MessagingException,
IllegalWriteException, IllegalStateException
Flags
javax.mail.Flags
class:
public class Flags extends Object implements Cloneable
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
Screenshot-5. Pine shows flags as letters in the lefthand column
getFlags( )
method returns the flags of a particular message:
public abstract Flags getFlags( ) throws MessagingException
isSet( )
method tests whether a specified flag is set for the given message:
public boolean isSet(Flags.Flag flag) throws MessagingException
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
Flags.Flag.DELETED
flag to true
. For example, to delete message
:
message.setFlag(Flags.Flag.DELETED, true);
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); }
}
% java FlagsClient imap://elharo@mail.metalab.unc.edu/INBOX
------------ Message 1 ------------
From: Mike Hall <mikehall@bugmenot.com>
Reply-to: Mike Hall <mikehall@bugmenot.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@bugmenot.com>
Reply-to: XML-INTEREST@JAVA.oracle.com To: XML-INTEREST@JAVA.oracle.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@bugmenot.com>
Reply-to: Jim Jackl-Mochel <jmochel@bugmenot.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
Folders
Folder
. The getFolder( )
method returns a reference to the Folder
object that contains this Message
:
public Folder getFolder( )
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( )
setMessageNumber()
method, but it's only for service providers, not for user code:
protected void setMessageNumber(int number)
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( )
setExpunged()
method, but it's only for service providers, not for user code:
protected void setExpunged(boolean expunged)
Searching
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