10.2 Sending Email

I l @ ve RuBoard

JavaMail's mechanism for creating new outgoing email messages is quite simple: instantiate a MimeMessage object, add content, and send via a transport. Adding file attachments is only a little more complex: simply create a message part for each component (body text, file attachments, etc.), add them to a multipart container, and add the container to the message. Example 10-1 should act as a quick refresher.

Example 10-1. Multipart mail send
 import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; import java.io.File; import java.util.Properties;  . . .  public class MimeAttach {  . . .    public static void main(String[  ] args) {     try {       Properties props = System.getProperties(  );       props.put("mail.smtp.host", "mail.company.com");       Session session = Session.getDefaultInstance(props, null);        . . .        Message msg = new MimeMessage(session);       msg.setFrom(new InternetAddress("logs@company.com"));       msg.setRecipient(Message.RecipientType.TO,                        new InternetAddress("root@company.com"));       msg.setSubject("Today's Logs");        . . .        Multipart mp = new MimeMultipart(  );       MimeBodyPart mbp1 = new MimeBodyPart(  );       mbp1.setContent("Log file for today is attached.", "text/plain");        . . .        mp.addBodyPart(mbp1);        . . .        File f = new File("/var/logs/today.log");       MimeBodyPart mbp = new MimeBodyPart(  );       mbp.setFileName(f.getName(  ));       mbp.setDataHandler(new DataHandler(new FileDataSource(f)));       mp.addBodyPart(mbp);        . . .        msg.setContent(mp);       Transport.send(msg);     } catch (MessagingException me) {       me.printStackTrace(  );     }   } } 

This example essentially does everything you might want to do when sending a message: it creates a mail session, creates a message, assigns message headers, and creates multipart content.

10.2.1 Use Dynamic Content Strategies

To get the most out of the power and flexibility of Java and JavaMail when building email-based applications, use dynamic content strategies to reduce coding, increase flexibility, and improve user response.

Many JavaMail applications use email as a publishing medium. Most major web sites, whether magazines, newspaper sites, developer communities, weblogs, or retail, provide some degree of email capability. Some of this information is broadcast: newsletter subscriptions, update announcements, and so forth. Other messages are related to transactions: subscription confirmations , order status announcements, receipts, reports , etc. By reaching from the web site into the user's mailbox, well designed, useful messaging support can make existing applications more valuable by increasing the likelihood that they'll actually be used.

JavaMail can be used to send one message to a large number of recipients, but given the other tools available, it really isn't a particularly efficient mechanism for sending spam. Instead, JavaMail (and Java) are ideally suited to dynamically building extremely rich messages targeted at particular users of an application.

Let's look at some examples on the public Internet and the private intranet. Every morning, the New York Times sends me several emails. One of them is a headlines summary, which includes a number of sections that I've indicated interest in. When I registered on the NYT web site, I was able to specify that I wanted all the headlines in national news, politics, technology, and Op-Ed. Because I'm not a New Yorker I didn't ask for the Metro section. I kept the Sports section, but asked for only one headline. I also had the opportunity to specify that I wanted to get my headlines in HTML format rather than plain text.

My daily NYT email is much more complex than the average newsletter. Rather than sending the same piece of content to every subscriber, as most newsletters do, the NYT system looks through the available stories, customizes a set of data according to my preferences, formats it appropriately, and sends it to me. This process is repeated for each subscriber.

Email "push," such as what the New York Times offers, is an incredibly useful tool for all kinds of enterprise applications. Beyond the obvious examples (content sites, retailers sending suggestions to customers, and so on), email reports, such as sales forecasts, transaction summaries, and usage statistics, can be valuable on the intranet as well. Building user acceptance is often one of the biggest challenges IT staffs face when rolling out a new system. Bringing the application to the users, in this case by automatically publishing important information, can save users time and increase the likelihood that the application will be used.

10.2.2 Use XML for Content

Content management across multiple sets of user preferences and multiple kinds of content can be a real chore. Building up the contents of most kinds of email messages in Java is a bad idea: you'll need to recompile for every change. To speed up the process, you can use XML to build the content, and define a set of XSL stylesheets to transform it into plain text or HTML based on the users' preferences. This takes the content and formatting out of the code, and allows redesigns to be implemented by changing the XSL. The XML can be generated by the Java program (which might be the case if the XML input is customized on a per-user basis, such as with receipts or custom newsletters), or it can be generated externally. You can also swap out XSL stylesheets according to user preferences ”for example, this would allow you to easily brand a service to particular clients by associating stylesheets with certain users and feeding the XML content through their customized stylesheet.

Here's a brief example of how XSL can be easily integrated with JavaMail via JAXP and a JAXP-compatible XSLT transformer. I've omitted most of the surrounding code for clarity.

 import javax.xml.transform.*; import javax.xml.transform.stream.*;  . . .  // Class support here. These can be cached.  . . .  TransformerFactory tFactory =                    javax.xml.transform.TransformerFactory.newInstance(  );  . . .  Transformer htmlTF = tFactory.newTransformer             (new StreamSource("contentToHtml.xsl"));  . . .  Transformer textTF = tFactory.newTransformer             (new StreamSource("contentToText.xsl"));  . . .  // Build content, place it in "content.xml", and set useHtml variable to user pref.  . . .  ByteArrayOutputStream bos = new ByteArrayOutputStream(  ); Transformer transformer = (useHtml ? htmlTF : textTF);  . . .  transformer.transform     (new StreamSource("content.xml"),       new StreamResult(bos));  . . .  // Add result to JavaMail message. message.setContent(bos.toString(  ), (useHtml ? "text/html" : "text/plain")); 

10.2.3 Use Templates for Repeated Content

In addition to using XML for content, some applications benefit from using templating engines for content generation. This approach allows easy maintenance of standard email messages (such as notifications, account management messages, and errors) in which the main content remains consistent, but individual details (such as account names , passwords, and confirmations) vary from message to message.

A templating engine takes a plain-text template, replaces placeholding elements with data, and returns the result to the client. The enterprise development world is littered with examples, the most important of which is JavaServer Pages (JSP).

