Mails met Spring, Velocity en JavaMail

September 11th, 2012 | 9 min read | Apache Velocity, JavaMail, mail, Spring, Spring MVC, Web

In deze tutorial ga ik een mail versturen met behulp van Apache Velocity, JavaMail en Spring. Velocity is een template engine en in deze tutorial ga ik deze gebruiken om een mail template te vullen met werkelijke data. JavaMail zorgt er dan weer voor dat de mail verstuurd kan worden en Spring integreert zowel Velocity als JavaMail zodat deze frameworks eenvoudiger zijn in gebruik.

Heads up!

In deze tutorial wordt met regelmaat verwezen naar mijn vorige Java tutorials. Het is dus handig dat je deze eens doorneemt.

Project opzetten

Net zoals gewoonlijk gaan we beginnen met een Spring webapp. Ik raad je aan de code te gebruiken uit mijn Spring webapp tutorial, dit bespaart enorm veel tijd en de standaard-configuratie hebben we daar al volledig achter de rug.

De package

be.g00glen00b.domain

mag je verwijderen, maak daarna wel de package

be.g00glen00b.service

aan en zorg voor de volgende configuratiebestanden:

spring-velocity.xml

(Spring bean configuration) en

mail.properties

.

In de package

be.g00glen00b.service

mag je nu een class

MailService

maken.

Mail template

Het eerste wat je nodig hebt is een (eenvoudige) mail template, zelf ga ik kiezen voor deze template. Sleep de content van jouw gekozen template naar

src/main/resources

.

Open nu de HTML pagina die bij de template zit en verander alle “variabele” inhoud door placeholders. Zo heb ik bijvoorbeeld op de plaats waar de titel moest komen

${title}

geplaatst en waar de tekst komt heb ik

{text}

geplaatst.

Op de plaatsen waar je een afbeelding tegen komt moet je de

src

veranderen naar

cid:

met daarachter een identifier. De afbeelding

images/facebook.gif

wordt daardoor bijvoorbeeld

cid:facebook

. Dat de verwijzing naar het pad verdwijnt maakt niet uit, de identifier mag je ook vrij kiezen en hoeft niet dezelfde te zijn als de filename.
Hernoem dit HTML bestand nu naar

template.vm

, dit is geen verplichting, maar meestal wordt dit gedaan bij Velocity templates.

Je project zou er nu moeten uitzien als in onderstaande afbeelding.

Maven dependencies

Bij dit voorbeeld horen ook enkele extra dependencies.

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>${velocity.version}</version>
</dependency>
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.4</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-tools</artifactId>
    <version>2.0</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-oxm</artifactId>
    <version>${spring.version}</version>
</dependency>

De bijhorende versienummer (voor Velocity) is

<velocity.version>1.7</velocity.version>

.

Configuratie

springmvc-servlet.xml

Hier gaan we niet veel moeten wijzigen. De enige aanpassingen die we moeten verrichten zijn dat we

be.g00glen00b.service

gaan toevoegen aan de te scannen packages en dat we

spring-velocity.xml

moeten importeren.

<context:component-scan base-package="be.g00glen00b.service" />
<import resource="spring-velocity.xml" />

spring-velocity.xml

In dit bestand gaan we weliswaar wat meer moeten aan toevoegen. De beans die je moet toevoegen zijn de volgende.

<bean
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:/mail.properties" />
    <property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="${smtp.host}" />
    <property name="port" value="${smtp.port}" />
    <property name="protocol" value="smtps" />
    <property name="username" value="${smtp.username}" />
    <property name="password" value="${smtp.password}" />

    <property name="javaMailProperties">
        <props>
            <prop key="mail.smtps.auth">${smtp.auth}</prop>
            <prop key="mail.smtps.starttls.enable">${smtp.tls}</prop>
            <prop key="mail.smtps.connectiontimeout">10000</prop>
        </props>
    </property>
</bean>

<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>

