Constructing and Sending MIME Messages

In the previous section, all the e-mails we created were simple, plain text e-mails with no fancy extras like HTML formatting, embedded images, or attachments. This might be okay for some applications where the e-mail is simply for information purposes or is used entirely within an organization, but often applications require the ability to send much more complex e-mail messages. The JavaMail implementation of MailSender, or more accurately of JavaMailSender, provides the ability to send MIME (Multipart Internet Message Encoding) messages, allowing for messages in HTML format, messages with embedded images, and messages with attachments.

In this section, we consider five separate scenarios for sending complex MIME messages:

  • Sending a message with HTML content

  • Sending an HTML message with embedded images

  • Sending a message with attachments

  • Sending an HTML message with a plain text alternative

  • Sending a MIME message with a complex structure

All of these examples rely on features in the JavaMailSenderImpl class and as a result, they do not work with CosMailSenderImpl. A major drawback of sending MIME messages is that you cannot use the SimpleMailMessage class for external configuration because the MIME-aware methods on JavaMailSenderImpl do not work with this class. For this reason, it is often better to create a class to encapsulate your MIME message that Spring can configure externally. For the examples in this section, we define a common base class that allows for external configuration of the messages, as shown in Listing 15-8.

Listing 15-8: The AbstractMessageSender Class

image from book
package com.apress.prospring.ch15.mime;      import org.springframework.mail.javamail.JavaMailSender;      public abstract class AbstractMessageSender {          protected String to;     protected String from;     protected String subject;     protected JavaMailSender sender;              public void setTo(String to) {         this.to = to;     }          public void setFrom(String from) {         this.from = from;     }          public void setSubject(String subject) {         this.subject = subject;     }          public void setJavaMailSender(JavaMailSender sender) {         this.sender = sender;     } }
image from book

The idea behind this class is that each of our examples inherits from it and can then be configured in the Spring configuration file. Also, all examples in this section require an instance of JavaMailSender, which is set via the javaMailSender property exposed by the AbstractMessageSender. To configure this JavaMailSender instance, we use a shared configuration file (shown in Listing 15-9) across all of the examples.

Listing 15-9: The Shared Configuration File

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean  >         <property name="host">             <value>localhost</value>         </property>     </bean>     </beans>
image from book

Here you can see that we configured a sender bean, of type JavaMailSenderImpl, and we configured the host property for our environment. You may need to modify the host for these examples to work in your environment.

In addition to the shared configuration file, each example uses a custom configuration such as that shown in Listing 15-10.

Listing 15-10: The Example-Specific Configuration File

image from book
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd"> <beans>     <bean               >         <property name="javaMailSender">             <ref bean="sender"/>         </property>         <property name="to">             <value>robh@cakesolutions.net</value>         </property>         <property name="from">             <value>mail@apress.com</value>         </property>         <property name="subject">             <value>Alternative Formats</value>         </property>     </bean>     </beans>
image from book

The only thing that changes in each of these configuration files is the class attribute of the messageSender bean. For this reason, we do not show each of these configurations in the text, but you can still find them in the code download.

Sending a Basic HTML E-Mail

An HTML message is created in a similar manner to plain text e-mail, but you must be sure to specify the MIME type of the message that you are creating—in this case, text/html. Although you can't use the SimpleMailMessage class when working with MIME messages, Spring provides the MimeMessageHelper class that allows you to work with MIME messages in a similar manner to the SimpleMailMessage class and to avoid some of the complexities of working with the JavaMail API directly.

In Listing 15-11, you can see the SimpleHtmlMessageSender class that builds and sends a simple HTML mail.

Listing 15-11: The SimpleHtmlMessageSender Class

image from book
package com.apress.prospring.ch15.mime;      import javax.mail.MessagingException; import javax.mail.internet.MimeMessage;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.mail.javamail.MimeMessageHelper;      public class SimpleHtmlMessageSender extends AbstractMessageSender {          public void sendMessage() throws MessagingException {         MimeMessage msg = sender.createMimeMessage();         MimeMessageHelper helper = new MimeMessageHelper(msg);              helper.setTo(to);         helper.setFrom(from);         helper.setSubject(subject);         helper.setText("<html><head></head><body><h1>Hello World!"                 + "</h1></body></html>", true);              sender.send(msg);     }          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 new String[] { "./ch15/src/conf/simpleHtmlMessageSender.xml",                         "./ch15/src/conf/javaMailSender.xml" });              SimpleHtmlMessageSender sender = (SimpleHtmlMessageSender) ctx                 .getBean("messageSender");         sender.sendMessage();     } } 
image from book