Example 10-2 shows a templated email using the Velocity template system from the Apache Jakarta project (for more information on Velocity, see http://jakarta.apache.org/velocity). Velocity takes this template and replaces the $username and $password lines with the data provided for it.

Example 10-2. newuser.vm
 You have been added to our system! Your login information is:  . . .  Username: $username Password: $password  . . .  Please do not give these out to anyone. For assistance,  please contact customer support at 212-555-2333 

Example 10-3 shows a small Java program that starts Velocity, provides the new username and password values, processes the template, and sends the email. For brevity, I accept the new username and password as read, but you can integrate this code into a Java servlet or other environment to easily incorporate standardized mail messages into your applications.

Example 10-3. MailTemplater.java
 import java.io.*; import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*;  . . .  import org.apache.velocity.*; import org.apache.velocity.app.Velocity;  . . .  public class MailTemplater  {  . . .   public static void main(String[  ] args) throws IOException {  . . .      try {       Properties velocityProperties = new Properties(  );       velocityProperties.setProperty(                   org.apache.velocity.runtime.Runtime.FILE_RESOURCE_LOADER_PATH,                   "c:/");                      Velocity.init(velocityProperties);        . . .        VelocityContext context = new VelocityContext(  );       context.put("username", "billg");       context.put("password", "windows");        . . .        Properties props = System.getProperties(  );       props.put("mail.smtp.host", "mail.attbi.com");       Session session = Session.getDefaultInstance(props, null);        . . .        Message msg = new MimeMessage(session);       msg.setFrom(new InternetAddress("support@company.com"));       msg.setRecipient(Message.RecipientType.TO,                        new InternetAddress("user@client.com"));       msg.setSubject("New User Account");        . . .        StringWriter messageContent = new StringWriter(  );       Template template = Velocity.getTemplate("newuser.vm");       template.merge(context, messageContent);       msg.setText(messageContent.toString(  ));        . . .        Transport.send(msg);            } catch (Exception e) {       e.printStackTrace(  );     }  }  } 

10.2.4 Accommodate Various Client Capabilities

Email messages sent by an application must support the widest variety of email clients possible. While the majority of Internet email users have by now upgraded to mail clients that support HTML content, a sizeable minority have not. You've just seen how to send a simple email with a plain-text body and create HTML or plain-text output according to user preferences known ahead of time. The latter is an ideal solution when generating customized content for known users but breaks down a bit when sending more generic messages, such as system announcements or newsletters, to larger groups of users in which user preference information isn't available. Add in the users who can support HTML mail but have intentionally turned off the feature in their client, and it turns out that sending HTML mail exclusively alienates a large portion of your potential audience.

One possible solution is simply to avoid HTML mail entirely and cater to the lowest common denominator. This is the way to go if maximum accessibility is important. However, the MIME standard does offer another option. When a MIME-compliant email program reads a normal multipart message, it treats the first part as the message body, which is displayed to the user, and the subsequent parts as file attachments:

 Multipart mp = new MimeMultipart("alternative"); MimeBodyPart mbp1 = new MimeBodyPart(  ); mbp1.setContent("Log file for today is attached.", "text/plain"); mp.addBodyPart(mbp1);  . . .  MimeBodyPart mbp2 = new MimeBodyPart(  ); mbp2.setContent("Log file for <b>today</b> is attached.", "text/html"); mp.addBodyPart(mbp2);    . . . 

10.2.5 Use Multipart/Related for Rich Messages

To ensure that they can be read as easily as possible, richly formatted messages should be completely self-contained, instead of relying on images or resources available on a server. The MIME standard provides another special multipart type: "related". Related parts can refer to each other via URIs pointing to the Content-ID , or "CID," associated with each part. A CID URI takes the form "cid:contentid". You can use the setHeader( ) method of MimeBodyPart to create known content IDs for each message part. Once this is done, HTML parts can, for example, display an image by referring to the message part containing the image data.

The following example contains an HTML part that includes an <img> tag pointing to the "cid:myimage" URI. It also contains a second part containing a GIF image with a content ID of "myimage" .

 . . .  Message msg = new MimeMessage(session); msg.setFrom(new InternetAddress("images@company.com")); msg.setRecipient(Message.RecipientType.TO,                  new InternetAddress("bob.cratchett@company.com")); msg.setSubject("Image Email"); Multipart multipartRelated = new MimeMultipart("related");   MimeBodyPart mbp = new MimeBodyPart(  );        mbp.setContent(  "<html><body><img src=\"cid:myimage\"></body></html>", "text/html"); multipartRelated.addBodyPart(mbp);  . . .  MimeBodyPart mbp2 = new MimeBodyPart(  ); mbp2.setDataHandler(new DataHandler(new FileDataSource("c:\inline.gif"))); mbp2.setFileName("inline.gif"); mbp2.setHeader("Content-ID","myimage"); multipartRelated.addBodyPart(mbp2);    msg.setContent(multipartRelated);  . . . 

Using the related type does bulk up the size of your messages, but it allows them to be read when the recipient is offline or when the web server that would otherwise provide the images is inaccessible. It also gives you control over the resource requirements for sending messages (you don't need to be able to support massive numbers of hits on your web site if every recipient decides to read the message at once). [3]

[3] There are two advantages to embedding links to images on a web server rather than embedding them in the message itself. First, you can reduce message sizes, which is valuable in some situations. Second, you have the opportunity to watch your web server logs to determine whether a message has been read. Use this capability with care, as it requires the user to have a live Internet connection when reading mail and also raises some potentially thorny privacy issues.

10.2.6 Use Related Alternatives

The related MIME type alone does not provide a plain-text alternative for browsers that don't support pretty graphics. If you add a text part to the preceding message, the text will display along with the HTML, rather than instead of it. Fortunately, JavaMail allows you to nest Multipart objects, so you can create a top-level "multipart/alternative" and include both a plain-text option and a "multipart/related" section containing HTML and supporting resources.

Multipart objects cannot be added directly to each other, but must be wrapped into a MimeBodyPart first. You can easily extend the earlier example to do this:

 Multipart topLevel = new MimeMultipart("alternative");     MimeBodyPart htmlContent = new MimeBodyPart(  ); htmlContent.setContent(multipartRelated); MimeBodyPart textAlternate = new MimeBodyPart(  ); textAlternate.setText("Guidelines");       topLevel.addBodyPart(textAlternate);     topLevel.addBodyPart(htmlContent); msg.setContent(topLevel); 

Recipients with HTML capability will now see the HTML version with the embedded graphics, and users with text-only capability will see the text version. Older clients will see everything, but placing the text part at the beginning of the message ensures that they'll be able to make some sense of what's going on.

Of course, the XSL approach can also be used to create the plain text and HTML content for multipart/alternative and multipart/related messages.

I l @ ve RuBoard

The OReilly Java Authors - JavaT Enterprise Best Practices
The OReilly Java Authors - JavaT Enterprise Best Practices
Year: 2002
Pages: 96

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