Java Spring MVC Email Example using Apache Velocity

Java Spring MVC Email Example using Apache Velocity

In this tutorial we will discuss how to set up a Java Spring MVC RESTful Webservice with Email using Apache Velocity to create a Velocity template that is used to create an HTML email message and embed an image, as shown below, using MIME Multipart Message. The Spring Framework facilitates sending emails by providing helpful utility libraries that shield the user from all the specifics of the underlying system.

spring_mail_velocity_email

What is a MIME Multipart Message?

MIME stands for Multipurpose Internet Mail Extensions and came about in the early 90’s because the internet community needed something more plain text email capability. They needed a protocol that would extend the Simple Mail Transfer Protocol (SMTP) to support character sets outside of ASCII, include audio, video, images and other binary data files in the email and the current protocol just could not do that.

A MIME multipart message contains one or more data sets within a single body. Additionally, these parts are able to reference other message parts by using “Content-ID” header.

Getting Started

In order to run this tutorial yourself, you will need the following:

Required Libraries

Copy all of the following jars to WebContent->WEB-INF->lib folder.

apache-commons-lang.jar
commons-codec-1.4.jar
commons-collections-3.2.1.jar
commons-logging-1.2.jar
jackson-core-asl-1.9.13.jar
jackson-mapper-asl-1.9.13.jar
log4j-1.2.17.jar
mail.jar
slf4j-api-1.7.10.jar
slf4j-simple-1.7.10.jar
spring-aop-4.0.6.RELEASE.jar
spring-aspects-4.0.6.RELEASE.jar
spring-beans-4.0.6.RELEASE.jar
spring-context-4.0.6.RELEASE.jar
spring-context-support-4.0.6.RELEASE.jar
spring-core-4.0.6.RELEASE.jar
spring-expression-4.0.6.RELEASE.jar
spring-tx-4.0.6.RELEASE.jar
spring-web-4.0.6.RELEASE.jar
spring-webmvc-4.0.6.RELEASE.jar
velocity-1.7.jar
velocity-tools-2.0.jar

Project Overview

I have added the project overview to give you a full view of the structure and show you all files contained in this sample project.

java_spring_mail_proj_struct

The Models

Our Customer Model (Customer.java)

package com.avaldes.model;

import org.codehaus.jackson.annotate.JsonProperty;

public class Customer {
  private String customerId;
  private String firstName;
  private String lastName;
  private String address;
  private String city;
  private String state;
  private String zipCode;
  private String phoneNumber;
  private String emailAddress;
  private String companyName;

  public Customer(String customerId, String firstName, String lastName,
      String address, String city, String state, String zipCode,
      String phoneNumber, String emailAddress, String companyName) {

    this.customerId = customerId;
    this.firstName = firstName;
    this.lastName = lastName;
    this.address = address;
    this.city = city;
    this.state = state;
    this.zipCode = zipCode;
    this.phoneNumber = phoneNumber;
    this.emailAddress = emailAddress;
    this.companyName = companyName;
  }

  @JsonProperty("customer-id")
  public String getCustomerId() {
    return customerId;
  }

  public void setCustomerId(String customerId) {
    this.customerId = customerId;
  }

  @JsonProperty("first-name")
  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  @JsonProperty("last-name")
  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  @JsonProperty("address")
  public String getAddress() {
    return address;
  }

  public void setAddress(String address) {
    this.address = address;
  }

  @JsonProperty("city")
  public String getCity() {
    return city;
  }

  public void setCity(String city) {
    this.city = city;
  }

  @JsonProperty("state")
  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

  @JsonProperty("zip-code")
  public String getZipCode() {
    return zipCode;
  }

  public void setZipCode(String zipCode) {
    this.zipCode = zipCode;
  }

  @JsonProperty("phone-number")
  public String getPhoneNumber() {
    return phoneNumber;
  }

  public void setPhoneNumber(String phoneNumber) {
    this.phoneNumber = phoneNumber;
  }

  @JsonProperty("email-address")
  public String getEmailAddress() {
    return emailAddress;
  }

  public void setEmailAddress(String emailAddress) {
    this.emailAddress = emailAddress;
  }

  @JsonProperty("company-name")
  public String getCompanyName() {
    return companyName;
  }

  public void setCompanyName(String companyName) {
    this.companyName = companyName;
  }

  @Override
  public String toString() {
    return "Customer [customerId=" + customerId + ", firstName="
        + firstName + ", lastName=" + lastName + ", address=" 
        + address + ", city=" + city + ", state=" + state 
        + ", zipCode=" + zipCode + ", phoneNumber=" + phoneNumber 
        + ", emailAddress=" + emailAddress + ", companyName=" 
        + companyName + "]";
  }
}

The Invoice Model (Invoice.java)