Much of this code is fairly self-explanatory, but there are a few points of note. First, notice that an instance of MimeMessage is retrieved by calling the createMimeMessage() method of the JavaMailSender interface. Second, this instance is wrapped in an instance of MimeMessageHelper and we use this to set the various parameters. The final point of note in this example is that in the call to MimeMessageHelper.setText(), we pass in true as the second argument to flag that this message is an HTML format message. Once the message is assembled, it is sent with a call to JavaMailSender.send().

Figure 15-4 shows the results of this code rendered in our e-mail client.

image from book
Figure 15-4: The HTML mail message

Sending an HTML Mail with Embedded Images

In the previous example, you saw how to assemble and send an HTML format e-mail. In this section, you see how to add embedded images to your mail and display these in the HTML body content.

Note 

In general, we have found that it is better to avoid embedded images and to use images that are accessed via a standard URL in your e-mails. Many e-mail clients have trouble dealing with embedded images especially when they form part of more complex MIME structures like the one shown later in the section entitled "Sending Complex MIME Messages."

In order to send an HTML e-mail that uses embedded images, you have to assemble the HTML message first and then add the images as additional parts to the MIME message. When adding a MIME part, you can associate a Content-ID with it; a Content-ID is used if you are referring to the image from the HTML. When using MimeMessageHelper, Spring takes care of much of the logic behind this process for you.

Listing 15-12 shows the InlineImageMessageSender class that demonstrates how to build a message with inline images.

Listing 15-12: The InlineImageMessageSender Class

