Exploring the JavaMail API

So far, today's lesson has concentrated on sending e-mail messages, but the JavaMail API provides support for other common e-mail operations, such as retrieving messages and attachments and deleting messages on the server. The API provides numerous classes that allow you to perform these types of operations. The remainder of today's lesson gives you an introduction to these operations. For a complete reference to the API's interfaces, classes, and methods, refer to the API documentation.

Retrieving Messages

Earlier in today's lesson, you learned that there are a number of message retrieval protocols, and that most prevalent of these 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 mail box with IMAP is very similar.

Specifically, this application runs from the command line and accepts three parameters—a host, username, and password. You can see the code that accepts these parameters in Listing 11.5 at the end of this section. As with all the applications in today's lesson, you first create an empty Properties object and a Session object:

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

Figure 11.4 shows the remainder of the process for retrieving messages.

Figure 11.4. The message retrieval process.


The first step in the process is to connect to a store on a given host. To do this, you must first create a Store object by calling the Session object's getStore() method. This method accepts one parameter that specifies the retrieval protocol and returns a Store that implements this protocol. After you create a Store, you invoke its connect() method to connect to the remote message store. The connect() method takes three strings as arguments—host, username, and password.

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

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, because it only supports IMAP, SMTP, and POP3. 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 you connect to the message store, you open a message folder. To do this, create a Folder object. The Folder class, like the Store class, is abstract. To create an instance of the class, you invoke the getFolder() method of the Store object. The method takes one parameter, the name of the folder as a string. In this example, the folder name is INBOX:

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

Now you can open the folder using the open() method of the Folder class. The method takes one argument that indicates the mode with which to open the folder. Static fields of the Folder class give the two possible values for the mode, READ_ONLY and READ_WRITE:

folder.open(Folder.READ_ONLY);

To retrieve the messages from the folder, you can either use the Folder's getMessage()method or its getMessages() method. The getMessages() method is overloaded and, as such, there are three versions of this method, each of which returns an array of Message objects. The first version takes an array of ints and returns the messages at the indices the ints specifies. The second version takes a start index and an end index and returns the messages within this range. The final version takes no arguments and returns all the messages within a folder:

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++) {
System.out.println(i + ": " + messages[i].getFrom()[0] + "	" + messages[i].getSubject() +
 "	" + messages[i].getSentDate() + "

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

As you can see, the Message class exposes a number of methods that take the form getITEM(). The API documentation provides a complete list of these methods. The Message class implements the Part interface, and it is from this that it inherits the writeTo() method. This method outputs a byte stream to a specified output stream, which in this example is System.out. It is important to note that the writeTo() method can throw a java.io.IOException that you must catch.

That is the message retrieval process completed, but to complete the code you must close the resources it uses. In this example, you must close the Folder and Store objects. To do this, simply call their close() methods. You must pass the Folder's close() method a single Boolean parameter indicating whether to delete (expunge) 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.

folder.close(false);
store.close();

Listing 11.5 shows the complete code for this application.

Listing 11.5. 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 {
            // Create empty properties object
            Properties props = new Properties();

            // Get a session
            Session session = Session.getDefaultInstance(props, null);

            // Get the store and connect to it
            Store store = session.getStore("pop3");
            store.connect(host, username, password);

            // Get folder and open it
            Folder folder = store.getFolder("INBOX");
            folder.open(Folder.READ_ONLY);

            // Get messages
            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);
            }

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

To run the application, compile it and then issue the following command:

java RetrieveMail mailHost username password

Deleting Messages

In the previous example, you retrieved messages from a mail server without removing them after retrieval. Of course in a real-world situation, you could not leave messages on the mail server indefinitely; it would soon become very crowded! Instead, you would delete messages after retrieving them. To modify the previous example so that messages are deleted after retrieval is a straightforward process.

Previously, you opened the folder in READ_ONLY mode, but 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 Flags.Flag class supplies predefined flags that are accessible as static fields. Table 11.2 shows these flags and describes 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 getPermanentFlags() method of a Folder object, which returns a Flags object that contains all the supported flags.

Table 11.2. Predefined Message Flags
Flag Description
ANSWERED The client has replied to the message.
DELETED The message is marked for deletion.
DRAFT The message is a draft.
FLAGGED The client has flagged the message.
RECENT The message has arrived in the folder since it was last opened.
SEEN The message has been retrieved by the client.
USER Indicates that the folder supports user defined flags.

To mark a message for deletion, you use the setFlag() method of the Message object. The method takes two parameters—a flag and a Boolean indicating the flag's value. For example, to mark a message ready for deletion in the previous example, you add the following line to the end of the for loop:

messages[i].setFlag(Flags.Flag.DELETED, true);

In case you are unsure, the modified for loop would appear as follows:

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);

    // Mark the message for deletion
    messages[i].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 (expunged) when the folder closes:

