Thursday, October 21, 2010

Spring v3.0.2 Learning Note 14 - Send Email with Spring

Reference Library

%SPRING%\dist\org.springframework.core-3.0.2.RELEASE.jar
%SPRING%\dist\org.springframework.asm-3.0.2.RELEASE.jar
%SPRING%\dist\org.springframework.beans-3.0.2.RELEASE.jar
%SPRING%\dist\org.springframework.context-3.0.2.RELEASE.jar
%SPRING%\dist\org.springframework.context.support-3.0.2.RELEASE.jar
%SPRING%\dist\org.springframework.expression-3.0.2.RELEASE.jar 
%SPRING%\dist\org.springframework.aspects-3.0.2.RELEASE.jar
%SPRING%\dist\org.springframework.aop-3.0.2.RELEASE.jar
%SPRING_DEP%\org.apache.commons\org.apache.common\com.springsource.org.apache.commons.logging\1.1.1\com.springsource.org.apache.commons.logging-1.1.1.jar
%SPRING_DEP%\javax.mail\com.springsource.javax.mail\1.4.0\com.springsource.javax.mail-1.4.0.jar
%SPRING_DEP%\org.aopalliance\com.springsource.org.aopalliance\1.0.0\com.springsource.org.aopalliance-1.0.0.jar

create a java project and add above library files to project classpath.

This is the interface class:
// interface class
package test.spring.email;

public interface AccountNotifier {
    public void notifyAccount(String accountId, String password);
}

 Send Email Using JavaMail API

package test.spring.email;

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.springframework.stereotype.Component;

@Component
public class AccountNotifierImpl implements AccountNotifier {

    @Override
    public void notifyAccount(String accountId, String password) {
        Properties props = new Properties();
        props.put("mail.smtp.host", "smtp.gmail.com");
        props.put("mail.smtp.port", "587");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        Session session = Session.getDefaultInstance(props,
                new Authenticator() {
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication("<gmail account>", "<gmail password>");
                    }
                });
        session.setDebug(true);
        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress("ABC@gmail.com"));
            message.setRecipients(Message.RecipientType.TO, InternetAddress
                    .parse("DEF@yahoo.com"));
            message.setSubject("Account Notification Test");
            message.setText("Dear Customer,\n\n" + "Account No. : " + accountId
                    + "\n" + "Password : " + password);
            Transport.send(message);
            System.out.println("success!");
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}

Firstly, open a mail session connecting to an SMTP server by defining the properties. Then create a message from this session for constructing your e-mail. After that, send the e-mail by making a call to Transport.send().

This is the spring configuration file.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <context:component-scan base-package="test.spring.email" />
</beans>

Run this junit test, you can see the email has been sent out.

// junit test
package test.spring.email;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EmailTester {

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Test
    public void testEmailSender() {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "spring.xml");

        AccountNotifier accountNotifier = (AccountNotifier) context
                .getBean("accountNotifierImpl");
        accountNotifier.notifyAccount("Account12", "pwd123");
    }
}

Send Email with Spring's MailSender

The core interface of Spring’s e-mail support is MailSender. If only sending plain text in the email, use this interface.


// implement class
package test.spring.email;

import javax.annotation.Resource;

import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Component;

@Component
public class AccountNotifierMailSenderImpl implements AccountNotifier {
    @Resource
    private MailSender mailSender;

    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
    }

    @Override
    public void notifyAccount(String accountId, String password) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("ABC@yahoo.com");
        message.setTo("DEF@yahoo.com");
        message.setSubject("Account Notifier with Spring Mail Sender");
        message.setText("Dear Customer,\n\n" + "Account No. : " + accountId
                + "\n" + "Password : " + password);
        mailSender.send(message);
    }
}

then, configure mailSender in spring as follows.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <context:component-scan base-package="test.spring.email" />
    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="smtp.gmail.com" />
        <property name="port" value="587" />
        <property name="username" value="<your account>" />
        <property name="password" value="<your password>" />
        <property name="javaMailProperties">
            <props>
                <!-- Use SMTP-AUTH to authenticate to SMTP server -->
                <prop key="mail.smtp.auth">true</prop>
                <!-- Use TLS to encrypt communication with SMTP server -->
                <prop key="mail.smtp.starttls.enable">true</prop>
                 <!-- print session debug info -->
                <prop key="mail.debug">true</prop>
            </props>
        </property>
    </bean>
</beans>

If you have a JavaMail session configured in your application server, you can first look it up with the help of JndiObjectFactoryBean.

<bean id="mailSession" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="mail/Session" />
</bean>

Or you can look up a JavaMail session through the <jee:jndi-lookup> element if you are using Spring 2.0 or later.

<jee:jndi-lookup id="mailSession" jndi-name="mail/Session" />

You can inject the JavaMail session into JavaMailSenderImpl for its use. In this case, you no longer need to set the host, port, username, or password.

<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="session" ref="mailSession" />
</bean>

If you are using tomcat 6.x, you can define JavaMail session as follows.

