Retrieving Email Messages

So far, today's lesson has concentrated on sending email messages, but the JavaMail API provides support for other common email operations, such as retrieving messages and attachments and deleting messages on the server. The remainder of today's lesson gives you an introduction to these operations.

Retrieving Simple Messages

Earlier in today's lesson, you saw that the most prevalent message-retrieval protocols are POP3 and IMAP. The application you are about to build uses POP3 to retrieve messages from a mail server. Note that because you work with an abstraction of a mail system, the technique for accessing a mailbox with IMAP is very similar.

Figure 11.3 shows the process for retrieving messages.

Figure 11.3. The message retrieval process.


The first step in the process is to connect to a message store on a given host as follows:

Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore("pop3");
store.connect(host, username, password);

No properties are needed for this example as the POP3 host and other information is passed through the javax.mail.Store.connect() method call. The Store object is used to connect to the message store and is obtained from the Session object using the getStore() method. The parameter to the getStore() method specifies the retrieval protocol and returns a Store that implements this protocol. JavaMail supports the “POP3” and “IMAP” protocols but third party products could support additional protocols. After you create a Store object, you pass the POP3 host, username and password to its connect() method. At this point you are ready to retrieve messages.

NOTE

When you use the JavaMail API to send POP3 credential information, the information is not encrypted. In fact, the API does not come with built-in support for credential encryption or the facility to use secure mail transport protocols. However, some third-party protocol providers do provide support for secure mail protocols, such as IMAP over SSL/TLS. You can find a list of these providers at http://java.sun.com/products/javamail.


After connecting to the message store, you open a message folder. To do this, create a javax.mail.Folder object using the Store.getFolder() method specifying the name of the folder as a parameter. Both POP3 and IMAP protocols support the INBOX folder containing messages received by the email server. An IMAP email server supports additional folders but POP3 only supports the single INBOX folder. Open the INBOX using:

Folder folder = store.getFolder("INBOX");

Now you can open the folder using the open() method specifying the mode with which to open the folder. Possible values for the mode are Folder.READ_ONLY and Folder.READ_WRITE:

folder.open(Folder.READ_ONLY);

NOTE

If you open a folder READ_ONLY, you will not be able to delete the messages from the POP3 mail store after you have read them.


To retrieve the messages from the folder, you can either use the getMessage() method to retrieve a message by its message number (its position in the folder) or you can use one of the various getMessages() methods to retrieve either all messages or a range of messages. The following example retrieves all messages:

Message messages[] = folder.getMessages();

The final step in the message retrieval process is to perform some form of user-defined processing on the retrieved messages. In this example, the code simply iterates through the messages printing the subject, sender, time, and then printing the entire message.

for (int i=0; i<messages.length; i++) {
  Message msg = messages[i];
  System.out.println(i + ": " + msg.getFrom()[0] + "	"
         + msg.getSubject() + "	" + msg.getSentDate() + "

");
  msg.writeTo(System.out);
}

As you can see, the Message class exposes a number of JavaBean getter methods to access the message attributes. The writeTo() method outputs the message content to a specified output stream, which in this example is System.out.

That is the message retrieval process completed, but to complete the code you must close the resources used by calling their close() methods.

You must pass the Folder's close() method a single Boolean parameter indicating whether to delete any messages marked for deletion within the folder on the server. You will learn how to mark messages for deletion in the “Deleting Messages” section of today's lesson.

Closing the Store object disconnects your application from the POP3 server.

Listing 11.8 shows the complete code for this application.

Listing 11.8. RetrieveMail.java Full Listing
import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.*;

public class RetrieveMail {
    public static void main(String[] args) {
        if (args.length!=3) {
        System.out.println("Usage: RetrieveMail POPHost username password");
            System.exit(1);
        }

        String host = args[0];
        String username = args[1];
        String password = args[2];

        try {
            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);

            Store store = session.getStore("pop3");
            store.connect(host, username, password);

            Folder folder = store.getFolder("INBOX");
            folder.open(Folder.READ_ONLY);

            Message messages[] = folder.getMessages();
            for (int i=0; i<messages.length; i++) {
                System.out.println(i + ": " + messages[i].getFrom()[0] + "	" +
                     messages[i].getSubject() + "	" +
                     messages[i].getSentDate() + "

");
                messages[i].writeTo(System.out);
            }

            folder.close(false);
            for (int i=0; i<messages.length; i++) {
              Message msg = messages[i];
              System.out.println(i + ": " + msg.getFrom()[0] +
              "	" + msg.getSubject() +
              "	" + msg.getSentDate() + "

");
              msg.writeTo(System.out);
           }
           store.close();
        }
        catch (MessagingException me) {
            System.err.println(me);
        }
        catch (IOException ioe) {
            System.err.println(ioe);
        }
    }
}

Run this example using the command:

asant RetrieveMail

Supply a hostname, username, and password. To read the messages you sent in the previous examples, use the host name localhost, username juliet and password juliet. Any messages stored on your mailHost will now be printed to the screen but will not be deleted from the folder.

Deleting Messages

In the previous example, you retrieved messages from a mail server without deleting them. Of course, in a real-world situation, you could not leave messages on the mail server indefinitely. Normally, you would delete messages after retrieving them.

NOTE

If you are working with an IMAP server you may choose to save the messages in a different folder rather than delete them. Sadly, manipulating and saving messages with an IMAP server is beyond the scope of today's introduction to JavaMail.


To modify the previous example so that messages are deleted after retrieval is a straightforward process. To retrieve and delete messages you must open the folder in READ_WRITE mode. So simply replace the line of code that opened the folder with the following line:

folder.open(Folder.READ_WRITE);

Deleting messages involves the use of flags. Messages can support a number of flags that indicate the state of a message. The javax.mail.Flags.Flag class supplies predefined flags that are accessible as static fields. See Table 11.3 for a list of flags and their meaning. Note, however, that just because these flags exist doesn't mean that all mail servers support them. In fact, POP3 mail servers typically only support the DELETED flag; IMAP servers normally support more of the flags. To find out exactly which flags your mail server supports, you can call the Folder.getPermanentFlags() method, which returns a Flags object that contains all the supported flags.

Table 11.3. Predefined Message Flags
FlagDescription
ANSWEREDThe client has replied to the message.
DELETEDThe message is marked for deletion.
DRAFTThe message is a draft.
FLAGGEDThe client has flagged the message.
RECENTThe message has arrived in the folder since it was last opened.
SEENThe message has been retrieved by the client.
USERIndicates that the folder supports user defined flags.

To mark a message for deletion, you use the Message.setFlag() method and supply the flag and a Boolean indicating the flag's value. For example, to mark a message ready for deletion:

message.setFlag(Flags.Flag.DELETED, true);

For Listing 11.8 you would modify the for loop as follows:

for (int i=0; i<messages.length; i++) {
   Message msg = messages[i];
   System.out.println(i + ": " + msg.getFrom()[0] +
    "	" + msg.getSubject() +
    "	" + msg.getSentDate() + "

");
   msg.writeTo(System.out);
   msg.setFlag(Flags.Flag.DELETED, true);
}

To complete the deletion, you must pass the folder's close() method a Boolean of true. Passing the value of true ensures that any messages marked for deletion are deleted when the folder closes:

folder.close(true);

That's it. If you want to view the complete modified listing, you will find it in a file called DeleteMail.java on the Web site accompanying this book.

CAUTION

Do not run the DeleteMail example on a live mail store as it will download and then delete all the messages in the inbox.


Once a message has been deleted from a folder, and that folder has been closed, the message no longer exists unless you have kept a local copy. If you change your mind about deleting a message you should mark the message as not deleted using:

msg.setFlag(Flags.Flag.DELETED, false);

before you close the folder. However if you close the folder with the false parameter

folder.close(false);

no messages will be deleted and any deleted message flags will be discarded.

Now that you know how to read simple messages, the next step is to read messages with attachments.

Retrieving Email Attachments

Retrieving an attachment from a message is a more involved process than simply reading a normal message. As you saw earlier, an attachment is a part of a multi-part message. When you retrieve a message that has an attachment, you must iterate through the body parts and identify which ones are attachments. After you identify a part as an attachment, you must process it, typically that means writing that part's content to a file.

You cannot simply identify a part as an attachment through its content type, because the sender of the message part may have intended for it to be displayed inline—like the HTML message you created earlier. Fortunately, RFC 2183 defines the Content-Disposition MIME message header. This header allows a message sender to mark body parts as either INLINE (displays within the message text) or ATTACHMENT (the part is an attachment).

The next example simply reads all messages in the mailbox and for any messages with an attachment saves those attachments to a local directory on disk. The first step for handling an attachment is to check each message to see if it is a multi-part message:

Message messages[] = folder.getMessages();
for (int i=0; i<messages.length; i++) {
   System.out.println(messages[i].getFrom()[0] + "	" +
                      messages[i].getSubject());
   Object obj = messages[i].getContent();
   if (obj instanceof Multipart)
       processMessage((Multipart)obj);
}

For each identified multi-part message the helper method processMessage() identifies the message parts that are attachments:

private static void processMessage(Multipart multipart)
throws MessagingException, IOException
{
    for (int i=0; i<multipart.getCount(); i++) {
        Part part = multipart.getBodyPart(i);
        String disposition = part.getDisposition();
        if (disposition != null &&
            disposition.equals(Part.ATTACHMENT))
            writeFile(part.getFileName(), part.getInputStream());
    }
}

The processMessage() uses the Part.getDisposition() method to get the part's disposition (how it should be displayed to the user) and checks whether the disposition is of the type ATTACHMENT. If the disposition is of the type ATTACHMENT, the code gets the attachment filename and an InputStream object representing the data and saves the file to disk.

The first task for saving the attachment is to check that the attachment has a valid filename, create one if no filename was given, strip off any filename directory component, and create a File object for the attachment (the example stores the file in a local attach directory):

private static void writeFile(String fileName, InputStream in)
                              throws IOException
{
     if (fileName == null)
        fileName = "attachment.dat";
    File file = new File(fileName);
    file = new File("attach",file.getName());

The example code checks to make sure it doesn't overwrite an existing file by generating a unique name (another approach is to prompt the user to confirm overwriting the file):

String rootName = file.getName();
for (int i=0; file.exists(); i++) {
   file = new File(file.getParent(),""+i+"_"+rootName);
}
System.out.println("Saving attachment in: "+ file.getAbsolutePath());

The final task is to save the data:

    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
    BufferedInputStream bis = new BufferedInputStream(in);
    byte[] buffer = new byte[2048];
    int n;
    while ((n=bis.read(buffer,0,buffer.length)) > 0) {
       bos.write(buffer,0,n);
    }
    bos.flush();    bos.close();    bis.close();
}

The InputStream object for reading the attachment is provided by a javax.activation.DataHandler object. This DataHandler understands the MIME encoding used in the message and reconstructs the original data for the InputStream. The client code is unaware that the attachment is MIME encoded.

Listing 11.9 shows the complete code listing for the RetrieveAttachment application.

Listing 11.9. RetrieveAttachment.java Full Listing
import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.*;

public class RetrieveAttachment {
    public static void main(String[] args)
    {
        if (args.length!=3) {
        System.out.println("Usage: RetrieveAttachment host username password");
            System.exit(1);
        }

        String host = args[0];
        String username = args[1];
        String password = args[2];

        try {
            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);
            Store store = session.getStore("pop3");
            store.connect(host, username, password);
            Folder folder = store.getFolder("INBOX");
            folder.open(Folder.READ_ONLY);
            Message messages[] = folder.getMessages();
            for (int i=0; i<messages.length; i++) {
                System.out.println(messages[i].getFrom()[0] + "	" +
                     messages[i].getSubject());
                Object obj = messages[i].getContent();
                if (obj instanceof Multipart)
                    processMessage((Multipart)obj);
            }

            folder.close(false);
            store.close();
        }
        catch (MessagingException me) {
             System.err.println(me);
        }
        catch (IOException ioe) {
            System.err.println(ioe);
        }
    }

    private static void processMessage(Multipart multipart)
                                       throws MessagingException, IOException
    {
        for (int i=0; i<multipart.getCount(); i++) {
            Part part = multipart.getBodyPart(i);
            String disposition = part.getDisposition();
            if (disposition != null &&
                disposition.equals(Part.ATTACHMENT))
                writeFile(part.getFileName(), part.getInputStream());
        }
    }

    private static void writeFile(String fileName, InputStream in)
                                  throws IOException
    {
        if (fileName == null)
            fileName = "attachment.dat";
        File file = new File(fileName);
        file = new File("attach",file.getName());

        String rootName = file.getName();
        for (int i=0; file.exists(); i++) {
            file = new File(file.getParent(),""+i+"_"+rootName);
        }
        System.out.println("Saving attachment in: "+ file.getAbsolutePath());

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
        BufferedInputStream bis = new BufferedInputStream(in);
        byte[] buffer = new byte[2048];
        int n;
        while ((n=bis.read(buffer,0,buffer.length)) > 0) {
            bos.write(buffer,0,n);
        }
        bos.flush();
        bos.close();
        bis.close();
    }
}

Run this example using the command

asant RetriveAttachment

and supply the hostname, username and password when prompted (locahost, juliet and juliet).

You will of course, need to use the SendAttachmentMail example to first send an email containing an attachment.

Before you look at using JavaMail within the J2EE environment it is worth studying the JavaMail support for authenticated email servers.

Authenticating Users and Security

The JavaMail API javax.mail package provides an Authenticator class that allows access to protected resources, such as a mailbox. You will need to use an Authenticator object if you do not pass the username and password to the Store.connect() method or if you are working with an authenticating SMTP server.

Using an Authenticator object requires you to define your own Authenticator sub-class and then pass an instance of this sub-class to the Session.getDefaultInstance() method as follows:

Properties props = new Properties();
props.put("mail.smtp.host","localhost");
props.put("mail.pop3.host","localhost");
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);

In this example the SMTP host and POP3 host are both defined as properties so that you can see where to supply your own email server host names.

An Authenticator subclass must define the single method getPasswordAuthentication() which returns a PasswordAuthentication object. This object is simply a container for the authentication information and has a constructor that accepts a username and a password. The MyAuthenticator example, shown below, simply prompts the user for his or her username and password:

class MyAuthenticator extends Authenticator {
  public PasswordAuthentication getPasswordAuthentication() {
  String username=null;
  String password=null;
  try {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    System.out.print("Username? ");
    username=in.readLine();
    System.out.print("Password? ");
    password=in.readLine();
  }
  catch (IOException ioe) {
    System.err.println(ioe);
  }
  return new PasswordAuthentication(username,password);
  }
}

NOTE

As mentioned previously, the JavaMail API does not directly support secure mail protocols, but some third-parties do provide this support. As such, it is important to remember that the PasswordAuthentication object acts only as a container for credential information, and it does not perform any form of encryption on the information.


If you want to read email using the Authenticator use the Store.Connect() method with no parameters as follows:

Store store = session.getStore("pop3");
store.connect();

The full version of this example is supplied in the Day 11 example code on the accompanying Web site as the AuthenticateRetrieveMail.java program.

The final task in today's study of JavaMail is to look at extending the Agency case study to use JavaMail.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.107.94