folder.close(true);

That's it. If you want to view the complete modified listing, you will find it on the CD-ROM accompanying this book.

Getting Attachments

Retrieving an attachment from a message is a more involved process than simply reading a normal message. If you remember from 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 write 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 attached (the part is an attachment). The JavaMail API provides support for this header, as you will soon learn.

The code for this application is based on the RetrieveMail application you wrote earlier. There are two changes to the code. The first is that it will only retrieve the first message on the mail server rather than all the messages. The second is that where it iterated through the messages and performed some basic processing on them (printed parts of the message), it will now process the individual parts of the message. The revised section appears as follows:

Message message = folder.getMessage(1);
    Multipart multipart = (Multipart)message.getContent();
    // Process each part of the message
    for (int i=0; i<multipart.getCount(); i++) {
        processPart(multipart.getBodyPart(i));
    }

In this application, you iterate through the messages and call a processPart() method that you will write shortly. The code passes one parameter to the method, a Part object. The processPart() method checks a body part to determine whether it is an attachment. In addition, it checks a body part that is identified as an attachment to determine if it has a filename. If it doesn't, the method provides a temporary file:

private static void processPart(Part part) throws MessagingException, IOException{
    String disposition = part.getDisposition();
    String fileName = part.getFileName();
    if (disposition.equals(Part.ATTACHMENT)) {
        if (fileName == null) {
            fileName = File.createTempFile("attachment", ".txt").getName();
        }
        writeFile(fileName,part.getInputStream());
    }
    else {
        // It's not an attachment – provide appropriate processing
    }
}
						

The code starts by using the getDisposition() and getFileName() methods, which the Part interface defines, to get the part's disposition and filename. If either of these items does not exist, the methods return null. The code then checks whether the disposition is of the type ATTACHMENT; the other possible value is INLINE. If the disposition is of the type ATTACHMENT, the code checks to see if the body part has an associated filename. If it does not, the code assigns a temporary filename. In both instances, the code calls the writeFile() method that you will write shortly. If the body part does not have a disposition type of ATTACHMENT, the code assumes that the body part should be displayed inline. The premise here is that the disposition will be either INLINE (should display within the message) or null. If the disposition is null, taking into consideration that this is a multi-part message, the message sender most likely wanted the part to be displayed inline.

The writeFile() method writes the content of a body part to a file. The method accepts two parameters—a string filename and an input stream:

private static void writeFile (String fileName, InputStream in) throws IOException {

The method begins by checking that the named file does not already exist; you don't want to overwrite a file! The code that does this simply creates a File object and checks whether it exists. If the file exists, the code alters the filename (through a numeric increment) and then repeats the previous process:

File file = new File(fileName);
for (int i=0; file.exists(); i++) {
    file = new File(fileName+i);
}

After the code identifies a valid filename, it writes the body part's content to the file. You may think that you could use the part's writeTo() method that you used in a previous example. You cannot use this method because it doesn't decode the attachment. Instead, the code uses familiar java.io classes that allow you to copy the body part's input stream onto a file output stream. This approach automatically decodes a variety of encoding formats, including Base-64:

    BufferedOutputStream bos = new BufferedOutputStream(new
FileOutputStream(file));
    BufferedInputStream bis = new BufferedInputStream(in);
    int aByte;
    while ((aByte = bis.read()) != -1) {
        bos.write(aByte);
    }
    bos.flush();
    bos.close();
    bis.close();
}