<Context path="/myapp" docBase="myapp">
<!-- JavaMail session factory -->
    <Resource name="mail/Session"
              auth="Container"
              type="javax.mail.Session"
              username="yourusername"
              password="yourpassword"
              mail.debug="true"
              mail.user="yourusername"
              mail.password="yourpassword"
              mail.transport.protocol="smtp"
              mail.smtp.host="your.smtphost.com"
              mail.smtp.auth="true"
              mail.smtp.port="25"
              mail.smtp.starttls.enable="true"/>
</Context>

Email Template

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/aop 
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:component-scan base-package="test.spring.email" />
 <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="smtp.gmail.com" />
  <property name="port" value="587" />
  <property name="username" value="<yourname>" />
  <property name="password" value="<password>" />
  <property name="javaMailProperties">
   <props>
    <!-- Use SMTP-AUTH to authenticate to SMTP server -->
    <prop key="mail.smtp.auth">true</prop>
    <!-- Use TLS to encrypt communication with SMTP server -->
    <prop key="mail.smtp.starttls.enable">true</prop>
    <prop key="mail.debug">true</prop>
   </props>
  </property>

 </bean>
 <bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage">
  <!-- <property name="from" value="ABC@gmail.com" />  -->
  <property name="from">
   <value><![CDATA[Application Notifier <noreply@gmail.com>]]></value>
  </property>
  <!-- <property name="to" value="DEF@yahoo.com" />   -->
  <property name="to">
   <value><![CDATA[System Customer <DEF@yahoo.com>]]></value>
  </property>
  <property name="subject"
   value="Account Notification Test with Email Template" />
  <property name="text">
   <value>
<![CDATA[
Dear Customer,

Account No. : %s
Password : %s

XXX Administrator
]]>
   </value>
  </property>
 </bean>

</beans>
package test.spring.email;

import javax.annotation.Resource;

import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Component;

@Component
public class AccountNotifierMailSenderImpl implements AccountNotifier {
 @Resource
 private MailSender mailSender;
 @Resource
 private SimpleMailMessage mailMessage;

 public void setMailSender(MailSender mailSender) {
  this.mailSender = mailSender;
 }

 public void setMailMessage(SimpleMailMessage mailMessage) {
  this.mailMessage = mailMessage;
 }

 @Override
 public void notifyAccount(String accountId, String password) {
  SimpleMailMessage message = new SimpleMailMessage(mailMessage);
  message.setText(String.format(mailMessage.getText(), accountId,
    password));
  mailSender.send(message);
 }

}
Note the placeholders %s, which will be replaced by message parameters through String.format().Of course, you can also use a powerful templating language such as Velocity or FreeMarker to generate the message text according to a template. It’s also a good practice to separate mail message templates from bean configuration files.Each time you send e-mail, you can construct a new SimpleMailMessage instance from this injected template. Then you can generate the message text using String.format() to replace the %s placeholders with your message parameters.

Sending MIME Message

// change bean definition
    <bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage">
        <!-- <property name="from" value="ABC@gmail.com" />  -->
        <property name="from">
            <value><![CDATA[Application Notifier <noreply@gmail.com>]]></value>
        </property>
        <!-- <property name="to" value="DEF@yahoo.com" />   -->
        <property name="to">
            <value><![CDATA[System Customer <xiang.wang@ufinity.com>]]></value>
        </property>
        <property name="subject"
            value="Account Notification Test with Email Template" />
        <property name="text">
            <value>
<![CDATA[               //---<<<<< include HTML tag within email content
<html><body>
Dear Customer,<p/>

Account No. :<b><style color="red"> %s</style></b><br/>
Password : %s<br/>
<br/>
Now you are allowed to access:<br/>
<ul>
<li><a href="www.yahoo.com">yahoo</a></li>
<li><a href="www.google.com">google</a></li>
</ul>
<br/>
XXX Administrator
</body></html>
]]>
            </value>
        </property>
    </bean>


// implement class
package test.spring.email;

import java.io.File;

import javax.annotation.Resource;
import javax.mail.internet.MimeMessage;

import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.stereotype.Component;

@Component
public class AccountNotifierMailSenderImpl implements AccountNotifier {
    @Resource
    private JavaMailSender mailSender;
    @Resource
    private SimpleMailMessage mailMessage;
    // should be JavaMailSende, not MailSender instead
    public void setMailSender(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void setMailMessage(SimpleMailMessage mailMessage) {
        this.mailMessage = mailMessage;
    }

    @Override
    public void notifyAccount(final String accountId, final String password) {

        MimeMessagePreparator preparator = new MimeMessagePreparator() {
            public void prepare(MimeMessage mimeMessage) throws Exception {
                MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,
                        true);
                helper.setFrom(mailMessage.getFrom());
                helper.setTo(mailMessage.getTo());
                helper.setSubject(mailMessage.getSubject());
                helper.setText(String.format(mailMessage.getText(), accountId,
                        password), true); // must set true,otherwise, will show plain text in email
                // add attachment
                FileSystemResource file = new FileSystemResource(new File(
                        "d:/logo.jpg"));
                helper.addAttachment("mylog.jpg", file);
            }
        };
        mailSender.send(preparator);
    }
}

 In the prepare() method, you can prepare the MimeMessage object, which is precreated for JavaMailSender. If there’s any exception thrown, it will be converted into Spring’s mail runtime exception automatically.

No comments:

Post a Comment