Section 15.3. Retrieving Messages


15.3. Retrieving Messages

In addition to allowing programs to create and send messages, the JavaMail API also provides facilities to retrieve incoming messages. This is done via a "message store." For standard Internet mail, the message store is generally held on a mail server and accessed via a mail client protocol.

JavaMail 1.2 and later distributions include message store implementations for POP-3 and IMAP mail client protocols. POP-3 is an older protocol that is still used by most ISPs and many corporate environments. It allows clients to list all available messages, retrieve them, and delete them. POP, which stands for Post Office Protocol, was designed to work like a post office, acting as a central clearinghouse for incoming messages and providing a mechanism to deliver those messages to the recipient.

POP-3 stops at the point of message delivery. IMAP, however, is a newer protocol that allows more complex message handling. IMAP clients can organize messages into different "folders" on the server, based on user commands or built-in server filtering rules. In some implementations, the IMAP server is intended as the final repository for all of a user's messages. The mail client simply accesses the IMAP folders and retrieves message information as required.

15.3.1. Message Stores

Message stores are accessed via implementations of the Store object. The getStore( ) method of Session is used to retrieve a Store implementation. getStore( ) provides a variety of ways to specify the desired message retrieval method, using the JavaMail system properties to greater or lesser degrees. Calling getStore( ) without any arguments will return a message store based completely on JavaMail system properties. You can also pass getStore( ) a text string specifying the protocol to use, a Provider object, or a URLName object that completely defines the connection. If the message store provider requested is unavailable, a NoSuchProviderException is thrown:

     Session session = Session.getDefaultInstance(                                            System.getProperties( ), null);     try {       // Get the default message store       Store defaultStore = session.getStore( );       // Get a store using the default server for IMAP       Store imapStore = session.getStore("imap");       // Get the default IMAP store based on a Provider       Store imapStore2 = session.getStore(session.getProvider("imap"));       // Get an IMAP store based on a URLName       Store namedStore = session.getStore(         new URLName("imap", "imap.my.com", -1, null, "user", "pw"));     } catch (NoSuchProviderException nsp) {     } 

Once you have a Store object, you need to connect to it before retrieving messages. Store extends the abstract Service class, which provides generic connection services for both Store and transport objects, as well as hooks to the JavaMail event model. To connect to the store, use the connect( ) method, which comes in three variants. The parameterless version can be used with message stores that don't require authentication or with authenticated stores that have already had the username and password specified via a URLName or an Authenticator. The other two versions allow you to specify the mail host, username, and password or the mail host, port, username, and password. To use the default value for any of these parameters, pass null (for object parameters) or -1 (for integer parameters.)

Between the system properties, the getStore( ) method, and the connect( ) method, JavaMail provides a variety of ways to specify the attributes of message stores. This is because different protocols require different information to successfully connect to a server. The information that is most directly provided always overrides the system or store defaults.

15.3.2. Handling Incoming Messages

Message stores provide the most general collection of messages possible. However, it is not a good idea to assume that every message in a system will be collected in the same place. JavaMail supports grouping of messages into folders. Every message store contains a default folder, represented by a Folder object. The default folder generally contains one or more subfolders that contain actual messages. The default folder is retrieved via the getdefaultFolder( ) method of a Store object. Subfolders can then be retrieved by calling the getFolder( ) method on the default folder:

     Folder defaultFolder = myStore.getDefaultFolder( );     Folder inboxFolder = defaultFolder.getFolder("INBOX"); 

In this example, we first retrieve the default folder from the message store and INBOX folder. This is, by the way, the standard hierarchy for both IMAP and POP-3 message stores; actual messages are stored, by default, in the INBOX folder. IMAP message stores allow users to configure a broader range of folders internally, either via a mail client or by configuring settings on the mail server. These folders can be nested indefinitely.

After obtaining a folder, you can open it with the Folder.open( ) method. Folders can be opened in either read-only or read/write mode, depending on whether the Folder.READ_ONLY or Folder.READ_WRITE constant is passed to the open( ) method. For example:

     // Open a folder for read-write access:     inboxFolder.open(Folder.READ_WRITE); 

Example 15-2 connects to a message store located at mail.college.edu, via the IMAP and POP-3 protocols and using an Authenticator to handle usernames and passwords .

Example 15-2. IMAP and POP message retrieval
 import javax.mail.*; public class MailCheck {   static String mailhost = "mail.college.edu";   public static void main(String[] args) {     Session session = Session.getDefaultInstance(System.getProperties( ),       new myAuthenticator( ));     try {       Store imapStore = session.getStore(new URLName("imap://" + mailhost));       imapStore.connect( );       System.out.println(imapStore.getURLName( ));       // Get a default folder, and use it to open a real folder       Folder defaultFolder = imapStore.getDefaultFolder( );       defaultFolder = defaultFolder.getFolder("INBOX");       defaultFolder.open(Folder.READ_WRITE);       Message[] msgs = defaultFolder.getMessages( );       if(msgs != null)         for (int i = 0; i < msgs.length; i++) {           System.out.println(msgs[i].getSubject( ));         }       // Now, get it with POP3, using authenticator       Store popStore = session.getStore("pop3");       popStore.connect(mailhost, null, null);       // Get a default folder, and use it to open a real folder       Folder defaultPopFolder = popStore.getDefaultFolder( );       defaultPopFolder = defaultPopFolder.getFolder("INBOX");       defaultPopFolder.open(Folder.READ_ONLY);       msgs = defaultPopFolder.getMessages( );       if(msgs != null)         for (int i = 0; i < msgs.length; i++) {           System.out.println(msgs[i].getSubject( ));         }     } catch (MessagingException me) {       me.printStackTrace(System.out);     }   } // End main( ) } class myAuthenticator extends Authenticator {   protected PasswordAuthentication getPasswordAuthentication( ) {     return new PasswordAuthentication("mailname", "test");   } } 

15.3.3. Searches and Message Management

JavaMail includes a mechanism for programmatically generating search criteria for finding messages within folders. Searches are built via the SearchTerm object. SearchTerm is an abstract class with a single method, match(Message), which returns true or false depending on whether the message matches the term's criteria. JavaMail provides a number of SearchTerm implementations that allow for searching on various aspects of a message. SubjectTerm, for instance, does substring matching on the Subject header. The following code will search a folder for all messages with "Urgent" in the subject line:

     SearchTerm t = new SubjectTerm("Urgent");     Message[] msgs = folder.search(t); 

More complex searches use the AndTerm, OrTerm, and NotTerm objects, which allow chaining and nesting of search criteria. Here's how to tell when we haven't gotten something to our editor on time:

     SearchTerm t = new AndTerm(new FromStringTerm("oreilly.com"),       new SubjectTerm("late"));     Message[] msgs = folder.search(t); 

The search will match messages from oreilly.com in which the subject contains the word "late." You can nest AndTerm and OrTerm objects as deeply as you want, although performance will obviously suffer with more complex searches.

The default implementation of the search functionality will rely on the data provided via Message objects to perform searches. This has the advantage of working regardless of whether the underlying message store supports search functionality and allows programmers to easily define their own search terms by extending the SearchTerm class. If the underlying message store does support search functionality, the implementation has the option of translating the search, wherever possible, into native terms.

Unsolicited commercial email (spam) is of great concern to many readers, so we offer our contribution to cleaner inboxes. Example 15-3 will connect to a mailbox and perform a set of subject searches for strings that generally indicate less than desirable content. Once identified, a copy of the message is sent to a separate address for monitoring purposes, and the original is deleted.

Example 15-3. SpamCleaner.java
 import javax.mail.*; import javax.mail.internet.*; import javax.mail.search.*; import java.util.Properties; public class SpamCleaner {   static String mailhost = "imap.mycompany.com";   static String smtphost = "mail.mycompany.com";   static String redirectEmail = "spamlog@mycompany.com";   static String[] badSubjects = {"XXX", "MAKEMONEYFAST", "!!!!"};   public static void main(String[] args) {     Properties props = new Properties( );     props.setProperty("mail.smtp.host", smtphost);     Session session = Session.getDefaultInstance(props, null);     try {       Store imapStore = session.getStore("imap");       imapStore.connect(mailhost, "scott", "yu7xx");       Folder defaultFolder = imapStore.getDefaultFolder( );       Folder inboxFolder = defaultFolder.getFolder("INBOX");       inboxFolder.open(Folder.READ_WRITE);       // Assemble some search criteria, using nested OrTerm objects       SearchTerm spamCriteria = null;       for(int i=0; i<badSubjects.length; i++) {         SubjectTerm st = new SubjectTerm(badSubjects[i]);         if(spamCriteria == null)           spamCriteria = st;         else           spamCriteria = new OrTerm(spamCriteria, st);       }       Message[] msgs = inboxFolder.search(spamCriteria);       if(msgs != null)         for (int i = 0; i < msgs.length; i++) {           // Redirect the message           Message m = new MimeMessage((MimeMessage)msgs[i]);           m.setRecipient(Message.RecipientType.TO,             new InternetAddress(redirectEmail));           // Clear out the cc: field so we don't loop or anything           m.setHeader("CC", "");           Transport.send(m);           // Now delete the original           System.out.println(            "Deleting Message "+msgs[i].getMessageNumber( )+":"             + msgs[i].getSubject( ));           msgs[i].setFlag(Flags.Flag.DELETED, true);         }       // Close inbox folder, with "Expunge" flag set to true       inboxFolder.close(true);       imapStore.close( );     } catch (MessagingException me) {       me.printStackTrace(System.out);     }   } // End main( ) } 

Example 15-3 also shows how to delete a message from an IMAP message store. Rather than calling a delete method on a folder or message object, we call the setFlag( ) method of Message and set the deletion flag (identified by the Flags.Flag.DELETED constant) to true. You can examine all the flags set on a particular message by calling the getFlags( ) method of Message, which returns a Flags object. However, in most cases, the ability to set the DELETED flag is all you need.

Note that marking a message as deleted doesn't actually delete it. Messages aren't removed from the message store until the store is expunged. This can be done either by calling the expunge( ) method of Folder or passing a Boolean true value to the close( ) method of Folder. Once a message has been expunged, the message numbers within the folder (returned by the getMessageNumber( ) method of Message) may change.

Example 15-3 shows how to clone a message. JavaMail 1.2 added a new constructor to the MimeMessage object, which causes the new MimeMessage to be populated with all the fields and content from the original. The example does something dangerous in assuming that the message retrieved by the search( ) function will actually be a MimeMessage. This is fine when using the Sun service implementations, but a more general version of this program would have to be more careful, either constructing the new Message object by hand or trying to modify and resend the copy pulled from the message store (which is generally not allowed).

If you are working with a message store that supports modification of message content, the loop in Example 15-3 could be replaced with this instead:

     for (int i = 0; i < msgs.length; i++) {       // Alter the message       System.out.println("Altering Message "+msgs[i].getMessageNumber( )+":"         + msgs[i].getSubject( ));       msgs[i].setSubject("SPAM:" + msgs[i].getSubject( ));       msgs[i].saveChanges( );     } 

Rather than deleting the message, this code alters the subject line and inserts the word "SPAM:" before the original subject. The saveChanges( ) method writes the changes back to the message stores. However, we can't use this approach with this code because incoming IMAP messages are read-only.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

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