package com.avaldes.model;

import java.util.Date;

import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;

import com.avaldes.util.JsonInvoiceDateSerializer;

public class Invoice {
  private String invNumber;
  private String poNumber;
  private Date invDate;
  private String customerId;
  private double invAmount;
  private boolean isTaxable;
  private double taxRate;

  public Invoice(String invNumber, String poNumber, Date invDate,
      String customerId, double invAmount, boolean isTaxable,
      double taxRate) {

    this.invNumber = invNumber;
    this.poNumber = poNumber;
    this.invDate = invDate;
    this.customerId = customerId;
    this.invAmount = invAmount;
    this.isTaxable = isTaxable;
    this.taxRate = taxRate;
  }

  @JsonProperty("invoice-number")
  public String getInvNumber() {
    return invNumber;
  }

  public void setInvNumber(String invNumber) {
    this.invNumber = invNumber;
  }

  @JsonProperty("po-number")
  public String getPoNumber() {
    return poNumber;
  }

  public void setPoNumber(String poNumber) {
    this.poNumber = poNumber;
  }

  @JsonProperty(value = "invoice-date")
  @JsonSerialize(using = JsonInvoiceDateSerializer.class)
  public Date getInvDate() {
    return invDate;
  }

  public void setInvDate(Date invDate) {
    this.invDate = invDate;
  }

  @JsonProperty(value = "customer-id")
  public String getCustomerId() {
    return customerId;
  }

  public void setCustomerId(String customerId) {
    this.customerId = customerId;
  }

  @JsonProperty(value = "is-taxable")
  public boolean isTaxable() {
    return isTaxable;
  }

  public void setTaxable(boolean isTaxable) {
    this.isTaxable = isTaxable;
  }

  @JsonProperty(value = "tax-rate")
  public double getTaxRate() {
    return taxRate;
  }

  public void setTaxRate(double taxRate) {
    this.taxRate = taxRate;
  }

  @JsonProperty(value = "invoice-amount")
  public double getInvAmount() {
    return invAmount;
  }

  public void setInvAmount(double invAmount) {
    this.invAmount = invAmount;
  }

  @Override
  public String toString() {
    return "Invoice [invNumber=" + invNumber + ", poNumber=" + poNumber
        + ", invDate=" + invDate + ", customerId=" + customerId
        + ", invAmount=" + invAmount + ", isTaxable=" + isTaxable
        + ", taxRate=" + taxRate + "]";
  }
}

The InvoiceItem Model (InvoiceItem.java)

package com.avaldes.model;

import org.codehaus.jackson.annotate.JsonProperty;

public class InvoiceItem {
  private String itemDesc;
  private int itemQty;
  private double itemPrice;
  private String invNumber;

  public InvoiceItem(String itemDesc, int itemQty, double itemPrice,
      String invNumber) {

    this.itemDesc = itemDesc;
    this.itemQty = itemQty;
    this.itemPrice = itemPrice;
    this.invNumber = invNumber;
  }

  @JsonProperty(value = "item-desc")
  public String getItemDesc() {
    return itemDesc;
  }

  public void setItemDesc(String itemDesc) {
    this.itemDesc = itemDesc;
  }

  @JsonProperty(value = "item-qty")
  public int getItemQty() {
    return itemQty;
  }

  public void setItemQty(int itemQty) {
    this.itemQty = itemQty;
  }

  @JsonProperty(value = "item-price")
  public double getItemPrice() {
    return itemPrice;
  }

  public void setItemPrice(double itemPrice) {
    this.itemPrice = itemPrice;
  }

  @JsonProperty(value = "invoice-number")
  public String getInvNumber() {
    return invNumber;
  }

  public void setInvNumber(String invNumber) {
    this.invNumber = invNumber;
  }

  @Override
  public String toString() {
    return "InvoiceItems [itemDesc=" + itemDesc + ", itemQty=" + itemQty
        + ", itemPrice=" + itemPrice + ", invNumber=" + invNumber + "]";
  }
}

Data Access Objects

Since the focus of this example/tutorial is really geared towards Spring MVC, Velocity and Java Mail I decided to keep data access local instead of having to persist it in a database like mongoDB, H2, mySQL, DB2, Oracle or Sybase. I have either created Maps (HashMaps) or Lists (ArrayList) for the data structures and stored the underlying Java Objects there. If you would like a tutorial covering how to use DAO objects to persist/retrieve information from a database, please refer to one of my many other tutorials to give you a better idea on how best to accomplish that.

Our Customer Data Access Object (CustomerDAO.java)

package com.avaldes.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import com.avaldes.model.Customer;

public class CustomerDAO {
  private static final Logger logger = Logger
      .getLogger(CustomerDAO.class);
  private static Map<String, Customer> allCustomers 
                            = new HashMap<String, Customer>();

