19.8 The Part Interface
Both Message and
BodyPart implement the Part
interface. Every Message is a
Part. However, some parts may contain other parts.
The Part interface declares three kinds of
methods:Methods for getting and setting the attributes of the partMethods for getting and setting the headers of the partMethods for getting and setting the contents of the part
The attributes of the part are things such as the size of the message
or the date it was received, details that aren't
explicitly specified in the message's header. The
headers, by contrast, are name-value pairs included at the front of
the part. Finally, the content of the part is the actual data that
the message is trying to transmit.
19.8.1 Attributes
The
JavaMail API defines five attributes for parts:
The approximate number of bytes in the part
The number of lines in the part
Whether the part is an attachment or should be displayed inline
A brief text summary of the part
The name of the file that the attachment came from
Not all parts have all attributes. For instance, a part that does not
represent an attached file is unlikely to have a filename attribute.
Each attribute is mapped to a getter method:
public int getSize( ) throws MessagingExceptionGenerally, the getter method returns null or -1 if a part
public int getLineCount( ) throws MessagingException
public String getDisposition( ) throws MessagingException
public String getDescription( ) throws MessagingException
public String getFileName( ) throws MessagingException, ParseException
doesn't possess the requested attribute. It throws a
MessagingException if there's
some problem retrieving the message; for instance, if the connection
goes down while the message is being retrieved.The getSize( ) method returns the
approximate number of bytes in the part. Depending on the server and
protocol, this may or may not account for changes in the size caused
by operations such as Base64 encoding the data.The getLineCount() method returns the approximate
number of lines in the content of the part or -1 if the number of
lines isn't known. Again, the number returned may or
may not account for changes in the size of the part caused by the
part's encoding.The getDisposition() method returns a string indicating
whether the content should be presented inline or as an attachment.
The value returned should either be null (the
disposition is not known) or one of the two named constants
Part.INLINE or Part.ATTACHMENT:
public static final String ATTACHMENT = "attachment";If
public static final String INLINE = "inline";
the disposition is Part.ATTACHMENT,
getFileName( ) should return the name of the file
to save the attachment in. Otherwise, getFileName() probably returns null. However, some
email clients, including Netscape 4.5 for Windows, do not properly
set the Content-disposition header for attachments. Consequently,
when receiving messages with attachments that were sent by Navigator,
you'll often get a null
disposition but a non-null filename. In practice, it seems more
reliable to assume that any body part with a non-null filename is an
attachment regardless of the Content-disposition header, and any body
part with no filename and no Content-disposition header should be
displayed inline if possible. If it's not
possiblefor instance, if you can't handle the
MIME typeyou can either ask the user for a filename or pick
some reasonable default, such as
attachment1.tif.Normally, the filename includes only the actual name of the file but
not any of the directories the file was in. It's up
to the application receiving the message to decide where to put the
incoming file. For instance, Eudora generally stores attachments in
the Attachments folder inside the Eudora folder. However, the user
has an option to pick a different location. Since
it's not uncommon to receive multiple attachments
with the same name over time, check to see whether a file with the
attached file's name already exists before writing
out the attachment. If a similarly named file does exist,
you'll have to rename the attachment in some
reasonable fashionfor instance, by appending a 1 or a 2 to it:
e.g., vcard1.vcf,
vcard2.vcf, and so on.The description, disposition, and filename attributes also have
setter methods. However, the size and line count attributes are
determined by the content of the part rather than a setter method:
public void setDisposition(String disposition) throwsThe setter methods all throw a MessagingException
MessagingException, IllegalWriteException, IllegalStateException
public void setFileName(String filename) throws MessagingException,
IllegalWriteException, IllegalStateException
public void setDescription(String description) throws
MessagingException, IllegalWriteException, IllegalStateException
if there's some problem while changing the message.
They can also throw an IllegalWriteException if
the relevant attribute of the part cannot be modified or an
IllegalStateException if the part belongs to a
read-only folder.The setDisposition() method determines whether the part
is to be viewed inline or as an attachment. Although
it's declared to take a String as
an argument, this String should be one of the two
named constants, Part.INLINE or
Part.ATTACHMENT. Parts that are attachments
generally have a filename included in their metainformation. This
name can be set with the setFileName() method. Finally, the
setDescriptionMethod() can take any
String at all to add a description to the part.Example 19-10 is a simple program that connects to a
mail server and reads the attributes of the messages in the mailbox.
Since each message is itself a part (even if it contains other
parts), we can invoke these methods on the entire message.
Example 19-10. A program to read mail attributes
import javax.mail.*;Here's some typical output. I used an IMAP server
import javax.mail.internet.*;
import java.util.*;
public class AttributeClient {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println(
"Usage: java AttributeClient 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)
+ " ------------");
String from = InternetAddress.toString(messages[i].getFrom( ));
if (from != null) System.out.println("From: " + from);
String to = InternetAddress.toString(
messages[i].getRecipients(Message.RecipientType.TO));
if (to != null) System.out.println("To: " + 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);
System.out.println( );
// Here's the attributes...
System.out.println("This message is approximately "
+ messages[i].getSize( ) + " bytes long.");
System.out.println("This message has approximately "
+ messages[i].getLineCount( ) + " lines.");
String disposition = messages[i].getDisposition( );
if (disposition == null) ; // do nothing
else if (disposition.equals(Part.INLINE)) {
System.out.println("This part should be displayed inline");
}
else if (disposition.equals(Part.ATTACHMENT)) {
System.out.println("This part is an attachment");
String fileName = messages[i].getFileName( );
if (fileName != null) {
System.out.println("The file name of this attachment is "
+ fileName);
}
}
String description = messages[i].getDescription( );
if (description != null) {
System.out.println("The description of this message is "
+ description);
}
}
// 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);
}
}
because most of these methods don't work nearly as
well with POP servers. IMAP servers can give you the attributes of a
message without making you download the entire message, but POP
servers aren't that sophisticated:
% java AttributeClient imap://elharo@mail.sunsite.unc.edu/INBOX
------------ Message 1 ------------
From: "Richman, Jeremy" <jrichman@hq.ileaf.com>
To: 'xsl-list' <XSL-List@mulberrytech.com>
Subject: Re: New twist: eliminating nodes with duplicate content
Sent: Mon Dec 06 08:37:51 PST 1999
This message is approximately 3391 bytes long.
This message has approximately 87 lines.
------------ Message 2 ------------
From: schererm@us.ibm.com
To: Unicode List <unicode@unicode.org>
Subject: Re: Number ordering
Sent: Mon Dec 06 11:00:28 PST 1999
This message is approximately 1554 bytes long.
This message has approximately 18 lines.
------------ Message 3 ------------
From: John Posner <jjp@connix.com>
To: 'Nakita Watson' <nakita@oreilly.com>
Subject: RE: Another conference Call
Sent: Mon Dec 06 11:16:38 PST 1999
This message is approximately 1398 bytes long.
This message has approximately 19 lines.
19.8.2 Headers
Classes
that implement the Part interfacefor
example, Messagegenerally declare methods
to return specific headers such as To: or From:. The
Part interface, by contrast, declares methods to
get and set arbitrary headers regardless of name.The getHeader( )
method gets the values of all the headers with a name that matches
the name argument. Some headers such as Received:
can have multiple values and can be included in a message multiple
times, so this method returns those values as an array of strings. It
returns null if no header with that name is
present in this Part:
public String[] getHeader(String name) throws MessagingExceptionThe setHeader( ) method adds a new
header to an outgoing message:
public void setHeader(String name, String value) throwsIf there's already a header with this name, that
MessagingException, IllegalWriteException, IllegalStateException
header is deleted and the new one inserted in its placeunless
the folder in which the message resides is read-only, in which case
an IllegalStateException is thrown.By contrast, the addHeader() method adds a header with the
specified name but does not replace any that exist:
public void addHeader(String name, String value) throwsThe removeHeader() method deletes all instances of the
MessagingException, IllegalWriteException, IllegalStateException
named header from this Part:
public void removeHeader(String name) throws MessagingException,The getAllHeaders() method returns a
IllegalWriteException, IllegalStateException
java.util.Enumeration object containing all the
headers in this message:
public Enumeration getAllHeaders( ) throws MessagingExceptionThe Enumeration contains one
javax.mail.Header object for each header in the
message:
public class Header extends ObjectThe Header class is very simple, with just a
constructor to set the name and value of the header, and
getName( ) and getValue( )
methods to return them:
public Header(String name, String value)Finally, the getMatchingHeaders() method returns an
public String getName( )
public String getValue( )
Enumeration containing all the headers in this
message with names that are one of the strings in the argument
names array. The getNonMatchingHeaders() method returns an
Enumeration containing all the headers in this
message with names that are not one of the
strings in the argument names array. Again, the
Enumeration contains Header
objects:
public Enumeration getMatchingHeaders(String[] names)You may recall that Example 19-8, HeaderClient,
throws MessagingException
public Enumeration getNonMatchingHeaders(String[] names)
throws MessagingException
printed only a few prespecified headers, such as To: and From:. With
the methods of the Part interface (that
Message implements), it's easy to
expand this to cover all headers in the message, whether known in
advance or not. Example 19-11 demonstrates. This ability is important
because Internet email can contain arbitrary headers;
it's not limited to just a few headers mentioned in
the relevant RFCs. For instance, some graphical mail clients for X
Windows use a completely nonstandard X-Face: header, whose value is a
48-pixel by 48-pixel, black-and-white, uuencoded bitmap of the
sender's countenance. Other clients use custom
headers for purposes both more serious and sillier.
Example 19-11. A program to read mail headers
import javax.mail.*;Here's a typical run:
import javax.mail.internet.*;
import java.util.*;
public class AllHeaderClient {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println(
"Usage: java AllHeaderClient 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 difference...
Enumeration headers = messages[i].getAllHeaders( );
while (headers.hasMoreElements( )) {
Header h = (Header) headers.nextElement( );
System.out.println(h.getName( ) + ": " + h.getValue( ));
}
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 AllHeaderClient pop3://eharold@utopia.poly.edu/INBOX
------------ Message 1 ------------
Received: (from eharold@localhost)
by utopia.poly.edu (8.8.8/8.8.8) id QAA05728
for eharold; Tue, 30 Nov 1999 16:14:29 -0500 (EST)
Date: Tue, 30 Nov 1999 16:14:29 -0500 (EST)
From: Elliotte Harold <eharold@utopia.poly.edu>
Message-Id: <199911302114.QAA05728@utopia.poly.edu>
To: eharold@utopia.poly.edu
Subject: test
Content-Type: text
X-UIDL: 87e3f1ba71738c8f772b15e3933241f0
Status: RO
------------ Message 2 ------------
Received: from russian.cloud9.net (russian.cloud9.net [168.100.1.4])
by utopia.poly.edu (8.8.8/8.8.8) with ESMTP id OAA28428
for <eharold@utopia.poly.edu>; Wed, 1 Dec 1999 14:05:06 -0500 (EST)
Received: from [168.100.203.234] (macfaq.dialup.cloud9.net [168.100.203.234])
by russian.cloud9.net (Postfix) with ESMTP id 24B93764F8
for <eharold@utopia.poly.edu>; Wed, 1 Dec 1999 14:02:50 -0500 (EST)
Mime-Version: 1.0
X-Sender: macfaq@mail.cloud9.net
Message-Id: <v04210100b46b1f97969d@[168.100.203.234]>
Date: Wed, 1 Dec 1999 13:55:40 -0500
To: eharold@utopia.poly.edu
From: Elliotte Rusty Harold <elharo@macfaq.com>
Subject: New system
Content-Type: text/plain; charset="us-ascii" ; format="flowed"
X-UIDL: 01fd5cbcf1768fc6c28f9c8f934534b5
Status: RO
------------ Message 3 ------------
Received: from russian.cloud9.net (russian.cloud9.net [168.100.1.4])
by utopia.poly.edu (8.8.8/8.8.8) with ESMTP id HAA17345
for <eharold@utopia.poly.edu>; Thu, 2 Dec 1999 07:55:04 -0500 (EST)
Received: from [168.100.203.234] (macfaq.dialup.cloud9.net [168.100.203.234])
by russian.cloud9.net (Postfix) with ESMTP id C036A7630E
for <eharold@utopia.poly.edu>; Thu, 2 Dec 1999 07:54:58 -0500 (EST)
Mime-Version: 1.0
X-Sender: elharo@luna.oit.unc.edu
Message-Id: <v04210100b46c0c686ecc@[168.100.203.234]>
Date: Thu, 2 Dec 1999 06:45:52 -0500
To: eharold@utopia.poly.edu
From: "Dr. Mickel" <Greatsmiles@mail.com>(by way of Elliotte Rusty Harold)
Subject: Breath RX Products now available Online!
Sender: elharo@metalab.unc.edu
Content-Type: text/plain; charset="us-ascii" ; format="flowed"
X-UIDL: 40fa8af2aca1a8c11994f4c56b792720
Status: RO
19.8.3 Content
Every
part has content that can be represented as a sequence of bytes. For
instance, in a part that's a simple email message,
the content is the body of the message. However, in multipart
messages, this content may itself contain other parts. The content of
each of these parts can be represented as a sequence of bytes.
Furthermore, this sequence of bytes may represent some more specific
content type, such as a uuencoded GIF image or a Base64-encoded WAV
audio clip.
19.8.3.1 Reading the contents of the part
The Part interface declares two methods for
determining a part's MIME content type. The
getContentType()
method returns the MIME content type of the part as a string; for
example: text/plain;
charset="us-ascii"; format=
"flowed". It returns null if the content
type can't be determined:
public String getContentType( ) throws MessagingExceptionThe isMimeType( ) method returns
true if this part has the specified MIME type and
subtype. Additional parameters, such as charset, are ignored:
public boolean isMimeType(String mimeType) throws MessagingExceptionThe Part interface also declares several methods
that return the content as a variety of different Java objects,
including InputStream, String,
DataHandler, and more. The
getInputStream()
method returns an InputStream from which the
part's content can be read:
public InputStream getInputStream( ) throws IOException,If the part's content has been encoded in some
MessagingException
wayfor example, Base64-encodedthen the
InputStream reads the decoded content. The
JavaMail API supports all common encodings except the BinHex format
used for Macintosh files. If it encounters a BinHex-encoded
attachment, it strips the MIME headers but otherwise leaves the
BinHex data untouched. BinHex documents are tough to deal with on
most platforms because of the unusual two-fork nature of a Mac file.
Unless you're a real Mac expert,
you're probably better off using a third-party
utility such as StuffIt Expander (http://www.stuffit.com/) to decode the file.Another possibility is to request a DataHandler
for the content with the getDataHandler(
) method. The
DataHandler class comes from the Java Activation
Framework. It declares methods to help decide what to do with the
contentfor instance, by finding the right Java bean or helper
application to display the content:
public javax.activation.DataHandler getDataHandler( )A third possibility is to request the content as an unspecified Java
throws MessagingException
object using the getContent()
method:
public Object getContent( ) throws IOException, MessagingExceptionThis is reminiscent of the getContent( ) method of
java.net.URL. However, rather than relying on the
poorly designed content handler mechanism, this getContent() method uses the Java Activation Framework, so the
behavior is a little more clearly specified. Most of the time, if the
content type is text/plain, a
String will be returned. If the content type is
multipart, then regardless of the subtype, a
javax.mail.Multipart object is returned. If the
content type is some other type that is recognized by the underlying
DataHandler, an appropriate Java object is
returned. Finally, if the type is unrecognized, an
InputStream is returned.You can change which objects are returned for which content types by
providing your own DataHandler, installed with
the setDataHandler( )
method:
public void setDataHandler(javax.activation.DataHandler handler)Although this method is declared to throw the usual group of
throws MessagingException, IllegalWriteException, IllegalStateException
exceptions, it's perhaps a little less likely to
actually do so, since setting the DataHandler only
affects the Message object rather than the actual
message stored on the server.
19.8.3.2 Writing the contents of the part
When sending a message, you naturally must set the
message's contents. Since email messages are text,
the most straightforward way is just to provide the text of the part
with setText( ):
public void setText(String text) throws MessagingException,The setText( ) method sets the MIME type to
IllegalWriteException, IllegalStateException
text/plain. Other objects can be made into content
as well, provided the part has a DataHandler that
understands how to convert them to encoded text. This is done with
the setContent( ) method:
public void setContent(Object o, String type) throwsAnother way to write the contents of a part is by using an
MessagingException, IllegalWriteException, IllegalStateException
OutputStream. The writeTo()
method writes the content of the Part onto an
OutputStream. If necessary, it will encode the
content using Base64, quoted-printable, or some other format as
specified by the DataHandler:
public void writeTo(OutputStream out) throws IOException,In fact, this not only writes the content of this
MessagingException
Part, it also writes the attributes and headers of
the part. Example 19-4 used this to provide a simple way of getting
an entire email message in one fell swoop. It's most
convenient, though, when you want to send an entire message to an
SMTP server in one method call.Finally, multiple parts can be added to a part by wrapping them in a
Multipart object and passing that to
setContent( ):
public void setContent(Multipart mp) throws MessagingException,In this case, the entire message typically has a content type such as
IllegalWriteException, IllegalStateException
multipart/mixed,
multipart/signed, or
multipart/alternative. The individual parts of the
message are all enclosed in one envelope but each part of the message
has its own content type, content encoding, and data. The multiple
parts may be used to present different forms of the same document
(e.g., HTML and plain-text mail), a document and metainformation
about the document (e.g., a message and the MD5 digest of the
message), or several different documents (e.g., a message and several
attached files). The next section expands on this process.
• Table of Contents• Index• Reviews• Reader Reviews• Errata• AcademicJava Network Programming, 3rd EditionBy
Elliotte Rusty Harold Publisher: O'ReillyPub Date: October 2004ISBN: 0-596-00721-3Pages: 706
Thoroughly revised to cover all the 100+ significant updates
to Java Developers Kit (JDK) 1.5, Java Network
Programming is a complete introduction to
developing network programs (both applets and applications)
using Java, covering everything from networking fundamentals
to remote method invocation (RMI). It includes chapters on
TCP and UDP sockets, multicasting protocol and content
handlers, servlets, and the new I/O API. This is the
essential resource for any serious Java developer.