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 part

  • Methods for getting and setting the headers of the part

  • Methods 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:


Size

The approximate number of bytes in the part


Line count

The number of lines in the part


Disposition

Whether the part is an attachment or should be displayed inline


Description

A brief text summary of the part


Filename

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 MessagingException public int    getLineCount( ) throws MessagingException public String getDisposition( ) throws MessagingException public String getDescription( ) throws MessagingException public String getFileName( ) throws MessagingException, ParseException 

Generally , the getter method returns null or -1 if a part 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"; public static final String INLINE     = "inline"; 

If 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) throws   MessagingException, IllegalWriteException, IllegalStateException   public void setFileName(String filename) throws MessagingException,   IllegalWriteException, IllegalStateException public void setDescription(String description) throws   MessagingException, IllegalWriteException, IllegalStateException 

The setter methods all throw a MessagingException 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.*; 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);             } } 

Here's some typical output. I used an IMAP server 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, Message generally 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 MessagingException 

The setHeader( ) method adds a new header to an outgoing message:

 public void setHeader(String name, String value) throws   MessagingException, IllegalWriteException, IllegalStateException 

If there's already a header with this name, that 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) throws   MessagingException, IllegalWriteException, IllegalStateException 

The removeHeader() method deletes all instances of the named header from this Part :

 public void removeHeader(String name) throws MessagingException,   IllegalWriteException, IllegalStateException 

The getAllHeaders() method returns a java.util.Enumeration object containing all the headers in this message:

 public Enumeration getAllHeaders( ) throws MessagingException 

The Enumeration contains one javax.mail.Header object for each header in the message:

 public class Header extends Object 

The 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) public String getName( ) public String getValue( ) 

Finally, the getMatchingHeaders() method returns an 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)   throws MessagingException public Enumeration getNonMatchingHeaders(String[] names)   throws MessagingException 

You may recall that Example 19-8, HeaderClient , 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.*; 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);             } } 

Here's a typical run:

 %  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 MessagingException 

The 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 MessagingException 

The 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,   MessagingException 

If the part's content has been encoded in some 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( )   throws MessagingException 

A third possibility is to request the content as an unspecified Java object using the getContent() method:

 public Object getContent( ) throws IOException, MessagingException 

This 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)  throws MessagingException, IllegalWriteException, IllegalStateException 

Although this method is declared to throw the usual group of 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,   IllegalWriteException, IllegalStateException 

The setText( ) method sets the MIME type to 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) throws   MessagingException, IllegalWriteException, IllegalStateException 

Another way to write the contents of a part is by using an 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,   MessagingException 

In fact, this not only writes the content of this 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,   IllegalWriteException, IllegalStateException 

In this case, the entire message typically has a content type such as 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.



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

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