  static {
    Customer c1 = new Customer("1", "John", "Crimson",
        "1928 Broad Street", "Clifton", "New Jersey", "07013",
        "201-778-2839", "john@tddtech.com",
        "TDD Technology Solutions, LLC");

    Customer c2 = new Customer("2", "Nancy", "Flanigan",
        "17 Northview Drive", "Hillside", "New Jersey", "07162",
        "908-565-3830", "nancy.flanigan@globalsigns.com",
        "Global Signs, Inc.");

    Customer c3 = new Customer("3", "Greg", "Kaminsky",
        "883 Franklin Blvd", "Cranford", "New Jersey", "08892",
        "908-221-7849", "greg@yaeger.com", "Yaeger Alliance");

    allCustomers.put("1", c1);
    allCustomers.put("2", c2);
    allCustomers.put("3", c3);
  }

  public static List<Customer> getAllCustomers() {
    List<Customer> all = new ArrayList<Customer>(
        allCustomers.values());

    return all;
  }

  public static Customer getCustomerByID(String id) {

    Customer cust = null;

    logger.info("Inside getCustomerByID() method...");

    if (allCustomers.containsKey(id)) {
      cust = allCustomers.get(id);
    } else {
      logger.error("Unable to find customer with ID=" + id);
    }

    return cust;
  }
}

The Invoice Data Access Object (InvoiceDAO.java)

package com.avaldes.dao;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.avaldes.model.Invoice;

public class InvoiceDAO {
  private static final Logger logger = Logger
      .getLogger(InvoiceDAO.class);
  private static Map<String, Invoice> allInvoices 
                                = new HashMap<String, Invoice>();

  static {
    Invoice i1 = new Invoice("1287129", "1872",
        new GregorianCalendar(2014, 10, 3).getTime(), "1", 4278.12,
        true, 0.08);
    Invoice i2 = new Invoice("1287130", "23982302",
        new GregorianCalendar(2014, 6, 13).getTime(), "2", 6781.91,
        true, 0.07);
    Invoice i3 = new Invoice("1287131", "475843",
        new GregorianCalendar(2014, 7, 4).getTime(), "1", 1672.00,
        false, 0);

    allInvoices.put("1287129", i1);
    allInvoices.put("1287130", i2);
    allInvoices.put("1287131", i3);
  }

  public static Invoice getInvoiceByID(String id) {

    Invoice inv = null;

    logger.info("Inside getInvoiceByID() method...");

    if (allInvoices.containsKey(id)) {
      inv = allInvoices.get(id);
    } else {
      logger.error("Unable to find invoice with ID=" + id);
    }

    return inv;
  }

  public static List<Invoice> getAllInvoices() {
    List<Invoice> all = new ArrayList<Invoice>(allInvoices.values());

    return all;
  }
}

The InvoiceItem Data Access Object (InvoiceItemDAO.java)

package com.avaldes.dao;

import java.util.ArrayList;
import java.util.List;

import com.avaldes.model.InvoiceItem;

public class InvoiceItemDAO {
  // Setting up Lists to Simulate Data from some DataSource
  // (ie: Database, External System, etc)
  private static List<InvoiceItem> allInvItems = new ArrayList<InvoiceItem>();

  static {
    InvoiceItem it1 = new InvoiceItem("3/4-inch plywood", 20, 19.99,
        "1287129");
    InvoiceItem it2 = new InvoiceItem("Doors", 4, 85.99, "1287129");
    InvoiceItem it3 = new InvoiceItem("Door Knobs", 4, 35.99,
        "1287129");
    InvoiceItem it4 = new InvoiceItem("Misc Hardware", 1, 73.50,
        "1287129");
    InvoiceItem it5 = new InvoiceItem("Labor", 20, 150.00, "1287129");

    allInvItems.add(it1);
    allInvItems.add(it2);
    allInvItems.add(it3);
    allInvItems.add(it4);
    allInvItems.add(it5);

    InvoiceItem it6 = new InvoiceItem("3/4-inch plywood", 12, 19.99,
        "1287130");
    InvoiceItem it7 = new InvoiceItem("Anderson Windows Std", 15,
        249.99, "1287130");
    InvoiceItem it8 = new InvoiceItem("Misc Hardware", 1, 98.50,
        "1287130");
    InvoiceItem it9 = new InvoiceItem("Labor", 15, 150, "1287130");

    allInvItems.add(it6);
    allInvItems.add(it7);
    allInvItems.add(it8);
    allInvItems.add(it9);

    InvoiceItem it10 = new InvoiceItem("Sander Rental", 8, 59.00,
        "1287131");
    InvoiceItem it11 = new InvoiceItem("Labor", 8, 150.00, "1287131");

    allInvItems.add(it10);
    allInvItems.add(it11);
  }