image from book
package com.apress.prospring.ch15.mime;      import java.io.File;      import javax.mail.MessagingException; import javax.mail.internet.MimeMessage;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.FileSystemResource; import org.springframework.mail.javamail.MimeMessageHelper;      public class InlineImageMessageSender extends AbstractMessageSender {          public void sendMessage() throws MessagingException {         MimeMessage msg = sender.createMimeMessage();         MimeMessageHelper helper = new MimeMessageHelper(msg, true);              helper.setTo(to);         helper.setFrom(from);         helper.setSubject(subject);              helper.setText("<html><head></head><body><h1>Hello World!</h1>"                 + "<img src=\"cid:abc\"></body></html>", true);              // add the image         FileSystemResource img = new FileSystemResource(new File(                 "./ch15/src/images/apress.gif"));         helper.addInline("abc", img);              sender.send(msg);     }          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 new String[] { "./ch15/src/conf/inlineImageMessageSender.xml",                         "./ch15/src/conf/javaMailSender.xml" });              InlineImageMessageSender sender = (InlineImageMessageSender) ctx                 .getBean("messageSender");         sender.sendMessage();     } }
image from book

This example is very similar to the previous example, but there are three noticeable differences in the sendMessage() method.

First, in the HTML that is added to the message body, there is an <img> tag whose src attribute is set to cid:abc. This tells the mail client to use the embedded resource with Content-ID abc when rendering this image.

Second, you can see that the image resource itself is embedded in the message using the MimeMessageHelper.addInline() method. When you are adding inline resources, it is important that you add the message body first and the resources second; otherwise, the message does not render correctly in the mail client. As of version 1.1, the Spring manual reports, incorrectly, that you must add inline resources first, followed by the message body. This is corrected in subsequent releases, but be aware that older releases still have this error in the documentation.

Finally, you will notice that when constructing the instance of MimeMessageHelper, we passed in a second, boolean, argument. This argument specifies whether or not the MimeMessageHelper should work in multipart mode, which is required to add attachments to a message.

Figure 15-5 shows the result of running this example rendered in a mail client.

image from book
Figure 15-5: Displaying a message with embedded images

As you can see from the figure, the embedded image is rendered as part of the message body. Using this approach for including images in your messages means that you avoid needing to store images on a server that is accessible by the recipients; however, many mail clients do not handle embedded images well when they are combined with other MIME features such as alternative message formats.

Sending a Message with Attachments

Sending attachments with a message is very much like including embedded resources; in fact, behind the scenes, both features create additional MIME parts in your message. The main difference is that embedded resources have their content disposition set to inline, indicating that they should not be considered as attachments.

Listing 15-13 shows a slight modification to the previous example—instead of embedding, the image attaches to the message.

Listing 15-13: Sending Attachments

image from book
package com.apress.prospring.ch15.mime;      import java.io.File;      import javax.mail.MessagingException; import javax.mail.internet.MimeMessage;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.FileSystemResource; import org.springframework.mail.javamail.MimeMessageHelper;      public class AttachmentMessageSender extends AbstractMessageSender {          public void sendMessage() throws MessagingException {         MimeMessage msg = sender.createMimeMessage();         MimeMessageHelper helper = new MimeMessageHelper(msg, true);              helper.setTo(to);         helper.setFrom(from);         helper.setSubject(subject);              helper.setText(                 "<html><head></head><body><h1>Hello World!</h1></body></html>",                 true);              // add the image         FileSystemResource img = new FileSystemResource(new File(                 "./ch15/src/images/apress.gif"));         helper.addAttachment("apress.gif", img);              sender.send(msg);     }          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 new String[] { "./ch15/src/conf/attachmentMessageSender.xml",                         "./ch15/src/conf/javaMailSender.xml" });              AttachmentMessageSender sender = (AttachmentMessageSender) ctx                 .getBean("messageSender");         sender.sendMessage();     } } 
image from book

As we mentioned, this example is very similar to the last. The only important difference here is that we have swapped the call to addInline() with a call to addAttachment(). Because attachments are included in the mail in the same way as embedded resources, they are also given a Content-ID, which most mail clients recognize as the file name. Figure 15-6 shows this message in the mail client; notice the file name that is displayed for the attachment.

image from book
Figure 15-6: Message with an attachment

Sending an HTML Mail Message with Plain Text Alternative

In the examples so far, there has been very little direct interaction with the JavaMail APIs, and we have been able to rely on Spring helper classes when assembling the MIME messages. However, in some cases, the complexity of the MIME message structure requires you to interact directly with the JavaMail API. This example and the one that follows are examples of this.

When you need more control over how a MIME message is assembled, you can create an implementation of the MimeMessagePreparator callback interface and pass this to the JavaMailSender.send() method in place of the MimeMessage. The main reason for doing this rather than attempting to assemble the MimeMessage directly is that JavaMail throws a lot of checked exceptions from which it is impossible to recover. Spring takes care of wrapping these checked exceptions in runtime exceptions, thus reducing the complexity of your code. You should note that you can still use the MimeMessageHelper class when assembling your own MimeMessages, but we choose not do so here to illustrate how to interact directly with JavaMail.

In this example you see how to assemble an HTML message with a plain text alternative. When rendered in a mail client that either doesn't support HTML or has had HTML disabled, the plain text alternative contained in the message is used in place of the HTML. This is like sending two copies of the same letter, each in a different language, in the same envelope. In general, both the HTML and plain text message parts contain the same message, albeit in different formats. In this example, we are going to make the plain text message different from the HTML one to illustrate how this works.

The code for this example is quite complex, so we explain it in pieces. Listing 15-14 shows the basic class that obtains the required beans from Spring and then sends the message with a call to JavaMailSender.send().

Listing 15-14: The AlternativeFormatMessageSender Class

image from book
package com.apress.prospring.ch15.mime;      import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.mail.javamail.MimeMessagePreparator;      public class AlternativeFormatMessageSender extends AbstractMessageSender {          public void sendMessage() {         sender.send(new MessagePreparator());     }          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 new String[] {                         "./ch15/src/conf/alternativeFormatMessageSender.xml",                         "./ch15/src/conf/javaMailSender.xml" });              AlternativeFormatMessageSender sender =                (AlternativeFormatMessageSender) ctx.getBean("messageSender");         sender.sendMessage();     }
image from book

Much of this code will be familiar to you by now, but notice that the call to send passes in an instance of the MessagePreparator class instead of an instance of MimeMessage. The MessagePreparator class is an inner class that implements the MimeMessagePreparator interface and it is responsible for actually constructing the MimeMessage object. Note that Spring creates the MimeMessage instance and passes it to the MimeMessagePreparator.prepare() method. The MessagePreparator is only responsible for configuring this object; it does not need to create the MimeMessage itself.

Listing 15-15 shows the MimeMessagePreparator class.

Listing 15-15: The MimeMessagePreparator Class

image from book
    private class MessagePreparator implements MimeMessagePreparator {              public void prepare(MimeMessage msg) throws Exception {                  // set header details             msg.addFrom(InternetAddress.parse(from));             msg.addRecipients(Message.RecipientType.TO, InternetAddress                     .parse(to));             msg.setSubject(subject);                  // create wrapper multipart/alternative part             MimeMultipart ma = new MimeMultipart("alternative");             msg.setContent(ma);                  // create the plain text             BodyPart plainText = new MimeBodyPart();             plainText.setText("This is the plain text version of the mail.");             ma.addBodyPart(plainText);                  //  create the html part             BodyPart html = new MimeBodyPart();             html.setContent(                "<html><head></head><body><h1>This is the HTML version of the mail."                             + "</h1></body></html>", "text/html");             ma.addBodyPart(html);         }     } }
image from book

Here the prepare() method starts by setting the sender and recipient addresses along with the subject. Notice that when setting the addresses of the sender and recipient, we have to use the InternetAddress.parse() method to create an instance of InternetAddress from a String. This is one of the details that are hidden by the MimeMessageHelper class, and it is often preferable to use MimeMessageHelper for configuring message properties, even when assembling the message directly.

Next, we create an instance of MimeMultipart, a wrapper class to hold multiple message parts, and we specify the MIME type as multipart/alternative, signifying that the parts contained in this multipart are alternative formats of the same data. This MimeMultipart instance is specified as the content of the MimeMessage instance using the MimeMessage.setContent() method.

Next, we create two instances of MimeBodyPart: one for the plain text body and one for the HTML body. When assembling the plain text BodyPart, it is enough simply to call the setText() method passing in the part content. This sets the MIME type of the BodyPart to text/plain. For the HTML BodyPart, we use the setContent() method in place of setText() and we explicitly pass in the MIME type of text/html as the second argument. Both of these BodyParts are then added to the MimeMultipart instance and as a result, they are added to the MimeMessage. The order in which you add BodyParts to the MimeMultipart is important, and you should add the BodyPart with the most preferable message format last.

The result of this code is to populate a MimeMessage instance so that the top-level MIME type is multipart/alternative and within the message there are two parts: one with the MIME type text/plain and the other with the MIME type text/html.

Figure 15-7 shows this message rendered as HTML in the mail client and Figure 15-8 shows it rendered as plain text.

image from book
Figure 15-7: The HTML message

image from book
Figure 15-8: The plain text message

Most mail clients allow you to select which format you want to view messages in. We have found that Mozilla Thunderbird is very useful during testing because it allows you to quickly swap between the different message formats. Another useful tool for testing is the Pegasus mail client for Microsoft Windows that allows you to view the individual parts of the message as a graphical tree representation. You can download Pegasus from www.pmail.com. Figure 15-9 shows the tree representation of this message rendered in Pegasus.

image from book
Figure 15-9: Checking the message structure with Pegasus

Sending Complex MIME Messages

So far you have seen how to send a variety of different MIME messages, including HTML messages with embedded images and messages with both HTML and plain text content, but how can you send a message that has HTML content with embedded images and a plain text alternative? In this section, we show you how.

The code required to send an HTML message with both embedded images and a plain text alternative is not that different from the code required to send the HTML message with the plain text alternative. The main change is that we need to wrap the HTML content along with the embedded images inside an additional multipart wrapper. This wrapper groups the HTML together with the embedded images and instructs the mail client that HTML together with the images forms the second message format, not just the HTML on its own. Listing 15-16 shows the code you need to do this.

Listing 15-16: Assembling Complex MIME Messages

image from book
package com.apress.prospring.ch15.mime;      import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.mail.javamail.MimeMessagePreparator;      public class ComplexMessageSender extends AbstractMessageSender {          public void sendMessage() throws MessagingException {         sender.send(new MessagePreparator());     }          public static void main(String[] args) throws Exception {         ApplicationContext ctx = new FileSystemXmlApplicationContext(                 new String[] {                         "./ch15/src/conf/complexMessageSender.xml",                         "./ch15/src/conf/javaMailSender.xml" });              ComplexMessageSender sender = (ComplexMessageSender) ctx                 .getBean("messageSender");         sender.sendMessage();     }          private class MessagePreparator implements MimeMessagePreparator {              public void prepare(MimeMessage msg) throws Exception {                  // set header details             msg.addFrom(InternetAddress.parse(from));             msg.addRecipients(Message.RecipientType.TO, InternetAddress                     .parse(to));             msg.setSubject(subject);                  // create wrapper multipart/alternative part             MimeMultipart ma = new MimeMultipart("alternative");             msg.setContent(ma);                  // create the plain text             BodyPart plainText = new MimeBodyPart();             plainText.setText("This is the plain text version of the mail.");             ma.addBodyPart(plainText);                  //  create the html and image multipart wrapper             BodyPart related = new MimeBodyPart();             MimeMultipart mr = new MimeMultipart("related");             related.setContent(mr);             ma.addBodyPart(related);                          BodyPart html = new MimeBodyPart();             html.setContent(                "<html><head></head><body><h1>This is the HTML version of the mail."                        + "</h1><img src=\"cid:0001\"></body></html>", "text/html");             mr.addBodyPart(html);                          BodyPart img = new MimeBodyPart();             img.setHeader("Content-ID", "0001");             img.setDisposition("inline");             img.setDataHandler(new DataHandler(                              new FileDataSource("./ch15/src/images/apress.gif")));             mr.addBodyPart(img);         }     } }
image from book

The important piece of this code is the prepare() method of the MessagePreparator class. Up to and including the code that adds the plain text BodyPart, the prepare() method is identical to the one in the last example. When it comes to the HTML format message, we start by creating a BodyPart and a second MimeMultipart instance. This MimeMultipart instance is given the MIME type multipart/related, indicating that the BodyParts it contains are related and, for the purposes of the enclosing MimeMultipart, should be treated as one. The MimeMultipart of type multipart/related is then set as the content for a BodyPart and this BodyPart is added to the top-level multipart/alternative MimeMultipart instance. The reason we create a BodyPart in this case is that you cannot add one MimeMultipart to another without first wrapping it in a BodyPart—essentially you are saying that a part of the top-level multipart is made up of another multipart.

Next we add the HTML BodyPart to the multipart/related MimeMultipart followed by the embedded image. In general, it is much easier to use the MimeMessageHelper class to add embedded resources to a MimeMultipart, but we wanted to show how to do it manually. The first two steps required when adding an embedded, inline resource are to set the Content-ID header of the multipart and to set the content disposition to inline. Once this is done, you can supply the content of the actual BodyPart using the setDataHandler() method. The setDataHandler() method requires an instance of javax.activation.DataHandler, which in turn requires

an instance of a class that implements javax.activation.DataSource—we use the

javax.activation.FileDataSource class. JavaMail and the JavaBeans Activation Framework take care of encoding the resource data for inclusion in the message as well as identifying the correct MIME type for the data and including this in the BodyPart.

In Figure 15-10, you can see the plain text version of this message and Figure 15-11 shows the HTML version with the embedded image.

image from book
Figure 15-10: The plain text message

image from book
Figure 15-11: The HTML message

Notice that in the plain text view, the inline image is treated as an attachment even though we set the content disposition to inline. This is just one example of how different mail clients treat the mail structure differently. Again, you can use Pegasus to view the structure of the message in graphical form as shown in Figure 15-12.

image from book
Figure 15-12: Checking the message structure in Pegasus

Here you can see that the plain text mail sits in the second level of the tree along with a multipart section containing both the HTML and GIF image in the third level of the tree. Interestingly, when Pegasus is configured to ignore the HTML part of the message, it displays the plain text part without showing the GIF image as an attachment.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

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