C H A P T E R  21

image

E-mail

E-mail notification is part of today's enterprise systems. Java enables e-mail notification by offering its Mail API. Using this API you can send e-mail communications in response to an event (say a completed form or a finalized script). You can also use the Email API to check an IMAP or POP3 mailbox. In all, sending and receiving e-mail using the Email API has become pretty straightforward.

For the recipes in this chapter, please make sure that you have set up your firewall to allow e-mail communication; most of the time, firewalls will allow outbound communications to e-mail servers without an issue, but if you are running your own local SMTP (e-mail) server, you may need to configure your firewall to allow the e-mail server to operate correctly.

image Note If you are using the J2EE JDK, the JavaMail API is included as part of the EE download. If you are using the J2SE, you will need to download and install the JavaMail API.

21-1. Installing JavaMail

Problem

You want to install JavaMail for use by your application in sending e-mail notifications.

Solution

Download JavaMail from the JavaMail web site. Currently, the download you need is found at http://www.oracle.com/technetwork/java/javamail/.

Once downloaded, unzip and add the JavaMail .jar files as dependencies from your project (both mail.jar and lib*.jar).

How It Works

The JavaMail API is included in the J2EE SDK, but if you are working with the J2SE SDK you will need to download and add the JavaMail API to your J2SE project. By downloading and adding the dependencies, you get access to the robust Email API that allows you to send and receive e-mails.

21-2. Sending an E-mail

Problem

You need your application to send an e-mail.

Solution

Using the Transport() methods, you can send an e-mail to specific recipients. In this solution, an e-mail message is constructed and sent through the smtp.somewhere.com server:

    private void start() {
        Properties properties = new Properties();
        properties.put("mail.smtp.host", "smtp.somewhere.com");
        properties.put("mail.smtp.auth", "true");

        Session session = Session.getDefaultInstance(properties, new
MessageAuthenticator("username","password"));

        Message message = new MimeMessage(session);
        try {
            message.setFrom(new InternetAddress("[email protected]"));
            message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));
            message.setSubject("Subject");
            message.setContent("This is a test message", "text/plain");
            Transport.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    class MessageAuthenticator extends Authenticator {
        PasswordAuthentication authentication = null;

        public MessageAuthenticator(String username, String password) {
            authentication = new PasswordAuthentication(username,password);
        }

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return authentication;
        }
    }

How It Works

The JavaMail API starts by creating a Properties object that works as a standard Map object (in fact, it inherits from it), in which you put the different properties that might be needed by the JavaMail service. In this example, you put the hostname ("mail.smtp.host” property) and that the host requires authentication ("mail.smtp.auth", “true"). After the properties object is configured, you fetch a javax.mail.Session that will hold the connection information for the e-mail message.

When creating a Session, you can specify the login information if the service requires authentication. This might be necessary when connecting to an SMTP service that is outside of your local area network. To specify the login information, you have to create an Authenticator object, which will contain the getPasswordAuthentication() method. In this example, there is a new class called MessageAuthenticator, which extends the Authenticator class. By making the getPasswordAuthentication() method return a PasswordAuthentication object, you can specify the username/password used for the SMTP service.

The Message object represents an actual e-mail message and exposes e-mail properties such as From/To/Subject and Content properties. After setting these properties, you call the Transport.send() static method to send the e-mail message.

imageTip If you don't need authentication information, you can call the Session.getDefaultInstance(properties, null) method, passing a null for the Authenticator parameter.

21-3. Attaching a File to an E-mail Message

Problem

You need to attach a file to an e-mail message.

Solution

Creating a message that contains different parts (a multipart message) is what allows you to send attachments such as files and images. You can specify the body of the e-mail message and also specify an attachment. Messages that contain different parts are referred to as Multipurpose Internet Mail Extensions (MIME) messages. They are represented in the javax.mail API by the MimeMessage class. The following code creates such a message:

Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject("Subject");

// Create Mime “Message” part
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent("This is a test message", "text/plain");

// Create Mime “File” part
MimeBodyPart fileBodyPart = new MimeBodyPart();
fileBodyPart.attachFile("attach.txt");

// Put Parts together in a Multipart Message
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
multipart.addBodyPart(fileBodyPart);

// Set the content of the message to be the MultiPart
message.setContent(multipart);
Transport.send(message);

How It Works