  public static List<InvoiceItem> getAllInvoiceItems() {
    return allInvItems;
  }

  public static List<InvoiceItem> getItemsbyInvoiceNumber(
      String invNum) {
    List<InvoiceItem> items = new ArrayList<InvoiceItem>();

    for (InvoiceItem item : allInvItems) {
      if (item.getInvNumber().equals(invNum)) {
        items.add(item);
      }
    }

    return items;
  }
}

The Velocity Template used in Email (invoice.vm)

<!DOCTYPE html>
<html>
  <body>
    <table width="99%">
      <tr>
        <td>
          <table>
            <tr>
              <td>
                #if (! $test)
                <img src="cid:myLogo" alt="myLogo" width="300" height="64">
								<br/>
                #else 
                <img src="/SpringMailExample/images/myLogo.png" 
														alt="myLogo" width="300" height="64"><br/>
                #end
                123 Street Address<br/>
                TownName, State 01920-1223<br/><br/>
                (800) 555-1212<br/>
                billing@example.com<br/>
              </td>
            </tr>
          </table>
        </td>
        <td align="right">
          <table>
            <tr>
              <td align="right">
                <h1>Invoice</h1>
                $date.format('MMMM dd, yyyy', ${invoice.invDate})<br/>
                Invoice #${invoice.invNumber}<br/>
                PO ${invoice.poNumber}<br/><br/>
                <strong>ATTN ${customer.firstName} ${customer.lastName}<br/>
                ${customer.companyName}</strong>
              </td>
            </tr>
          </table>
        </td>
      </tr>
      <tr>
        <td colspan="2"><hr/></td>
      </tr>
      <tr>
        <td  colspan="2" align="center">
          <table width="80%">
            <tr>
              <td  align="left">
Dear ${customer.firstName} ${customer.lastName},<br/><br/>
    
Below please find a detailed cost breakdown for our recently 
completed work. Please make payment at your earliest convenience, 
and do not hesitate to contact me should you have any questions.<br/>
<br/>
    
Many thanks,<br/>
Amaury Valdes<br/><br/>
              </td>
            </tr>
          </table>
        </td>
      </tr>
      <tr>
        <td colspan="2" align="center">
          <table width="80%" bgcolor="#9EC5FF" border="1" 
								bordercolor="#888" cellspacing="0" cellpadding="4">
            <tr>
              <th align="left">#</th>
              <th align="left">Item Description</th>
              <th align="left">Quantity</th>
              <th align="left">Unit Price ($)</th>
              <th align="left">Total ($)</th>
            </tr>
            #set ( $row = 1 )
            #set ( $subtotal = 0)
            #set ( $salestax = 0)
            #set ( $total = 0)
            #set ( $taxPercent = $invoice.taxRate * 100.0)
            #foreach ( $item in $items )
              #if ( $row % 2 == 0 )
                #set ($color="#CBDCFF")
              #else
                #set ($color="#E3EEFF")
              #end
              <tr bgcolor="$color">
                <td align="left">$row</td>
                <td align="left">$item.itemDesc</td>
                <td align="center">$item.itemQty</td>
                <td align="right">$number.currency($item.itemPrice)</td>
                #set ( $itemTotal = $item.itemQty * $item.itemPrice )
                <td align="right">$number.currency($itemTotal)</td>
              </tr>
              #set ( $subtotal = $subtotal + $itemTotal )
              #set ( $row = $row + 1 )
            #end
            <tr bgcolor="#E3EEFF">
              <td colspan="4" align="left"><strong>Subtotal</strong></td>
              <td align="right">$number.currency($subtotal)</td> 
            </tr>
            <tr bgcolor="#E3EEFF">
              #set ( $salestax = $subtotal * $invoice.taxRate)
              <td colspan="4" align="left"><strong>
								Sales Tax ($number.format('##0.##', $taxPercent)%)
							</strong></td>
              <td align="right">$number.currency($salestax)</td>
            </tr>
            <tr bgcolor="#9EC5FF">
              #set ( $total = $subtotal + $salestax)
              <td colspan="4" align="left"><strong>Total</strong></td>
              <td align="right">$number.currency($total)</td>
            </tr>
          </table>
        </td>
      </tr>
    </table>
  </body>
</html>

MailDispatcher Insights

The MailDispatcher handles the job of creating the emails with the use of org.springframework.mail package. This package is the root level package for Spring Framework’s email support. We use the JavaMailSender interface which adds specialized JavaMail features such as MIME message support. JavaMailSender also provides a callback interface for preparation of JavaMail MIME messages, called org.springframework.mail.javamail.MimeMessagePreparator.