<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
    <property name="from" value="${mail.from}" />
    <property name="subject" value="Nieuwsbrief" />
</bean>

De

PropertyPlaceHolder

bean zorgt ervoor dat de configuratie uit property-files beschikbaar wordt net zoals we eerder al deden met de datasource bij Hibernate.

Dan hebben we de

mailSender

bean die, zoals de naam al verklapt, ervoor zal zorgen dat mails verstuurd kunnen worden. Hiervoor hebben we natuurlijk enkele gegevens nodig zoals SMTP host, port, username, … . Deze gegevens worden via de

PropertyPlaceHolder

beschikbaar gesteld.

De

velocityEngine

bean zorgt voor het omzetten van de template naar de juiste output. Hiervoor moeten we natuurlijk de locatie van de template kennen die via deze bean configuratie vastgelegd wordt.
De meeste Velocity configuratiefouten komen voor in dit deel. In een Maven webapp zijn er twee folders die aan het classpath toegevoegd worden, namelijk

src/main/java

en

src/main/resources

. De

webapp

folder wordt niet aan het classpath toegevoegd maar deze bestanden komen terecht in de context root van je webapp.

Ten slotte hebben we nog de bean

templateMessage

die enkele default-gegevens bevat voor een mail. Indien je geen “default” onderwerp of zender hebt zou je ervoor kunnen kiezen om deze bean weg te laten en deze gegevens in de code vast te leggen.

mail.properties

Als laatste bestand moeten we enkel nog de SMTP-server gerelateerde gegevens invoeren.
Voor Google Mail zouden de volgende gegevens moeten werken.

smtp.host=smtp.gmail.com
smtp.port=465
smtp.username=*******@gmail.com
smtp.password=*******
smtp.auth=true
smtp.tls=true
mail.from=*******@gmail.com

Vergeet natuurlijk niet om de sterretjes te vervangen door jouw eigen gegevens.

Controller

Om deze mail applicatie te testen ga ik een form maken waar je een mail adres kan invoeren waar de mail naar verstuurd moet worden. De code voor deze controller is de volgende.

@Controller
public class MainController {

    @Autowired
    private MailService service;

    @Autowired
    private SimpleMailMessage message;

    @RequestMapping(value = "/index.html", method = RequestMethod.GET)
    public String getIndex() {
        return "index";
    }

    @RequestMapping(value = "/index.html", method = RequestMethod.POST)
    public String postIndex(@RequestParam("receiver") String receiver,
            ModelMap model) {
        ModelMap mailModel = new ModelMap();
        SimpleDateFormat format = new SimpleDateFormat("mmmm dd, yyyy");

        mailModel.addAttribute("title", "Nieuwsbrief");
        mailModel.addAttribute("subtitle", "Velocity rocks");
        mailModel
                .addAttribute(
                        "text",
                        "Zoals je kan zien wordt de ModelMap op meerdere manieren gebruikt. We gebruiken het al een hele tijd om model-data mee te geven aan onze webpaginas, maar nu gebruiken we het ook om model data in de mail template te laden.<br />We voegen tevens de afbeeldingen toe die we nodig hebben als attachment. Deze kunnen we later tonen door cid: voor de afbeeldingsnaam te plaatsen.");
        mailModel.addAttribute("date", format.format(new Date()));

        SimpleMailMessage msg = new SimpleMailMessage(message);
        msg.setTo(receiver);
        service.sendMail(mailModel, msg);

        model.addAttribute("receiver", receiver);
        return "index";
    }
}

In de

postIndex

zal de mail service aangesproken worden die de mail zal versturen. In deze methode gaan we al enkele zaken vast leggen zoals de gegevens die getoond moeten worden. Deze gegevens leggen we vast in een

ModelMap

instantie zoals we al vaker deden bij Spring MVC. De namen van deze attributen komen overeen met de placeholders die we in de

template.vm

vastgelegd hebben.

Daarnaast heb ik hier ook een