Within the JavaMail API you can create a Multipurpose Internet Mail Extensions (MIME) e-mail. This type of Message allows it to have different body parts. In the example, you create a plain text part (which contains the text that the e-mail displays), and an attachment part, which holds the attachment you are trying to send. Depending on the type of attachment, the Java API will choose an appropriate encoding for the attachment part. Once these two parts are created, they are combined by creating a MultiPart object, and adding each individual part (the plain text and the attachment) to it. Once the MultiPart object has all the parts, it is assigned as the content of the MimeMessage and sent (just like recipe 21-2).

21-4. Sending E-mail to a Group

Problem

You want to send the same e-mail to multiple recipients.

Solution

Use the setRecipients() method from the Mail API to send e-mail to multiple recipients. The setRecipients() method allows for specifying more than one recipient at a time. For example:

// Main send body
    message.setFrom(new InternetAddress("[email protected]"));
    message.setRecipients(Message.RecipientType.TO, getRecipients(emails));
    message.setSubject("Subject");
    message.setContent("This is a test message", "text/plain");
    Transport.send(message);

// ------------------


    private Address[] getRecipients(List<String> emails) throws AddressException {
        Address[] addresses = new Address[emails.size()];
        for (int i =0;i < emails.size();i++) {
            addresses[i] = new InternetAddress(emails.get(i));
        }
        return addresses;
    }

How It Works

By using the setRecipients() method of the Message object, you can specify multiple recipients on the same message. The setRecipients() method accepts an array of Address objects. In this recipe, because you have a collection of Strings, you create the array as the size of the collection and create InternetAddress objects to fill the array with. Sending e-mails using multiple e-mail addresses (as opposed to individual e-mails) is much more efficient because only one message is sent from your client to the target mail servers. Each target mail server will then deliver to all recipients that it has mailboxes for. For example, if sending to five different yahoo.com accounts, the yahoo.com mail server will only need to receive one copy of the message and it will deliver it to all the yahoo.com recipients specified in the message.

images Tip If you want to send bulk messages, you might want to specify the Recipient Type as BCC, so that the e-mail received doesn’t show everyone else that might have gotten the e-mail. To do so, specify Message.RecipientType.BCC in the setRecipients() method.

21-5. Checking E-mail

Problem

You need to check if a new e-mail has arrived in an e-mail account.

Solution

Use javax.mail.Store to connect, query, and retrieve messages from an Internet Message Access Protocol (IMAP) e-mail account. For example, the following code connects to an IMAP account, retrieves the last five messages from that IMAP account, and marks those five messages as “read”.

Session session = Session.getDefaultInstance(properties, null);
Store store = session.getStore("imaps");
    store.connect(host,username,password);
    System.out.println(store);
    Folder inbox = store.getFolder(folder);
    inbox.open(Folder.READ_WRITE);
    int messageCount = inbox.getMessageCount();
    int startMessage = messageCount - 5;
    int endMessage = messageCount;
    if (messageCount < 5) startMessage =0;
    Message messages[]  = inbox.getMessages(startMessage,endMessage);
    for (Message message : messages) {
boolean hasBeenRead = false;
for (Flags.Flag flag : message.getFlags().getSystemFlags()) {
if (flag == Flags.Flag.SEEN) {
hasBeenRead = true;
break;
    }
}
message.setFlag(Flags.Flag.SEEN, true);
System.out.println(message.getSubject() + " "+ (hasBeenRead? "(read)" : "") +
message.getContent());

    }
    inbox.close(true);

How It Works

A Store object allows you to access e-mail mailbox information. By creating a store and then requesting the “Inbox” folder, you have access to the messages in the main mailbox of your IMAP account. With the folder object, you can request to download the messages from the inbox. To do so, you use the getMessages (start, end) method. The inbox also provides a getMessageCount() method, which allows you to know how many e-mails are in the inbox. Keep in mind that the messages start at index 1.

Each message will have a set of flags that can then tell whether the message has been read (Flags.Flag.SEEN) or whether the message has been replied to (Flags.Flag.ANSWERED). By parsing the SEEN flag, you can then process messages that haven’t been seen before.

To set a message as being seen (or answered), you can call the message.setFlag() method. This method allows you to set (or reset) e-mail flags. If setting message flags, you need to open the folder as READ_WRITE, which allows you to make changes to e-mail flags; and you will need to call inbox.close(true) at the end of your code, which will tell the JavaMail API to flush the changes to the IMAP store.