Additionally, we use the org.springframework.mail.javamail.MimeMessageHelper class, which shields us from having to use the verbose JavaMail API directly. By utilizing the MimeMessageHelper class it makes it quite trivial to create a MimeMessage.

Once we have this MIME message via the MimeMessageHelper class, we use the setter methods to set up the recipients, the fromAddress, and subject and begin to construct the multiple parts of the message. These parts are constructed by using the MimeBodyPart class. Using this class we create our message body which will be HTML by setting its content to be “text/html”. This message body will come from Velocity via the VelocityEngineUtils.mergeTemplateIntoString method which will merge the Velocity Template (invoice.vm) with the models I have created in the REST API call.

Attaching Images to the MIME Multipart Message

In order to attach the image to the MIME Multipart message load the image using a URLDataSource class in order to load the PNG file from the classpath under the templates folder and set it to the imagePart of the message using the DataHandler class. This will effectively base64 encode the image data into the MIME message. Now the important part: In order for the Velocity template to make use of this image which resides in another part of this “related” MIME Multipart message we must set “Content-ID” header to some ID which will be used in the Velocity Template, in my case, I set it to myLogo.

MailDispatcher (MailDispatcher.java)

package com.avaldes.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.activation.FileDataSource;
import javax.mail.Multipart;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.log4j.Logger;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
import org.springframework.ui.velocity.VelocityEngineUtils;

public class MailDispatcher {
  private static final Logger logger = Logger
      .getLogger(MailDispatcher.class);
  private Properties applicationProperties = null;
  private VelocityEngine velocityEngine;

  private String replyname = null;
  private String hostname = null;
  private String host = null;
  private String port = null;
  private String auth = null;
  private String starttls_enable = null;
  private String starttls_required = null;
  private String mail_debug = null;

  private JavaMailSenderImpl sender = new JavaMailSenderImpl();

  public MailDispatcher() {
    logger.info("Getting the Mail Properties...");
    applicationProperties = TomcatEnvironment
        .getApplicationProperties();
    replyname = applicationProperties
        .getProperty("mail.smtp.replyname").toString().trim();
    logger.info("mail.smtp.replyname is: [" + replyname + "]");

    hostname = applicationProperties
        .getProperty("mail.smtp.hostname").toString().trim();
    logger.info("mail.smtp.hostname is: [" + hostname + "]");

    host = applicationProperties.getProperty("mail.smtp.host")
        .toString().trim();
    logger.info("mail.smtp.host is: [" + host + "]");

    port = applicationProperties.getProperty("mail.smtp.port")
        .toString().trim();
    logger.info("mail.smtp.port is: [" + port + "]");

    auth = applicationProperties.getProperty("mail.smtp.auth")
        .toString().trim();
    logger.info("mail.smtp.auth is: [" + auth + "]");

    starttls_enable = applicationProperties
        .getProperty("mail.smtp.starttls.enable").toString().trim();
    logger.info("mail.smtp.starttls is: [" + starttls_enable + "]");

    starttls_required = applicationProperties
        .getProperty("mail.smtp.starttls.required").toString().trim();
    logger.info("mail.smtp.starttls.required is: ["
        + starttls_required + "]");

    mail_debug = applicationProperties.getProperty("mail.debug")
        .toString().trim();
    logger.info("mail.debug is: [" + mail_debug + "]");

    Properties mailProperties = new Properties();
    mailProperties.put("mail.smtp.host", host);
    mailProperties.put("mail.smtp.auth", auth);
    mailProperties.put("mail.smtp.starttls.enable", starttls_enable);
    mailProperties.put("mail.smtp.starttls.required",
        starttls_required);
    mailProperties.put("mail.debug", mail_debug);

    sender.setHost(host);
    sender.setPort(Integer.parseInt(port));
    sender.setProtocol("smtp");
    sender.setJavaMailProperties(mailProperties);

  }

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

  public void sendEmail(final String toAddress,
      final String fromAddress, final String subject,
      final String message) {

    logger.info("Inside of sendEmail...");

    String replyFrom = String.format("%s@%s", replyname, hostname);
    SimpleMailMessage email = new SimpleMailMessage();
    email.setTo(toAddress);
    if (fromAddress != null)
      email.setFrom(fromAddress);
    else
      email.setFrom(replyFrom);
    email.setSubject(subject);
    email.setText(message);

    sender.send(email);
  }

  public void sendEmail(final ArrayList<String> toAddress,
      final String fromAddress, final String subject,
      final String message) {

    logger.info("Inside of sendEmail...");

    String[] recipientsArray = toAddress.toArray(new String[toAddress
        .size()]);

    String replyFrom = String.format("%s@%s", replyname, hostname);
    SimpleMailMessage email = new SimpleMailMessage();
    email.setTo(recipientsArray);
    if (fromAddress != null)
      email.setFrom(fromAddress);
    else
      email.setFrom(replyFrom);
    email.setSubject(subject);
    email.setText(message);

    sender.send(email);
  }

