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
.
The Message
class has
three constructors:
protected Message( ) protected Message(Folder folder, int messageNumber) protected Message(Session session)
Since all the constructors are protected, these 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, then the Folder
or
Session
you’re reading from will create the
Message
objects and pass them to you.
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, then 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.
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 allow 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.
A typical RFC 822 message contains a header that looks something like this:
From [email protected] Fri Aug 5 10:57:08 1994 Date: Fri, 5 Aug 1994 10:57:04 +0700 From: [email protected] (Denise Levi) To: [email protected] Subject: Apologies Content-Length: 517 Status: RO X-Lines: 13
The exact fields 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 allow you to 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.
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( )
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.
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 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 used 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.
Since the subject is simply a single string of text, it’s very 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.
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, 5 Aug
2000 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 e) { e.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 that and simply omits the
fields if they’re not present.
% java HeaderClient pop3://[email protected]/INBOX
------------ Message 1 ------------
From: Elliotte Harold <[email protected]>
Reply-to: Elliotte Harold <[email protected]>
To: [email protected]
Subject: test
Sent: Tue Nov 30 13:14:29 PST 1999
------------ Message 2 ------------
From: Elliotte Rusty Harold <[email protected]>
Reply-to: Elliotte Rusty Harold <[email protected]>
To: [email protected]
Subject: New system
Sent: Wed Dec 01 10:55:40 PST 1999
------------ Message 3 ------------
From: Dr. Mickel <[email protected]>
Reply-to: Dr. Mickel <[email protected]>
To: [email protected]
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.
When you invoke one of the previous set or add methods, some
implementations will 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 that they 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 a message, whether I’ve read a
message, and so on. As Figure 19.5 shows, these are
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 will be set.
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
, that 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 e) { e.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://[email protected]/INBOX
------------ Message 1 ------------
From: Mike Hall <[email protected]>
Reply-to: Mike Hall <[email protected]>
To: [email protected]
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 <[email protected]>
Reply-to: [email protected]
To: [email protected]
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 <[email protected]>
Reply-to: Jim Jackl-Mochel <[email protected]>
To: [email protected]
Subject: CPreProcessorStream
Sent: Mon Dec 13 07:14:00 PST 1999
Received: Mon Dec 13 07:08:00 PST 1999
Answered
Read
Messages
received from the network (as opposed to being sent to the network)
will 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 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)
3.129.13.201