User ${timesheet.consultant.accountName} has updated one of their timesheets.
Velocity uses a syntax similar to the expression language used by JSPs and the standard tag library (JSTL) for representing content for replacement The Velocity markup engine is provided with the macro from Listing 8-9 and a suitably named timesheet object The part of Listing 8-9 marked in bold will be equivalent to calling the getConsultant() method on the timesheet object, and the getAccountName() method on the resulting UserAccount object The resulting variable (the timesheet owner’s account name) will be substituted into the HTML when the message is sent 163 Minter_685-4C08.fm Page 164 Monday, November 5, 2007 12:13 PM 164 CHAPTER ■ SE NDING E-MAIL Listing 8-10 shows the implementation of this version of the DAO Listing 8-10 The Implementation of Our Simple DAO for Sending HTML-Formatted Mail public class VelocityMailDaoImpl extends AbstractMailDaoImpl { private JavaMailSender mailSender; private String velocityMacroPath; private VelocityEngine velocityEngine; public void sendTimesheetUpdate(final Timesheet timesheet) { final MimeMessagePreparator preparator = new MimeMessagePreparator() { public void prepare(MimeMessage mimeMessage) throws Exception { final MimeMessageHelper message = new MimeMessageHelper(mimeMessage); message.setTo(rcptAddress); message.setSubject(subject); message.setFrom(fromAddress); final Map model = new HashMap(); model.put("timesheet", timesheet); final String text = VelocityEngineUtils mergeTemplateIntoString(velocityEngine, velocityMacroPath, model); message.setText(text, true); } }; this.mailSender.send(preparator); } @Required public void setMailSender(JavaMailSender mailSender) { this.mailSender = mailSender; } @Required public void setVelocityEngine(VelocityEngine velocityEngine) { this.velocityEngine = velocityEngine; } Minter_685-4C08.fm Page 165 Monday, November 5, 2007 12:13 PM C H AP TE R ■ SEN DI NG E -M AI L @Required public void setVelocityMacroPath(final String velocityMacroPath) { this.velocityMacroPath = velocityMacroPath; } } Again we draw the addressing and subject information from the properties of the parent class, and we require a MailSender implementation (though here it must be a JavaMailSender, while the previous implementation accepted any MailSender implementation) These parts are similar, but the creation of the message is somewhat more complicated First, we create an anonymous instance of a MimeMessagePreparator to format the message This is a symptom of the complexity of the standard JavaMail library that Spring uses to perform MIME operations When a message is sent, the preparator’s prepare method is passed a MimeMessage and the preparator must populate it Nonetheless, within this method there are some similarities with Listing 8-7 To create the body of the message, we populate a map object with the entities that will be needed by the Velocity macro in order to render the e-mail For this example, this is the timesheet only, and the key value inserted into the map is the first part of the name used in Listing 8-9 to identify the substitution value (where the other parts of the name were the names of the bean properties to obtain) Listing 8-11 shows the configuration of this enhanced DAO implementation for sending formatted e-mails Listing 8-11 The Configuration of Our Simple DAO Implementation for Sending HTML-Formatted Mail The notable differences are the requirements for a velocityEngine bean (used to invoke the appropriate Velocity formatting) and the path to the Velocity macro file of Listing 8-9 Listing 8-12 shows the configuration details required for the Velocity engine bean required by Listing 8-11 Listing 8-12 The Configuration Details for Velocity in Spring 165 Minter_685-4C08.fm Page 166 Monday, November 5, 2007 12:13 PM 166 CHAPTER ■ SE NDING E-MAIL resource.loader=class class.resource.loader.class=org.apache.velocity.runtime ➥ resource.loader.ClasspathResourceLoader The purpose of this bean is essentially to provide a more IOC-oriented implementation of existing Velocity classes, but it also allows us to override some default properties We have used this to specify that the markup files to be used should be loaded from the classpath instead of from an explicitly specified path The resulting e-mail is shown in Figure 8-2 Figure 8-2 The formatted e-mail The text/html content type is applied to the message by the MimeMessageHelper’s setText method; setting the second Boolean parameter to true specifies that an HTML message is being created If the flag is set to false or the single parameter version of the send method is used, the content type is set to text/plain The specific formatting mechanism used to create the content does not need to be Velocity Other templating tools such as FreeMarker can be used, or the content can be created from code for particularly simple cases If the content is not to be modified at all, it can be read directly from an HTML file Including Inline Images and Attachments The previous section shows how e-mail can be formatted as HTML, but what about including external content in the e-mail? If we want to add graphics to the e-mail, how should we go about doing this? One option is to use references to externally hosted material, and this will work in some cases, but it has some disadvantages The first minor objection is that you will need to host the content for as long as the content of the e-mail will remain valid The users should not find that their e-mail becomes unreadable just because your website is unavailable (if they are temporarily offline, for example) The more major objection is that many e-mail clients Minter_685-4C08.fm Page 167 Monday, November 5, 2007 12:13 PM C H AP TE R ■ SEN DI NG E -M AI L will not automatically download offline content There are various reasons for this that are unimportant to us, because the net result is that we cannot predict how our e-mail will appear to all users The solution is to include the content within the e-mail itself and to reference it from the HTML An example of the HTML used to reference an inline image is shown in Listing 8-13 (but note that this technique works for any inline content, not just images) In addition to explicitly referenced inline binary content such as images, we can include unreferenced attachments in our message The user’s mail client will typically make these available for download upon receipt, so if the purpose of your e-mail is purely to transfer a file, the binary should be included as a file, not as inline content Our example sends the same image in both modes The code marked in bold in Listing 8-13 is an image tag referencing an image to be included in the message Listing 8-13 A Velocity Macro Containing a URI Referencing Inline Content ## Sent whenever a timesheet is updated Timesheet updatedUser ${timesheet.consultant.accountName} has updated one of their timesheets.
Image attached Should be equivalent to the following image:
The cid: prefix is a URI representing message content (CID stands for common image descriptor) The inlineImage following this is the identifier we will be using to associate the link with the correct inline content Naturally, you must select a unique name to identify unique content items The naming format follows the RFC 1738 URL address specification, but I would recommend that you constrain yourself to simple letters and numbers Listing 8-14 shows the implementation of our DAO to send the timesheet update with both the HTML content and images Listing 8-14 Our DAO Implementation Supporting Both Attachments and Inline Images public class VelocityImageMailDaoImpl extends AbstractMailDaoImpl { private JavaMailSender mailSender; private String velocityMacroPath; private VelocityEngine velocityEngine; private Resource attachment; private Resource image; 167 Minter_685-4C08.fm Page 168 Monday, November 5, 2007 12:13 PM 168 CHAPTER ■ SE NDING E-MAIL public void sendTimesheetUpdate(final Timesheet timesheet) { final MimeMessagePreparator preparator = new MimeMessagePreparator() { public void prepare(MimeMessage mimeMessage) throws Exception { final MimeMessageHelper message = new MimeMessageHelper( mimeMessage, true); message.setTo(rcptAddress); message.setSubject(subject); message.setFrom(fromAddress); message.addAttachment(attachment.getFilename(), attachment); final Map model = new HashMap(); model.put("timesheet", timesheet); final String text = VelocityEngineUtils mergeTemplateIntoString(velocityEngine, velocityMacroPath, model); message.setText(text, true); message.addInline("inlineImage", image); } }; mailSender.send(preparator); } @Required public void setMailSender(final JavaMailSender mailSender) { this.mailSender = mailSender; } @Required public void setVelocityEngine(final VelocityEngine velocityEngine) { this.velocityEngine = velocityEngine; } @Required public void setVelocityMacroPath(final String velocityMacroPath) { this.velocityMacroPath = velocityMacroPath; } @Required public void setAttachment(final Resource attachment) { this.attachment = attachment; } Minter_685-4C08.fm Page 169 Monday, November 5, 2007 12:13 PM C H AP TE R ■ SEN DI NG E -M AI L @Required public void setImage(final Resource image) { this.image = image; } } Broadly speaking, this code is similar to the example given in Listing 8-10 The differences are in the attachment of the images First, we add an attachment to the message This can be done at any point within the prepare method We then add the message text Finally, we add an inline image The ordering of the last two steps is mandatory: the body text that contains URIs referencing inline content must be added to the message before the inline images themselves are added In Listing 8-14, I have specified the attachment and image properties as accepting a Resource object in preference to file paths This allows the greatest flexibility in the type of resource definition that can be provided, and so in Listing 8-15 I have specified the properties as paths relative to the classpath Listing 8-15 The Configuration of the Image-Aware DAO Implementation ■Note If you have a large number of files, you may want to use an alternative mechanism to add the files to your outgoing message A flat directory structure containing the template files and images could be checked at runtime, allowing the images to be attached programmatically with CIDs based on their (necessarily unique) filenames If generated files (for example, PDFs created by using the view technologies described in Chapter 6) are to be attached, the path to the file and a suitable unique identifier can be passed in with the model information In this case, as in many others, the use of a classpath-relative resource ensures that the application will be easy to port to other environments and other platforms The rest of the configuration details in Listing 8-15 are similar to those of the other HTML example in Listing 8-11, except for the specific Velocity macro file to be loaded and the DAO implementation class 169 Minter_685-4C08.fm Page 170 Monday, November 5, 2007 12:13 PM 170 CHAPTER ■ SE NDING E-MAIL As Figure 8-3 shows, most e-mail clients treat inline content and attachments as quite distinct parts of the message Indeed, it is possible for the body of the message to consist of plain text but still include separate attachments Figure 8-3 The formatted e-mail with an attachment and an inline image Conclusion In this chapter, you have seen how a Spring application can create e-mail content on-the-fly, injecting the specific formatting (and, indeed, the specific formatting mechanism) into the bean responsible for sending messages In the next chapter, you will see the provision for remoting mechanisms, allowing our application’s service layer to be accessed by external systems without going through the web front end ... represent the standards for sending e-mails composed of multiple discrete files—typically the e-mail text, any inline images, and any attachments associated with the e-mail Minter_685-4C08.fm... plain-text e-mail that will be sent by the basic e-mail DAO implementation Figure 8-1 The plain-text e-mail For the sake of the simplicity of the examples, the recipient, sender, and subject of the e-mail. .. the service layer, and so invoke the mechanism to send e-mail as appropriate Using the Mail Sender Spring provides two interfaces for sending e-mail The first and simplest of these is the MailSender