404 lines
18 KiB
XML
404 lines
18 KiB
XML
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<chapter id="mail">
|
||
|
|
<title>Email</title>
|
||
|
|
|
||
|
|
<section id="mail-introduction">
|
||
|
|
<title>Introduction</title>
|
||
|
|
<sidebar>
|
||
|
|
<title>Library dependencies</title>
|
||
|
|
<para>The following additional jars to be on the classpath of your
|
||
|
|
application in order to be able to use the Spring Framework's email library.</para>
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>The <ulink url="http://java.sun.com/products/javamail/">JavaMail</ulink> <filename class="libraryfile">mail.jar</filename> library</para>
|
||
|
|
</listitem>
|
||
|
|
<listitem>
|
||
|
|
<para>The <ulink url="http://java.sun.com/products/javabeans/jaf/downloads/index.html">JAF</ulink> <filename class="libraryfile">activation.jar</filename> library</para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
<para>All of these libraries are available in the Spring-with-dependencies
|
||
|
|
distribution of the Spring Framework (in addition to also being freely
|
||
|
|
available on the web).</para>
|
||
|
|
</sidebar>
|
||
|
|
|
||
|
|
<para>The Spring Framework provides a helpful utility library for sending
|
||
|
|
email that shields the user from the specifics of the underlying mailing
|
||
|
|
system and is responsible for low level resource handling on behalf of
|
||
|
|
the client.</para>
|
||
|
|
|
||
|
|
<para>The <literal>org.springframework.mail</literal> package is the root level package
|
||
|
|
for the Spring Framework's email support. The central interface for sending
|
||
|
|
emails is the <interfacename>MailSender</interfacename> interface; a simple value object
|
||
|
|
encapsulating the properties of a simple mail such as <emphasis>from</emphasis> and
|
||
|
|
<emphasis>to</emphasis> (plus many others) is the <classname>SimpleMailMessage</classname> class.
|
||
|
|
This package also contains a hierarchy of checked exceptions which provide
|
||
|
|
a higher level of abstraction over the lower level mail system exceptions
|
||
|
|
with the root exception being <exceptionname>MailException</exceptionname>. Please
|
||
|
|
refer to the Javadocs for more information on the rich mail exception hierarchy.</para>
|
||
|
|
|
||
|
|
<para>The <interfacename>org.springframework.mail.javamail.JavaMailSender</interfacename>
|
||
|
|
interface adds specialized <emphasis>JavaMail</emphasis> features such as MIME
|
||
|
|
message support to the <interfacename>MailSender</interfacename> interface
|
||
|
|
(from which it inherits). <interfacename>JavaMailSender</interfacename> also provides a
|
||
|
|
callback interface for preparation of JavaMail MIME messages, called
|
||
|
|
<interfacename>org.springframework.mail.javamail.MimeMessagePreparator</interfacename></para>
|
||
|
|
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="mail-usage">
|
||
|
|
<title>Usage</title>
|
||
|
|
<para>Let's assume there is a business interface called <interfacename>OrderManager</interfacename>:</para>
|
||
|
|
<programlisting><![CDATA[public interface OrderManager {
|
||
|
|
|
||
|
|
void placeOrder(Order order);
|
||
|
|
}]]></programlisting>
|
||
|
|
|
||
|
|
<para>Let us also assume that there is a requirement stating that an email message
|
||
|
|
with an order number needs to be generated and sent to a customer placing the
|
||
|
|
relevant order.</para>
|
||
|
|
|
||
|
|
<section id="mail-usage-simple">
|
||
|
|
<title>Basic <interfacename>MailSender</interfacename> and <classname>SimpleMailMessage</classname> usage</title>
|
||
|
|
<programlisting><![CDATA[import org.springframework.mail.MailException;
|
||
|
|
import org.springframework.mail.MailSender;
|
||
|
|
import org.springframework.mail.SimpleMailMessage;
|
||
|
|
|
||
|
|
public class SimpleOrderManager implements OrderManager {
|
||
|
|
|
||
|
|
private MailSender mailSender;
|
||
|
|
private SimpleMailMessage templateMessage;
|
||
|
|
|
||
|
|
public void setMailSender(MailSender mailSender) {
|
||
|
|
this.mailSender = mailSender;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void setTemplateMessage(SimpleMailMessage templateMessage) {
|
||
|
|
this.templateMessage = templateMessage;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void placeOrder(Order order) {
|
||
|
|
|
||
|
|
]]><lineannotation>// Do the business calculations...</lineannotation><![CDATA[
|
||
|
|
|
||
|
|
]]><lineannotation>// Call the collaborators to persist the order...</lineannotation><![CDATA[
|
||
|
|
|
||
|
|
]]><lineannotation>// Create a thread safe "copy" of the template message and customize it</lineannotation><![CDATA[
|
||
|
|
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
|
||
|
|
msg.setTo(order.getCustomer().getEmailAddress());
|
||
|
|
msg.setText(
|
||
|
|
"Dear " + order.getCustomer().getFirstName()
|
||
|
|
+ order.getCustomer().getLastName()
|
||
|
|
+ ", thank you for placing order. Your order number is "
|
||
|
|
+ order.getOrderNumber());
|
||
|
|
try{
|
||
|
|
this.mailSender.send(msg);
|
||
|
|
}
|
||
|
|
catch(MailException ex) {
|
||
|
|
]]><lineannotation>// simply log it and go on...</lineannotation><![CDATA[
|
||
|
|
System.err.println(ex.getMessage());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}]]></programlisting>
|
||
|
|
|
||
|
|
<para>Find below the bean definitions for the above code:</para>
|
||
|
|
<programlisting><![CDATA[<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
|
||
|
|
<property name="host" value="mail.mycompany.com"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
]]><lineannotation><!-- this is a template message that we can pre-load with default state --></lineannotation><![CDATA[
|
||
|
|
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
|
||
|
|
<property name="from" value="customerservice@mycompany.com"/>
|
||
|
|
<property name="subject" value="Your order"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
|
||
|
|
<property name="mailSender" ref="mailSender"/>
|
||
|
|
<property name="templateMessage" ref="templateMessage"/>
|
||
|
|
</bean>]]></programlisting>
|
||
|
|
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="mail-usage-mime">
|
||
|
|
<title>Using the <interfacename>JavaMailSender</interfacename> and the <classname>MimeMessagePreparator</classname></title>
|
||
|
|
<para>Here is another implementation of <interfacename>OrderManager</interfacename> using
|
||
|
|
the <interfacename>MimeMessagePreparator</interfacename> callback interface. Please note
|
||
|
|
in this case that the <literal>mailSender</literal> property is of type
|
||
|
|
<interfacename>JavaMailSender</interfacename> so that we are able to use the JavaMail
|
||
|
|
<classname>MimeMessage</classname> class:</para>
|
||
|
|
|
||
|
|
<programlisting><![CDATA[import javax.mail.Message;
|
||
|
|
import javax.mail.MessagingException;
|
||
|
|
import javax.mail.internet.InternetAddress;
|
||
|
|
import javax.mail.internet.MimeMessage;
|
||
|
|
|
||
|
|
import javax.mail.internet.MimeMessage;
|
||
|
|
import org.springframework.mail.MailException;
|
||
|
|
import org.springframework.mail.javamail.JavaMailSender;
|
||
|
|
import org.springframework.mail.javamail.MimeMessagePreparator;
|
||
|
|
|
||
|
|
public class SimpleOrderManager implements OrderManager {
|
||
|
|
|
||
|
|
private JavaMailSender mailSender;
|
||
|
|
|
||
|
|
public void setMailSender(JavaMailSender mailSender) {
|
||
|
|
this.mailSender = mailSender;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void placeOrder(final Order order) {
|
||
|
|
|
||
|
|
]]><lineannotation>// Do the business calculations...</lineannotation><![CDATA[
|
||
|
|
|
||
|
|
]]><lineannotation>// Call the collaborators to persist the order...</lineannotation><![CDATA[
|
||
|
|
|
||
|
|
MimeMessagePreparator preparator = new MimeMessagePreparator() {
|
||
|
|
|
||
|
|
public void prepare(MimeMessage mimeMessage) throws Exception {
|
||
|
|
|
||
|
|
mimeMessage.setRecipient(Message.RecipientType.TO,
|
||
|
|
new InternetAddress(order.getCustomer().getEmailAddress()));
|
||
|
|
mimeMessage.setFrom(new InternetAddress("mail@mycompany.com"));
|
||
|
|
mimeMessage.setText(
|
||
|
|
"Dear " + order.getCustomer().getFirstName() + " "
|
||
|
|
+ order.getCustomer().getLastName()
|
||
|
|
+ ", thank you for placing order. Your order number is "
|
||
|
|
+ order.getOrderNumber());
|
||
|
|
}
|
||
|
|
};
|
||
|
|
try {
|
||
|
|
this.mailSender.send(preparator);
|
||
|
|
}
|
||
|
|
catch (MailException ex) {
|
||
|
|
]]><lineannotation>// simply log it and go on...</lineannotation><![CDATA[
|
||
|
|
System.err.println(ex.getMessage());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}]]></programlisting>
|
||
|
|
|
||
|
|
<note>
|
||
|
|
<para>The mail code is a crosscutting concern and could well be a candidate
|
||
|
|
for refactoring into a <link linkend="aop">custom Spring AOP aspect</link>,
|
||
|
|
which then could be executed at appropriate joinpoints on the
|
||
|
|
<interfacename>OrderManager</interfacename> target.</para>
|
||
|
|
</note>
|
||
|
|
|
||
|
|
<para>The Spring Framework's mail support ships with two
|
||
|
|
<interfacename>MailSender</interfacename> implementations. The standard JavaMail
|
||
|
|
implementation and the implementation on top of Jason Hunter's
|
||
|
|
<classname>MailMessage</classname> class that is included in
|
||
|
|
<ulink url="http://servlets.com/cos">the <literal>com.oreilly.servlet</literal>
|
||
|
|
package</ulink>. Please refer to the relevant Javadocs for more information.</para>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section id="mail-javamail-mime">
|
||
|
|
<title>Using the JavaMail <classname>MimeMessageHelper</classname></title>
|
||
|
|
|
||
|
|
<para>A class that comes in pretty handy when dealing with JavaMail messages is
|
||
|
|
the <classname>org.springframework.mail.javamail.MimeMessageHelper</classname> class,
|
||
|
|
which shields you from having to use the verbose JavaMail API. Using
|
||
|
|
the <classname>MimeMessageHelper</classname> it is pretty easy to
|
||
|
|
create a <classname>MimeMessage</classname>:</para>
|
||
|
|
<programlisting><lineannotation>// of course you would use DI in any real-world cases</lineannotation><![CDATA[
|
||
|
|
JavaMailSenderImpl sender = new JavaMailSenderImpl();
|
||
|
|
sender.setHost("mail.host.com");
|
||
|
|
|
||
|
|
MimeMessage message = sender.createMimeMessage();
|
||
|
|
MimeMessageHelper helper = new MimeMessageHelper(message);
|
||
|
|
helper.setTo("test@host.com");
|
||
|
|
helper.setText("Thank you for ordering!");
|
||
|
|
|
||
|
|
sender.send(message);]]></programlisting>
|
||
|
|
|
||
|
|
<section id="mail-javamail-mime-attachments">
|
||
|
|
<title>Sending attachments and inline resources</title>
|
||
|
|
<para>Multipart email messages allow for both attachments and inline resources.
|
||
|
|
Examples of inline resources would be be images or a stylesheet you want to use
|
||
|
|
in your message, but that you don't want displayed as an attachment.</para>
|
||
|
|
<section id="mail-javamail-mime-attachments-attachment">
|
||
|
|
<title>Attachments</title>
|
||
|
|
<para>The following example shows you how to use the
|
||
|
|
<classname>MimeMessageHelper</classname> to send an email along with a
|
||
|
|
single JPEG image attachment.</para>
|
||
|
|
<programlisting><![CDATA[JavaMailSenderImpl sender = new JavaMailSenderImpl();
|
||
|
|
sender.setHost("mail.host.com");
|
||
|
|
|
||
|
|
MimeMessage message = sender.createMimeMessage();
|
||
|
|
|
||
|
|
]]><lineannotation>// use the true flag to indicate you need a multipart message</lineannotation><![CDATA[
|
||
|
|
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||
|
|
helper.setTo("test@host.com");
|
||
|
|
|
||
|
|
helper.setText("Check out this image!");
|
||
|
|
|
||
|
|
]]><lineannotation>// let's attach the infamous windows Sample file (this time copied to c:/)</lineannotation><![CDATA[
|
||
|
|
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg"));
|
||
|
|
helper.addAttachment("CoolImage.jpg", file);
|
||
|
|
|
||
|
|
sender.send(message);]]></programlisting>
|
||
|
|
</section>
|
||
|
|
<section id="mail-javamail-mime-attachments-inline">
|
||
|
|
<title>Inline resources</title>
|
||
|
|
<para>The following example shows you how to use the
|
||
|
|
<classname>MimeMessageHelper</classname> to send an email along with an
|
||
|
|
inline image.</para>
|
||
|
|
<programlisting><![CDATA[JavaMailSenderImpl sender = new JavaMailSenderImpl();
|
||
|
|
sender.setHost("mail.host.com");
|
||
|
|
|
||
|
|
MimeMessage message = sender.createMimeMessage();
|
||
|
|
|
||
|
|
]]><lineannotation>// use the true flag to indicate you need a multipart message</lineannotation><![CDATA[
|
||
|
|
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||
|
|
helper.setTo("test@host.com");
|
||
|
|
|
||
|
|
]]><lineannotation>// use the true flag to indicate the text included is HTML</lineannotation><![CDATA[
|
||
|
|
helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);
|
||
|
|
|
||
|
|
]]><lineannotation>// let's include the infamous windows Sample file (this time copied to c:/)</lineannotation><![CDATA[
|
||
|
|
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
|
||
|
|
helper.addInline("identifier1234", res);
|
||
|
|
|
||
|
|
sender.send(message);]]></programlisting>
|
||
|
|
<warning>
|
||
|
|
<para>Inline resources are added to the mime message using the
|
||
|
|
specified <literal>Content-ID</literal> (<literal>identifier1234</literal>
|
||
|
|
in the above example). The order in which you are adding the text and the
|
||
|
|
resource are <emphasis role="bold">very</emphasis> important. Be sure to
|
||
|
|
<emphasis>first add the text</emphasis> and after that the resources. If
|
||
|
|
you are doing it the other way around, it won't work!</para>
|
||
|
|
</warning>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
<section id="mail-templates">
|
||
|
|
<title>Creating email content using a templating library</title>
|
||
|
|
<para>The code in the previous examples explicitly has been creating the
|
||
|
|
content of the email message, using methods calls such as
|
||
|
|
<methodname>message.setText(..)</methodname>. This is fine for
|
||
|
|
simple cases, and it is okay in the context of the aforementioned
|
||
|
|
examples, where the intent was to show you the very basics of the API.</para>
|
||
|
|
<para>In your typical enterprise application though, you are not going
|
||
|
|
to create the content of your emails using the above approach for a number
|
||
|
|
of reasons.</para>
|
||
|
|
<para>
|
||
|
|
<itemizedlist>
|
||
|
|
<listitem>
|
||
|
|
<para>Creating HTML-based email content in Java code is tedious and error prone</para>
|
||
|
|
</listitem>
|
||
|
|
<listitem>
|
||
|
|
<para>There is no clear separation between display logic and business logic</para>
|
||
|
|
</listitem>
|
||
|
|
<listitem>
|
||
|
|
<para>Changing the display structure of the email content requires writing Java code, recompiling, redeploying...</para>
|
||
|
|
</listitem>
|
||
|
|
</itemizedlist>
|
||
|
|
</para>
|
||
|
|
<para>Typically the approach taken to address these issues is to use a template library
|
||
|
|
such as FreeMarker or Velocity to define the display structure of email content. This leaves
|
||
|
|
your code tasked only with creating the data that is to be rendered in the email
|
||
|
|
template and sending the email. It is definitely a best practice for when
|
||
|
|
the content of your emails becomes even moderately complex, and with
|
||
|
|
the Spring Framework's support classes for FreeMarker and Velocity becomes
|
||
|
|
quite easy to do. Find below an example of using the Velocity template library
|
||
|
|
to create email content.</para>
|
||
|
|
<section id="mail-templates-example">
|
||
|
|
<title>A Velocity-based example</title>
|
||
|
|
<para>To use <ulink url="http://velocity.apache.org">Velocity</ulink> to
|
||
|
|
create your email template(s), you will need to have the Velocity libraries
|
||
|
|
available on your classpath. You will also need to create one or more Velocity templates
|
||
|
|
for the email content that your application needs. Find below the Velocity
|
||
|
|
template that this example will be using... as you can see it is HTML-based,
|
||
|
|
and since it is plain text it can be created using your favorite HTML editor
|
||
|
|
without recourse to having to know Java.</para>
|
||
|
|
<programlisting><lineannotation># in the <literal>com/foo/package</literal></lineannotation><![CDATA[
|
||
|
|
<html>
|
||
|
|
<body>
|
||
|
|
<h3>Hi ${user.userName}, welcome to the Chipping Sodbury On-the-Hill message boards!</h3>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
Your email address is <a href="mailto:${user.emailAddress}">${user.emailAddress}</a>.
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
|
||
|
|
</html>]]></programlisting>
|
||
|
|
<para>Find below some simple code and Spring XML configuration that
|
||
|
|
makes use of the above Velocity template to create email content and
|
||
|
|
send email(s).</para>
|
||
|
|
<programlisting><![CDATA[package com.foo;
|
||
|
|
|
||
|
|
import org.apache.velocity.app.VelocityEngine;
|
||
|
|
import org.springframework.mail.javamail.JavaMailSender;
|
||
|
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||
|
|
import org.springframework.mail.javamail.MimeMessagePreparator;
|
||
|
|
import org.springframework.ui.velocity.VelocityEngineUtils;
|
||
|
|
|
||
|
|
import javax.mail.internet.MimeMessage;
|
||
|
|
import java.util.HashMap;
|
||
|
|
import java.util.Map;
|
||
|
|
|
||
|
|
public class SimpleRegistrationService implements RegistrationService {
|
||
|
|
|
||
|
|
private JavaMailSender mailSender;
|
||
|
|
private VelocityEngine velocityEngine;
|
||
|
|
|
||
|
|
public void setMailSender(JavaMailSender mailSender) {
|
||
|
|
this.mailSender = mailSender;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void setVelocityEngine(VelocityEngine velocityEngine) {
|
||
|
|
this.velocityEngine = velocityEngine;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void register(User user) {
|
||
|
|
|
||
|
|
]]><lineannotation>// Do the registration logic...</lineannotation><![CDATA[
|
||
|
|
|
||
|
|
sendConfirmationEmail(user);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void sendConfirmationEmail(final User user) {
|
||
|
|
MimeMessagePreparator preparator = new MimeMessagePreparator() {
|
||
|
|
public void prepare(MimeMessage mimeMessage) throws Exception {
|
||
|
|
MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
|
||
|
|
message.setTo(user.getEmailAddress());
|
||
|
|
message.setFrom("webmaster@csonth.gov.uk"); ]]><lineannotation>// could be parameterized...</lineannotation><![CDATA[
|
||
|
|
Map model = new HashMap();
|
||
|
|
model.put("user", user);
|
||
|
|
String text = VelocityEngineUtils.mergeTemplateIntoString(
|
||
|
|
velocityEngine, "com/dns/registration-confirmation.vm", model);
|
||
|
|
message.setText(text, true);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
this.mailSender.send(preparator);
|
||
|
|
}
|
||
|
|
}]]></programlisting>
|
||
|
|
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||
|
|
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
|
||
|
|
|
||
|
|
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
|
||
|
|
<property name="host" value="mail.csonth.gov.uk"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="registrationService" class="com.foo.SimpleRegistrationService">
|
||
|
|
<property name="mailSender" ref="mailSender"/>
|
||
|
|
<property name="velocityEngine" ref="velocityEngine"/>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
|
||
|
|
<property name="velocityProperties">
|
||
|
|
<value>
|
||
|
|
resource.loader=class
|
||
|
|
class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
|
||
|
|
</value>
|
||
|
|
</property>
|
||
|
|
</bean>
|
||
|
|
|
||
|
|
</beans>]]></programlisting>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
</chapter>
|