SimpleMailMessage

object dat eigenlijk de gegevens bevat van de mail zelf zoals de zender, ontvanger, onderwerp, … . Omdat we de meeste gegevens al vastgelegd hebben in de bean configuration moeten we nu enkel nog de ontvanger vast leggen.

Mail service

De mail service zal ervoor zorgen dat de mail met de juiste inhoud gevuld wordt dankzij Velocity + Spring en dat deze mail ook verstuurd geraakt via JavaMail + Spring.

De code voor deze class is de volgende.

@Component
public class MailService {

    @Autowired
    private VelocityEngine velocityEngine;

    @Autowired
    private JavaMailSender mailSender;

    public void sendMail(final ModelMap model, final SimpleMailMessage msg) {
        mailSender.send(new MimeMessagePreparator() {

            @Override
            public void prepare(MimeMessage mimeMessage)
                    throws MessagingException {
                MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8");
                message.setTo(msg.getTo());
                message.setFrom(msg.getFrom());
                message.setSubject(msg.getSubject());

                String body = VelocityEngineUtils.mergeTemplateIntoString(
                        velocityEngine, "/template.vm", model);
                message.setText(body, true);

                message.addInline("facebook", new ClassPathResource(
                        "images/facebook.gif"));
                message.addInline("header", new ClassPathResource(
                        "images/header.jpg"));
                message.addInline("pic1", new ClassPathResource(
                        "images/pic1.jpg"));
                message.addInline("tweet", new ClassPathResource(
                        "images/tweet.gif"));
            }
        });
    }
}

Zoals je kan zien hebben we in deze class twee velden die overeen komen met de twee zaken (template en mail versturen) die ik eerder al enkele keren genoemd heb.

In de methode

sendMail()

wordt eigenlijk alles geregeld. De eerste stap is dat we de gegevens uit de

SimpleMailMessage

uit de controller gaan omzetten naar een meer uitgebreide versie waar je afbeeldingen en dergelijke aan kan toevoegen, namelijk

MimeMessageHelper

.

De volgende stap is dat de model attributen samengevoegd worden met de template om zo de juiste HTML response terug te krijgen in de variabel

body

.
Uiteindelijk moeten we dan enkel nog de bijlagen toevoegen, namelijk de afbeeldingen die we tonen in de mail. De namen van deze resources komen overeen met de identifiers die je vastgelegd hebt in de mail template.

JSP pagina

Als laatste stap moeten we enkel nog

index.jsp

wijzigen. De code voor deze pagina is vrij eenvoudig, het enige wat we moeten doen is een form voorzien. De code hiervoor is:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Velocity</title>
</head>
<body>
    <c:if test="${not empty receiver}">
        <h3>Mail verzonden naar ${receiver}</h3>
    </c:if>
    <form action="index.html" method="post">
        <table>
            <tr>
                <td>Verstuur naar:</td>
                <td><input type="text" name="receiver" value="${receiver}" /></td>
            </tr>
            <tr>
                <td colspan="2" align="center"><input type="submit"
                    value="Versturen" /></td>
            </tr>
        </table>
    </form>
</body>
</html>

Builden

Het project builden doen we zoals gewoonlijk met Maven. Eenmaal klaar kan je je browser openen en naar de index-pagina gaan. Voer daar een mail adres in en na een tijd zal je een boodschap te zien krijgen.

Dit wilt zeggen dat je mail verstuurd is, je kan nu naar je inbox gaan om de mail te controleren.

Zoals je kan zien staan de afbeeldingen mooi op z’n plaats en is de inhoud van de mail mooi samengesmolten met de template.

Download project

Download – velocity-example.rar (71 kB)

Back to tutorialsContact me on TwitterDiscuss on Twitter

Profile picture

Dimitri "g00glen00b" Mestdagh is a consultant at Cronos and tech lead at Aquafin. Usually you can find him trying out new libraries and technologies. Loves both Java and JavaScript.