That's it. You have written the code to identify an attachment and write it to a file. Listing 11.6 shows the complete code listing for the RetrieveAttachment application. To run the application, compile the code and then run it by issuing the following command:

java RetrieveAttachment host username password

Listing 11.6. 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 message = folder.getMessage(1);
            Multipart multipart = (Multipart)message.getContent();

            // Process each part of the message
            for (int i=0; i<multipart.getCount(); i++) {
                processPart(multipart.getBodyPart(i));
            }

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

private static void processPart(Part part) throws MessagingException, IOException{
        String disposition = part.getDisposition();
        String fileName = part.getFileName();

        if (disposition.equals(Part.ATTACHMENT)) {
            // It's an attachment
            if (fileName == null) {
                // the file name is null, so assign a name
                fileName = File.createTempFile("attachment", ".txt").getName();
            }
            // write the part to a file
            writeFile(fileName,part.getInputStream());
        }
        else {
            // the disposition is either INLINE or null
        }
    }

private static void writeFile (String fileName, InputStream in) throws IOException {
        // Do no overwrite existing file
        File file = new File(fileName);
        for (int i=0; file.exists(); i++) {
            file = new File(fileName+i);
        }
        // Write the part to file
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
        BufferedInputStream bis = new BufferedInputStream(in);
        int aByte;
        while ((aByte = bis.read()) != -1) {
            bos.write(aByte);
        }
        bos.flush();
        bos.close();
        bis.close();
    }
}
						

Authenticating Users and Security

The JavaMail API javax.mail package provides an Authenticator class that allows access to protected resources, such as a mail box. If you are familiar with the java.net package, you may be aware of another class with the same name. However, the two classes are different.

This next application is based on the RetrieveMail application you wrote earlier, so this section only walks through the code that differs from that application. You can still find a full listing at the end of this section (Listing 11.7) or on the CD-ROM that accompanies this book. Previously, you passed the host, username, and password to the Store object's connect() method. In contrast, this application will prompt the user for a username and password, and the host is placed as a system property:

Properties props = new Properties();
props.put("mail.pop3.host",host);

You then create a new Authenticator object by using the MyAuthenticator class. This class is a subclass of Authenticator, which itself is abstract. You will write the MyAuthenticator class shortly. After you create an Authenticator object, you pass it together with the Properties object to the getDefaultInstance() method of the Session class:

Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);

After you have a Session object, you create a Store object in the same way you did in the previous applications. You then connect to the store using its connect() method, but in this example, you pass no parameters—previously you passed the host, username, and password. The reason for this is that you have already supplied the information to the Session object.

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

That is the extent of the changes to the application's main class. Now you must write the MyAuthenticator class. This class must define one method, getPasswordAuthentication(), and return a PasswordAuthentication object. This object is simply a container for the authentication information and has just one constructor that accepts two strings—a username and a password. The method body in this example, which you must implement, simply prompts the user for his or her username and password.

Note

As mentioned previously, the JavaMail 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.


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.getMessage());
        }
        return new PasswordAuthentication(username,password);
    }
}
						

That's it. Listing 11.7 shows the complete code for this application. To run it, compile the code and then issue the following command:

java AuthenticateRetrieveMail mail.host

Listing 11.7. AuthenticateRetrieveMail.java Full Listing
import java.util.Properties;
import javax.mail.*;
import javax.mail.internet.*;
import java.io.*;

public class AuthenticateRetrieveMail {
    public static void main(String[] args) {
        if (args.length!=1) {
            System.out.println("Usage: AuthenticateRetrieveMail SMTPHost");
            System.exit(1);
        }

        String host = args[0];

        try {
            Properties props = new Properties();

            //place the authentication info in
            props.put("mail.pop3.host",host);

            // create an empty authenticator object
            Authenticator auth = new MyAuthenticator();

            // Get a session - pass auth object
            Session session = Session.getDefaultInstance(props, auth);

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

            // do not pass any arguments to the connect method
            store.connect();

           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);
            store.close();
        }
        catch (MessagingException me) {
            System.err.println(me.getMessage());
        }
        catch (IOException ioe) {
             System.err.println(ioe.getMessage());
        }
    }
}

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.getMessage());
        }
        return new PasswordAuthentication(username,password);
    }
}
						

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

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