`
ispring
  • 浏览: 355959 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Send mail with spring mail support and velocity

阅读更多
One of the requirements on my current project is to send Job Applicants an e-mail when they apply for a position. Since we're using Spring, I figured I'd try out its JavaMail and Velocity support to send this e-mail. Below is a short tutorial for setting up Spring's JavaMail support on a PositionManager class, followed by replacing the e-mail's text with a Velocity template. It's possible there's easier ways to do this, but this is what worked for me.
Step 1: Configure the JavaMailSenderImpl

The first step is to setup a MailSender for the PositionManager. To do this, you need to configure a JavaMailSenderImpl with a host or a session. For a host, it's rather simple. Add the following to your applicationContext.xml file:
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host"><value>localhost</value></property>
    <!-- If you don't want to hardcode "localhost", load it from a mail.properties file
         with PropertyPlaceholderConfigurer -->
</bean>

Optionally, you can also configure it with a Session from JNDI when you're running in a servlet container:
<bean id="mailSession" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName"><value>java:comp/env/mail/Session</value></property>
</bean>

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

I use the first option for JUnit tests, and the 2nd when running in Tomcat. Thanks to Juergen for showing me how easy the JNDI setup is. ;-)
Step 2: Configure a SimpleMailMessage with default values

Next you can configure a SimpleMailMessage with some default values in your applicationContext.xml file:
<bean id="mailMessage" class="org.springframework.mail.SimpleMailMessage">
    <property name="from"><value><![CDATA[Human Resources <hr@raibledesigns.com>]]></value></property>
    <property name="subject"><value>Your application has been received</value></property>
</bean>

Step 3: Configure dependencies in PositionManagerImpl

Then in traditional Spring-style, you need to add variables and setters to the PositionManagerImpl class:
    private MailSender mailSender;
    private SimpleMailMessage message;

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

    public void setMessage(SimpleMailMessage message) {
        this.message = message;
    }

Then configure this class's definition in applicationContext.xml so Spring will inject its dependencies:
<bean id="positionManagerTarget" class="org.appfuse.service.PositionManagerImpl">
    ...
    <property name="mailSender"><ref bean="mailSender"/></property>
    <property name="message"><ref bean="mailMessage"/></property>
    ...
</bean>

Now you should be able to easily send an e-mail in a method of this class:
    // user and position objects looked up...
    SimpleMailMessage msg = new SimpleMailMessage(this.message);
    msg.setTo(user.getFullName() + "<" + user.getEmail() + ">");

    StringBuffer txt = new StringBuffer();
    txt.append("Dear " + user.getFullName() + ",\n\n");
    txt.append("Thank you for application for our ");
    txt.append(position.getName() + " position. You can check ");
    txt.append(" on the status of this position at the URL below.\n\n");
    txt.append("    http://raibledesigns.com/positions/status.jsp\n\n");  // doesn't really exist ;-)
    txt.append("Sincerely, \n\nRaible Designs Human Resources");
    msg.setText(txt.toString());
    try {
        mailSender.send(msg);
    } catch (MailException ex) {
        log.error(ex.getMessage());
    }

The only problem with this is that the e-mail message is hard-coded into our Java code - so let's refactor it to use a Velocity template for the text.
Step 4: Configuring Velocity in applicationContext.xml

The next step is to use Spring's Velocity support classes to configure a VelocityEngine. For this, add the following to your applicationContext.xml file:
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="velocityProperties">
        <props>
            <prop key="resource.loader">class</prop>
            <prop key="class.resource.loader.class">
                org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
            </prop>
        </props>
    </property>
</bean>

NOTE: You can also use <property name="configLocation">velocity.properties</property> if velocity.properties file is in your classpath. However, my velocity.properties file has a webapp.loader defined in it, and since this depends on javax.servlet.ServletContext, I didn't want to use it in my business logic layer. You could also load velocity.properties with PropertyPlaceholderConfigurer and then refer to ${class.resource.loader.class}.
Step 5: Configure Velocity dependency in PositionManagerImpl

In order to use this nice little velocityEngine you just configured, you'll need to add a variable and setter to PositionManagerImpl:
    private VelocityEngine velocityEngine;

    public void setVelocityEngine(VelocityEngine velocityEngine) {
        this.velocityEngine = velocityEngine;
    }

And configure it's dependency in applicationContext.xml:
<bean id="positionManagerTarget" class="org.appfuse.service.PositionManagerImpl">
    ...
    <property name="velocityEngine"><ref bean="velocityEngine"/></property>
    ...
</bean>

Now you can refactor the text part of the previous e-mail sending logic to use a template.
    Map model = new HashMap();
    model.put("user", user);
    model.put("position", position);

    String result = null;
    try {
        // notificationTemplate.vm must be in your classpath
        result = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
                        "notificationTemplate.vm", model);
    } catch (VelocityException e) {
        e.printStackTrace();
    }
    msg.setText(result);

Pretty slick huh? A further configuration option is to use Spring to set the name of the template. If you know of any better ways to do JavaMail and e-mail templates with Spring, or find errors in my code - please let me know.

One thing that seems to wrong with this is that when I run my PositionManagerTest JUnit test - it initializes Velocity a number of times. This is because the PersonManagerImpl is re-initialized each time in my setUp() method. This is a JUnit issue, not a Spring issue. I could probably do something so the PositionManagerImpl is only created once for the entire Test run. Either that, or figure out a way to initialize Velocity for only one test. Hints would be great.

Another issue is that I'd like to use Velocity's DataSourceResourceLoader, but it only accepts a JNDI DataSource name. It'd be nice if there was an alternative version that would allow setting of the DataSource via IoC.
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics