Velocity
Velocity is a java based template engine. It
can be used to generate dynamic web pages by accessing the java objects at
runtime.
Why Velocity Templates?
1. Very useful to
create the dynamic email templates depending on the data at runtime.
2. Allows you to
specify the email style using html <span style=”...”>.
3. Can have multiple
templates for sending notification emails in different languages
Email Notification using Velocity and Spring Framework
Spring eases the email notification implementation is easy to integrate with Velocity.Velocity Template for email Creation
The email html message body and subject can be
created on the fly using Velocity Templates. We can have two template files (.vm)
for email subject and message.
emailSubject.vm
User ${userDTO.userId} login acknowledgement
|
emailBody.vm
<html>
<body>
<span style="font-size:10pt; font-family:Trebuchet MS, Helvetica, sans-serif;">
This email is to acknowledge the login of User: ${userDTO.userName}
to the application.
</span>
</body>
</html>
|
In that the dynamic data will be represented by the placeholders
(for example, ${UserDTO.userId} and ${UserDTO.userName}) andat runtime they will
be replaced with the data from the java object (UserDTO) .
Prerequisites
Jars:
1. velocity-1.5.jar
-
Velocity engine
to load velocity templates
2. spring-context-support-3.0.5.RELEASE.jar
- Provides
implementation for mail sender, which is used to send email using SMTP protocol.
3. mail-1.4.1.jar
- Provides
implementation to create the mime style email message
4. spring-core-3.0.5.RELEASE.jar
- It’s the core
module which requires for all spring modules.
5. commons-logging-1.1.1.jar
6. log4j-1.2.14.jar
- for logging
purpose
7. spring-beans-3.0.5.RELEASE.jar
- Spring's core
beans and bean factory support
8. spring-context-3.0.5.RELEASE.jar
- Provides
implementation for the spring application context.
9. spring-asm-3.0.5.RELEASE.jar
10. spring-expression-3.0.5.RELEASE.jar
11. commons-collections-3.2.jar
12. commons-lang-2.6.jar
SMTP
Mail HostName:
Need to have a SMTP host name to send and retrieve email.
Configuration
notification-config.xml
1.
Define the
mailsender spring bean, which is used to send email using SMTP protocol.
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<!-- Set the mail server host, typically an
SMTP host. -->
<property
name="host"
value="${hostName}"></property>
</bean>
|
-
Where host is the
SMTP mail host name.
The
place holder is replaced with the value from the notification.properties
(hostname=<your SMTP mail host name>)
Note:
By default SMTP will use the default port 25.
If your mail host port is different then provide the port number using the
property port in the mailSender bean
configuration.
If
you want to have authentication before sending email, provide the user
credentials for the mail account (for example, user account details in GMAIL
mail host) define the mailSender bean with the following properties.
<bean
id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property
name="javaMailProperties">
<props>
<prop
key="mail.smtp.auth">true</prop>
<prop
key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
<property name="host" value="smtp.gmail.com"></property>
<property name="port" value="587"></property>
<property
name="username"value="username"/>
<property
name="password"value="password"/>
</bean>
|
Where
javaMailProperties is a properties
object used to set the JavaMail properties in the current JavaMail session.
Within that properties object provide all the required properties
1.
mail.smtp.auth: specifies whether you need to provide
auth. details (usename and password for the email account).
2.
mail.smtp.starttls.enable: states to use the TLS encrypted
connection.
Note: If you don’t provide these two
properties then the specified
username and password will not be sent to the mail server by the JavaMail
runtime for authentication; so will not be able to send email from application.
3.
Username- username for the account at the mail host.
4.
Password- password for the user account.
5.
Host – SMTP mail host.
6.
Port – port used to send email.
2. Define the
velocityEngine bean, which is used to load the velocity templates defined for
your notification email creation.
<bean id="velocityEngine"
class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property
name="resourceLoaderPath"
value="classpath:/"
/>
<property
name="preferFileSystemAccess"
value="false"
/>
</bean>
|
Note:
If it is a web application, the templates should be placed under a specific
folder in WEB-INF/classes folder (for example,/WEB-INF/classes/mailtemplates/).
3. Define a
notificationService bean, which is used to create and send the notification
using java mail, Velocity and Spring APIs.
<bean id="iNotificationService" class="com.felix.service.NotificationService">
<property
name="mailSender">
<ref
bean="mailSender"/>
</property>
<property
name="velocityEngine">
<ref
bean="velocityEngine"/>
</property>
</bean>
|
4. Define the
mailTemplate bean which has two properties which hold mail velocity templates
(emailSubject.vm and emailBody.vm).
<bean id="mailTemplate" class="com.felix.dto.MailTemplateDTO">
<property
name="mailSubjectTemplateFile"
value="emailSubject.vm"
/>
<property
name="mailBodyTemplateFile"
value="emailBody.vm"
/>
</bean>
|
Implementation
NotificationService.java
1. First you need to
create theJavaMail MimeMessage(which represents the email that you need to
send) by invoking the createMimemessage () method on the JavaMailSenderImpl.
MimeMessage message = mailSender.createMimeMessage();
|
2. Create a new instance of the MimeMessageHelper for the MimeMessage instance that you
have created.
MimeMessageHelper messageHelper = new MimeMessageHelper(message); |
Note: MimeMessageHelper is a helper class for easy population of the given
javax.mail.internet.MimeMessage
. It provides setter
methods to set the underlying MimeMessage properties. For example, setter
methods for to, cc, bcc, subject etc.
3 Set the MimeMessage properties using
the MimeMessageHelper instance, using the details from the NotificationDTO.
messageHelper.setTo((notificationDTO.getTo()));
messageHelper.setSubject(VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
mailTemplateDTO.getMailSubjectTemplateFile(), model));
String text =
VelocityEngineUtils.mergeTemplateIntoString(velocityEngine,
mailTemplateDTO.getMailBodyTemplateFile(),
model);
|
VelocityEngineUtils is a utility class to merge the velocity
template with the model (map) to generate the email subject and message
dynamically at runtime. The model is a map with key as the string and value as
the model object. The velocity template location will get from the
velocityEngine instance.
The
place holders in the template files should be {model key name}.{property names
of the model object}. For example:
Model object
Map<String, Object> model = newHashMap<String, Object>();
model.put("userDTO",
userDTO);
model.put("notificationDTO",
notificationDTO);
emailSubject.vm
User
${UserDTO.userId} login acknowledgement
|
After
merging it will replace the placeholders with the actual value and generate the
subject and message string. For example, it generates the subject string asUser 1234 login acknowledgement, where
1234 is the userId.
4. Send the MimeMessage using the send()
method of JavaMailSenderImpl
this.mailSender.send(message); |
Notification Client
If
it is a web application, you can invoke the Notification Service from any of
your service classes or controller class.
A
standalone sample client to invoke the NotificationService to send
notification:
public static void main(String[] args) {
Logger
log = Logger.getLogger(NotificationDriver.class);
URL
url = org.apache.log4j.helpers.Loader
.getResource("log4j.properties");
PropertyConfigurator.configure(url);
log.info("Log4j using URL = " + url);
ApplicationContextapplicationContext
= new
ClassPathXmlApplicationContext(
"notification-config.xml");
INotificationServiceiNotificationService
= (INotificationService) applicationContext.getBean("iNotificationService");
MailTemplateDTO
mailTemplate = (MailTemplateDTO) applicationContext
.getBean("mailTemplate");
MailTemplateDTOmailTemplateSpanish
= (MailTemplateDTO) applicationContext.getBean("mailTemplateSpanish");
String[]
toArray = { "test@gmail.com" };
NotificationDTO
notificationDTO = new NotificationDTO();
notificationDTO.setFrom("admin@gmail.com");
notificationDTO.setTo(toArray);
UserDTO
userDTO = new UserDTO();
userDTO.setUserID("1234");
userDTO.setUserName("Felix");
userDTO.setUserAddress("Bangalore");
Map<String,
Object> model = newHashMap<String,
Object>();
model.put("userDTO", userDTO);
model.put("notificationDTO", notificationDTO);
try {
iNotificationService.sendEmail(notificationDTO,
mailTemplate, model);
//To send a email notification on spanish
/*iNotificationService.
sendEmail(notificationDTO,mailTemplateSpanish, model);*/
}
catch
(NotificationServiceExceptionnotifException) {
log.error("Error occured while sending email
notification",
notifException);
}
}
|
Issues Faced
1. Exception in thread "main"
java.lang.NoClassDefFoundError:
org/springframework/asm/ClassVisitor
org/springframework/asm/ClassVisitor
Solution:
Include spring-asm-3.0.5.RELEASE.jar in the classpath.
2. Exception in thread "main"
java.lang.NoClassDefFoundError: org/springframework/expression/PropertyAccessor
Solution:
Include spring-expression-3.0.5.RELEASE.jar
3. Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'velocityEngine' defined
in class path resource [notification-config.xml]: Invocation of init method
failed; nested exception is java.lang.NoClassDefFoundError:
org/apache/commons/collections/ExtendedProperties
Solution:
Include commons-collections-3.2.jar
4.
org.apache.velocity.exception.ResourceNotFoundException: Unable to find resource 'emailSubject.vm'
Solution: As the exception
says it cannot find the template file using the ClasspathResourceLoader. So
replace the velocityProperties with two properties resourceLoaderPath and preferFileSystemAccess.
It solves me the issue.
<beanid="velocityEngine"
class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<propertyname="resourceLoaderPath"value="classpath:/"/>
<propertyname="preferFileSystemAccess"value="false"/>
<!--
<property
name="velocityProperties"><value>
resource.loader=classpath class.resource.loader.class=org.apache.velocity.runtime.resource
.loader.ClasspathResourceLoader</value></property>
-->
</bean> |
5. Exception in thread "main" org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException
Solution: This is because
you haven’t provided the correct email user account credentials (username and
password) for authentication. If the SMTP host used is a Gmail SMTP host, then
provide a proper Gmail user account details for authenticating it against the
Gmail host before sending the email, for example, <name>@gmail.com and
its password.
6. Email is not delivered to the
provided Gmail account.
Soultion:
Soultion:
· Provide mail.smtp.auth and
mail.smtp.starttls.enable as true.
· Provide proper hostname and port (smtp.gmail.com, 587)
· Provide the correct user account
credentials for authentication
Provide the proper email address in the
‘to’ email address field.