  public void sendMimeEmail(final ArrayList<String> toAddress,
      final String fromAddress, final String subject,
      final Map<String, Object> model) {

    logger.info("Inside of sendMimeEmail...");

    final String replyFrom = String.format("%s@%s", replyname,
        hostname);
    MimeMessagePreparator prep = new MimeMessagePreparator() {

      @Override
      public void prepare(MimeMessage mimeMessage) throws Exception {
        MimeMessageHelper message = new MimeMessageHelper(
            mimeMessage, MimeMessageHelper.MULTIPART_MODE_RELATED,
            "UTF-8");

        String[] recipientsArray = toAddress
            .toArray(new String[toAddress.size()]);

        message.setTo(recipientsArray);
        if (fromAddress != null)
          message.setFrom(new InternetAddress(fromAddress));
        else
          message.setFrom(new InternetAddress(replyFrom));
        message.setSubject(subject);

        ClassLoader classLoader = Thread.currentThread()
            .getContextClassLoader();
        if (classLoader == null) {
          classLoader = this.getClass().getClassLoader();
        }

        // --Create the HTML body part of the message
        MimeBodyPart mimeBody = new MimeBodyPart();
        String body = VelocityEngineUtils.mergeTemplateIntoString(
            velocityEngine, "templates/invoice.vm", "UTF-8", model);
        mimeBody.setContent(body, "text/html");

        // --Create the image part of the message
        MimeBodyPart mimeImage = new MimeBodyPart();
        DataSource ds = new URLDataSource(
            classLoader.getResource("templates/myLogo.png"));

        mimeImage.setDataHandler(new DataHandler(ds));
        mimeImage.setHeader("Content-ID", "myLogo");

        Multipart multipart = new MimeMultipart();
        multipart.addBodyPart(mimeBody);
        multipart.addBodyPart(mimeImage);

        mimeMessage.setContent(multipart);
      }
    };
    sender.send(prep);
  }

  public String testMimeEmail(final Map<String, Object> model) {

    logger.info("Inside of sendEmail...");

    return VelocityEngineUtils.mergeTemplateIntoString(
        velocityEngine, "templates/invoice.vm", "UTF-8", model);
  }
}

Spring MVC Rest Controller(RestController.java)

package com.avaldes.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.velocity.tools.generic.DateTool;
import org.apache.velocity.tools.generic.NumberTool;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.avaldes.dao.CustomerDAO;
import com.avaldes.dao.InvoiceDAO;
import com.avaldes.dao.InvoiceItemDAO;
import com.avaldes.model.Customer;
import com.avaldes.model.Invoice;
import com.avaldes.model.InvoiceItem;
import com.avaldes.util.ApplicationContextProvider;
import com.avaldes.util.MailDispatcher;

@Lazy
@Controller
public class RestController {

 private static final Logger logger = Logger
   .getLogger(RestController.class);
 public static final String APPLICATION_HTML = "text/html";
 private MailDispatcher mailDispatcher;

 public RestController() {
  logger.info("Inside Constructor of RestController()...");

  mailDispatcher = (MailDispatcher) ApplicationContextProvider
    .getApplicationContext().getBean("mailDispatcher");

  try {
   ArrayList<String> recipients = new ArrayList<String>();
   recipients.add("amaury@avaldes.com");
   mailDispatcher.sendEmail(recipients, null,
     "SpringMailExample Started",
     "SpringMailExample have been successfully restarted...");
  } catch (Exception e) {
   logger.error("Unable to send email to Mail Server...");
  }
 }

 @RequestMapping(value = "/status", method = RequestMethod.GET, 
                     produces = APPLICATION_HTML)
 
 public @ResponseBody String status() {
  return "RestController Backend Status OK...";
 }

 @RequestMapping(value = "/getCustomerbyId", method = RequestMethod.GET)
 public @ResponseBody Customer getCustomerByID(
   @RequestParam(value = "id") String id) {

  Customer cust = CustomerDAO.getCustomerByID(id);
  return cust;
 }

 @RequestMapping(value = "/getAllCustomers", method = RequestMethod.GET)
 public @ResponseBody List<Customer> getAllCustomers() {
  return CustomerDAO.getAllCustomers();
 }

 @RequestMapping(value = "/getInvoicebyId", method = RequestMethod.GET)
 public @ResponseBody Invoice getInvoiceByID(
   @RequestParam(value = "id") String id) {

  Invoice inv = InvoiceDAO.getInvoiceByID(id);
  return inv;
 }

