Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








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

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


/ 164