images Tip  For IMAP over SSL, you would want to use session.getStore("imaps"). This creates a secure IMAP store.

21-6 Monitoring an E-mail Account

Problem

You want to monitor when e-mails arrive at a certain account and want to process them depending on their content.

Solution

Begin with recipe 21-5. Then add IMAP flag manipulation to create a robust e-mail monitor for your application. The following example checks the subject of new messages and deals with them appropriately. The example uses message flags to delete processed messages so they need not be read twice. Messages that can’t be processed are marked as read but left in the server for troubleshooting by a human.

private void checkForMail() {
        System.out.println("Checking for mail");
        Properties properties = new Properties();
        String username = "username";
        String password = "password";
        String folder = "Inbox";
        String host = "imap.server.com";

        try {
            Session session = Session.getDefaultInstance(properties, null);
            Store store = session.getStore("imaps");
            store.connect(host,username,password);
Folder inbox = store.getFolder(folder);
            inbox.open(Folder.READ_WRITE);
int messageCount = inbox.getMessageCount();
            Message messages[]  = inbox.getMessages(1,messageCount);
            for (Message message : messages) {
                boolean hasBeenRead = false;
                if (Arrays.asList(message.getFlags().getSystemFlags()).contains(Flags.Flag.SEEN)) {
                    continue;                     // not interested in "seen" messages
                }
                if (processMessage(message)) {
                    System.out.println("Processed :"+message.getSubject());
                    message.setFlag(Flags.Flag.DELETED, true);
                } else {
                    System.out.println("Couldn't Understand :"+message.getSubject());
                    // set it as seen, but keep it around
                    message.setFlag(Flags.Flag.SEEN, true);
                }
            }
            inbox.close(true);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

    private boolean processMessage(Message message) throws MessagingException {
        boolean result = false;

        String subject = message.getSubject().toLowerCase();
        if (subject.startsWith("subscribe")) {
            String emailAddress = extractAddress (message.getFrom());
            if (emailAddress != null) {
                subscribeToList(emailAddress);
                result = true;
            }

        } else if (subject.startsWith("unsubscribe")) {
            String emailAddress = extractAddress (message.getFrom());
            if (emailAddress != null) {
                unSubscribeToList(emailAddress);
                result = true;
            }
        }

        return result;
    }


    private String extractAddress(Address[] addressArray) {
        if ((addressArray == null) || (addressArray.length < 1)) return null;
        if (!(addressArray[0] instanceof InternetAddress)) return null;
        InternetAddress internetAddress = (InternetAddress) addressArray[0];
        return internetAddress.getAddress();
    }

How It Works

After connecting to the IMAP server, the example requests all messages received. The code skips over the ones that are marked as SEEN. To do so, the recipe uses the Arrays.AsList to convert the array of system message flags into an ArrayList. Once the list is created, it is a matter of querying the list to see whether it contains the Flag.SEEN enum value. If it’s present, the recipe skips to the next item.

When a message is found that has not been read, the message is then processed by the processMessage() method. The method subscribes or unsubscribes the sender of the message depending on the start of the subject line (This is akin to a mailing list, where sending a message with the subject of “subscribe” adds the sender to the mailing list.)

After determining the command to execute, the code proceeds to extract the sender’s e-mail from the message. To do so, the processMessage() calls the extractEmail() method. Each message contains an array of possible “From” addresses. These Address objects are generic because the Address object can represent Internet or newsgroup addresses. After checking that the Address object is indeed an InternetAddress, you cast the Address object as InternetAddress and call the getAddress() method, which contains the actual e-mail address.

Once the e-mail address is extracted, the recipe calls subscribe or unsubscribe, depending on the subject line. If the message could be understood (meaning that the message was processed), the processMessage() method returns true (if it couldn’t understand the message, it returns false). In the checkForMail() method, when the processMessage() method returns true, the message is flagged for deletion (by calling message.setFlag(Flags.Flag.DELETED, true); otherwise, the message is just flagged as “Seen”. This will allow the message to still be around if it wasn’t understood or deleted if it was processed. Finally, to commit the new flags on the messages (and expunge deleted messages), you need to call the inbox.close(true) method.

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

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