 @RequestMapping(value = "/getAllInvoices", method = RequestMethod.GET)
 public @ResponseBody List<Invoice> getAllInvoices() {
  return InvoiceDAO.getAllInvoices();
 }

 @RequestMapping(value = "/getAllInvoiceItems", method = RequestMethod.GET)
 public @ResponseBody List<InvoiceItem> getAllInvoiceItems() {
  return InvoiceItemDAO.getAllInvoiceItems();
 }

 @RequestMapping(value = "/getItemsByInvoiceNum", 
                                             method = RequestMethod.GET)
 public @ResponseBody List<InvoiceItem> getItemsbyInvoiceNumber(
   @RequestParam(value = "invno") String invno) {
  return InvoiceItemDAO.getItemsbyInvoiceNumber(invno);
 }

 @RequestMapping(value = "/generateInvoiceByNumber", 
                                             method = RequestMethod.GET)

 public @ResponseBody String generateInvoiceByNumber(
   @RequestParam(value = "invno") String invno) {

  boolean status = buildInvoice(invno);

  String message = (status == true)
    ? "Invoice Generated and Emailed"
    : "Unable to Find/Send Invoice";

  return message;
 }

 @RequestMapping(value = "/testInvoiceByNumber", method = RequestMethod.GET)
 public @ResponseBody String testInvoiceByNumber(
   @RequestParam(value = "invno") String invno) {

  return buildTestInvoice(invno);
 }

 private String buildTestInvoice(String inv) {
  String msg = null;
  Map<String, Object> model = new HashMap<String, Object>();

  // Find the Invoice
  Invoice invoice = InvoiceDAO.getInvoiceByID(inv);
  if (invoice != null) {
   Customer customer = CustomerDAO
     .getCustomerByID(invoice.getCustomerId());
   List<InvoiceItem> items = InvoiceItemDAO
     .getItemsbyInvoiceNumber(inv);

   model.put("test", new Boolean(true));
   model.put("invoice", invoice);
   model.put("customer", customer);
   model.put("items", items);
   model.put("date", new DateTool());
   model.put("number", new NumberTool());

   msg = mailDispatcher.testMimeEmail(model);
  }
  return msg;
 }

 private boolean buildInvoice(String inv) {
  boolean status = false;
  Map<String, Object> model = new HashMap<String, Object>();

  // Find the Invoice
  Invoice invoice = InvoiceDAO.getInvoiceByID(inv);
  if (invoice != null) {
   status = true;
   Customer customer = CustomerDAO
     .getCustomerByID(invoice.getCustomerId());
   List<InvoiceItem> items = InvoiceItemDAO
     .getItemsbyInvoiceNumber(inv);

   model.put("test", new Boolean(false));
   model.put("invoice", invoice);
   model.put("customer", customer);
   model.put("items", items);
   model.put("date", new DateTool());
   model.put("number", new NumberTool());

   try {
    ArrayList<String> recipients = new ArrayList<String>();
    recipients.add("tester@gmail.com");
    mailDispatcher.sendMimeEmail(recipients,
      "support@avaldes.com",
      String.format("Invoice %s", invoice.getInvNumber()),
      model);
   } catch (Exception e) {
    logger.error("Unable to send email to Mail Server...");
    logger.error("Exception: " + e);
    return false;
   }
  }
  return status;
 }
}

Web Deployment Descriptor (web.xml)

Our web.xml is quite straight forward. Here we define our DispatcherServlet servlet, define our servlet’s application context and define what our URL pattern is going to be for the dispatcher. I have configured it to look for the log4j.xml in the classpath in order to set up our logging options.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  id="WebApp_ID" version="2.5">

  <display-name>SpringMailExample</display-name>
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
			org.springframework.web.util.Log4jConfigListener
		</listener-class>
  </listener>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/config/root-context.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
  </listener>
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/config/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Configure Spring Web DispatcherServlet (dispatcher-servlet.xml)

Modify the dispatcher-servlet.xml and add the necessary MailDispatcher and VelocityEngineBeanFactory configurations. The MailDispatcher is set up to pull in all of the STMP configuration properties from the mailService.{env}.properties files during system start up. In addition, we will be injecting the velocityEngine via its bean reference using the setter method in MailDispatcher. This class uses the JavaMailSenderImpl class which supports both MimeMessages and SimpleMailMessages.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">

  <!-- Enables the Spring MVC @Controller programming model -->
  <mvc:annotation-driven />

  <context:component-scan base-package="com.avaldes" />

  <bean id="initialContext" class="javax.naming.InitialContext" />

  <bean id="tomcatEnvironment" init-method="init"
    class="com.avaldes.util.TomcatEnvironment">
    <property name="initialContext" ref="initialContext" />
  </bean>

  <bean id="applicationContext" 
                  class="com.avaldes.util.ApplicationContextProvider" />

  <bean id="mailDispatcher" class="com.avaldes.util.MailDispatcher"
    depends-on="tomcatEnvironment">
    <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>

Mail Properties File

property.name=default

#---MAILHOST Properties---
mail.smtp.replyname=support
mail.smtp.hostname=dev.avaldes.com
mail.smtp.host=smtp.avaldes.com
mail.smtp.port=25
mail.smtp.auth=false
mail.smtp.starttls.enable=false
mail.smtp.starttls.required=false
mail.debug=true

Testing out our Spring MVC Email Application

Download the Complete Source Code

That’s It!

I hope you enjoyed this tutorial. It was certainly a lot of fun putting it together and testing it out. Please continue to share the love and like us so that we can continue bringing you quality tutorials. Happy Coding!!!

spring_mail_velocity

Related Spring Posts

  • Creating Hello World Application using Spring MVC on Eclipse IDE
    In this tutorial we will go into some detail on how to set up your Eclipse IDE environment so that you can develop Spring MVC projects. In this post, we will create our first Spring MVC project with the all to familiar “Hello World” sample program.
  • Spring MVC Form Handling Example
    The following tutorial will guide you on writing a simple web based application which makes use of forms using Spring Web MVC framework. With this web application you will be able to interact with the customer entry form and enter all of the required values and submit them to the backend processes. I have taken the liberty of using CSS to beautify and transform the HTML page from a standard drab look and feel to a more appealing view.
  • Spring @RequestHeader Annotation Example
    In this tutorial, we will discuss the different ways that Spring MVC allow us to access HTTP headers using annotation. We will discuss how to access individual header fields from the request object as well accessing all the headers by supplying Map and then iterating through the LinkedHashMap collection. We will also show you how to set the headers in the response object.
  • Spring MVC Exception Handling using @ExceptionHandler with AngularJS GUI
    Good exception handling is a essential part of any well developed Application Framework and Spring MVC is no exception — pardon the pun. Spring MVC provides several different ways to handle exceptions in our applications. In this tutorial, we will cover Controller Based Exception Handling using the @ExceptionHandler annotation above the method that will handle it.
  • Spring RESTful Web Service Example with JSON and Jackson using Spring Tool Suite
    For this example, I will be using Spring Tool Suite (STS) as it is the best integrated development environment for building the Spring framework projects. Spring is today's leading framework for building Java, Enterprise Edition (Java EE) applications. One additional feature that makes Spring MVC so appealing is that it now also supports REST (REpresentational State Transfer) for build Web Services.
  • Spring MVC RESTful Web Service Example with Spring Data for MongoDB and ExtJS GUI
    This post will show another example of how to build a RESTful web service using Spring MVC 4.0.6, Spring Data for MongoDB 1.6.1 so that we can integrate the web application with a highly efficient datastore (MongoDB 2.6). In this tutorial we will walk you through building the web service and NoSQL database backend and show you how to implement CRUD (Create, Read, Update and Delete) operations.
  • Building DHTMLX Grid Panel User Interface with Spring MVC Rest and MongoDB Backend
    In this tutorial we will show how easy it is to use DHTMLX dhtmlxGrid component while loading JSON data with Ajax pulling in data from the Spring MVC REST web service from our MongoDB data source. You will see how simple it is to create a visually appealing experience for your client(s) with minimal javascript coding.
  • Spring MVC with JNDI Datasource for DB2 on AS/400 using Tomcat
    In this tutorial we will discuss how to set up Spring MVC web services and configure a JNDI Datasource using Tomcat and connect to IBM DB2 Database on a AS/400. JNDI (Java Naming and Directory Interface) provides and interface to multiple naming and directory services.
  • Java Spring MVC Email Example using Apache Velocity
    In this tutorial we will discuss how to set up a Java Spring MVC RESTful Webservice with Email using Apache Velocity to create a Velocity template that is used to create an HTML email message and embed an image, as shown below, using MIME Multipart Message.
  • Implementing Basic and Advanced Search using Angular Material Design, Grid-UI, Spring MVC REST API and MongoDB Example
    In this tutorial we will discuss how to implement basic and advanced search techniques in MongoDB using AngularJS and Google’s Material Design with Spring MVC REST API backend. The advanced search user interface (UI) will use logical operators and build a JSON object which contains the search field name, boolean or logical operator and the search value.
  • Spring MVC Interceptor using HandlerInterceptorAdapter Example
    In this tutorial we will discuss how to use the HandlerInterceptorAdapter abstract class to create a Spring MVC interceptor. These interceptors are used to apply some type of processing to the requests either before, after or after the complete request has finished executing.

Please Share Us on Social Media

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *