Reference Guide

Overview
PostOffice Pattern
Download
Framework-Configuration
Templating
Server-Configuration
Sending the mail
The map
Localization
Loadbalancing
Fallback, Retry and Delay
For testing
Logging


Overview

In order to send e-mails with Java, you use the Java Mail API. Although it is working good, it is not really nice to use. Therefore the commons-email.jar of Apache was developed as a wrapper for the Java Mail API - and it really is much more fun using this than using the Sun's Java Mail API directly. Still it introduces no new features (like templating, multitenancy or localization).

Imagine you want an application with e-mail support. In almost all applications there is at least one e-mail template - either as plain text or in html format. This template includes a text and some key words which are substituted for values from the program when a e-mail is sent. Let's have a small example:

:address:

We have reset your password.
Your new password is: :newPassword:

With best regards,
FooBar
		

In this template the keywords :address: and :newPassword: are substituted for the address of the person and a newly created password. The source code for reading this file and making the substitutions is quite similar in every application. So templating is a good first step and for sure a better way than assembling whole texts using a StringBuffer directly in the source code (reminds me of the times when html code was assembled in Servlets...)

The second step is the multitenancy. Imagine the application is used by several departments (clients/mandants) within a concern and each of these departments wants to have a personal text in the e-mail. Perhaps one may even want html e-mails and another one plain text. A solution is having two templates (one for html and one for plain text) which are allocated to the clients in the source code:

if (user.getClient().equals("client1") ){
    sendPasswordHtml();
} else {
    sendPasswordPlain();
}
		

If the clients also want their e-mails localized, you need even more templates - with 2 clients, each having both English and German e-mails, we have a total of 4 templates per e-mail. This can lead to a lot of complex if-else-statements which are prone to errors.

As the requirements are quite similar in all applications, rather than writing the same code again and again, this code can be outsourced into an framework - and there we have the ploinMailFactory.

The ploinMailFactory uses the commons.email.jar of Apache and introduces the following new features:

  • Templating system
  • Multitenancy
  • Localization
  • Consistent repository for e-mail templates
  • Improved support for special characters
  • Load balancing for e-mail server
  • Fall back server definition
  • Retry function
  • Defining delay time between the retrys
The framework is open source (released under the Apache 2.0 license at SourceForge).

PostOffice Pattern

Before we give detailed explanations on the usage, let us first have some words to the basic approach used in the framework. Having the source code for sending e-mails being distributed all over an application is confusing and leading to errors - therefore we strictly advocate having the relevant portions of your source code concentrated at one point.

There are two situations which lead to varying realizations:

In some applications there is no connection with a database and instead persistence is realized by sending e-mails to other systems. In such an application you can have an extra layer in your application which handles the act of sending e-mails (each e-mail which can be sent by the application could be realized with an extra class (like a DAO for an entity in a normal persistence layer).

In most applications, you have a persistence layer handling the storage and retrieval of data to/from a database. Therefore, e-mails are only sent in a manageable number of cases. A good approach in such a situation is using the PostOffice Design Pattern - this means having a single class (PostOffice) in your business layer which is responsible for sending all e-mails. This class will have a method for each mail of your application.

In the following example we have two e-mails:

  • registrationMail
  • newPasswordMail

The PostOffice class could look like this:

public class PostOffice implements IPostOffice {

  public String sendRegistrationMail(String client, Locale locale, .. more params ..){
     doSomething();
     return "success";
  }

  public String sendNewPasswordMail(String client, Locale locale, .. more params ..){
     doSomething();
     return "success";
  }
}
		

Download

You have to download the jar-files from SourceForge. In order to use the framework in one of your applications, you have to add the ploinMailFacotry.jar and the dependencies in the lib directory to the classpath of the application.

http://sourceforge.net/projects/ploinmailfactor/

Framework-Configuration

The ploinMailFactory expects a file "mail.properties" located in your root source-folder, where your hibernate.properties and log4j.properties are placed, too. This is the central configuration file for the ploinMailFacotry.

Here is an example file:

propFileName=mail.properties

mailDirectory=service/businesslogic/mail

htmlExtension=.html

plainExtension=.txt

singleThread=false
		
propFileName The name for further configuration files (overriding / extending the main configuration)
mailDirectory The path to the directory (now called "mail directory"), where the e-mail templates are placed
htmlExtension The extension for the html templates
plainExtension The extension for the plain text templates
singleThread If this property is false, the e-mail sending process is happening in the current thread. If this property is true, the ploinMailFactory will start a new thread for sending the e-mail. This behavior is very useful if you have multiple E-Mail-Server and long time-outs.

Templating

The mail directory, where the e-mail templates are placed, should include a subdirectory for each client/mandant (now called "client directories") simply being named like the client. With 2 clients client1 and client2 we would have the following directory structure:

service/businesslogic/mail
service/businesslogic/mail/client1
service/businesslogic/mail/client2 
 		

The ploinMailFactory framework searches for the right template using a simple mechanism. First it looks for a template in the client directory. If it can't find one, it looks in the mail directory. Lastly, if it can't find one in the mail directory, too, it looks in the root classloader.

E-Mail templates equal for all clients/mandants should be placed in the main direcotry (service/businesslogic/mail). E-Mail templates specified for just one client/mandant should be placed in the client directory (service/businesslogic/mail/client1 or service/businesslogic/mail/client2).

A template has to end with .html (html template) or .txt (plain text template). If there are templates with both endings, the framework sends a multipart e-mail with both html and plain text. E-mail clients which cannot (or must not) show html e-mails, should show the plain text part.

Here is an example for a "InfoMail" equals for all clients/mandants.

:dear:

you are out of budget.

With best regards,
FooBar
		

Server-Configuration

The e-mail server settings to be used for sending the mails is set in a properties file "mail.properties" (the name may vary, depending on the propFileName-parameter in the main "mail.properties"-file in the root), placed in the the same direcotry there the mail tmeplate file.

loadbalancing.nodes=server1

server1.host=your.email.server
server1.fromEmail=foo@bar.org
server1.fromName=Foo Bar
server1.authUser=foo
server1.authPassword=bar
server1.replyTo=fo@bar.org
		

Like the e-mail templates, the settings also support multiple clients using the same mechanism. So if you need different settings for each client, put the "mail.properties" in the client directories, whereas you can use a single properties file in the main directory if you want to use the same settings for all clients.

Sending the mail

The most important class of the framework is the org.ploin.pmf.impl.MailFactory. Your PostOffice class should hold a reference to an instance of this class. Best would be to realize both classes as Singletons or alternatively give them Singleton scope with an IoC container like Spring:

<bean id="mailFactory" class="org.ploin.pmf.impl.MailFactory" init-method="init" />
<bean id="postOffice" class="org.company.project.java.service.businesslogic.impl.PostOffice">
---------<property name="mailFactory" ref="mailFactory" />
</bean>

Don't forget to call the init() method after the instanciation.

For sending a template, the MailFactory class provides this method:

public SendingResult sendMail(MailConfig mailConfig, TemplateConfig templateConfig);
 		

The first parameter of the method is the MailConfig object, it contains the recievers and the subject. The second parameter is the tmeplateConfig object, it contains the client/mandant, locale, the name of the email-template and a map for substitution. A valid call would be.

MailConfig mailConfig = new MailConfig();
mailConfig.addToRecipient("John Doe", "john@doe.com");
mailConfig.setSubject("InfoMail");

TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setName("InfoMail");
Map<String, String> map = new HashMap<String, String>();
map.put(":dear:", "Dear John Doe");
templateConfig.setMap(map);

mailFactory.sendMail(mailConfig, templateConfig);
		

If a template is found (using the mechanism described above), template parameters will be replaced with the values from the map and the e-mail will be sent.

The map

But that's not all. You can also access the values in the mail.properties using JSTL style accessors in your Java code. Say all e-mails include a link to your application. So you add the following line to the "mail.properties".

mail.link=http://www.example.com
		

In your code you simply use the following line:

map.put(":link:", "${mail.link}");
		

Your template could look like this:

:dear:

you are out of budget.

With best regards,
FooBar
:link:
		

The framework starts looking for the property in the "mail.properties" in the client directory. If this file does not exist or if it does not include the specified property, the framework looks at the mail properties in the mail directory.

Localization

Localized versions of your e-mails are realized similar to Java's concept of resource bundles. You just have to add the locale to your e-mail templates like this:

InfoMail_en.html
InfoMail_de.html
		

To tell your code which template to use, you have to add the locale to the templateConfig object.

TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setLocale(new Locale("de", "DE"));
		

If the framework cannot find the template for the specified locale it uses the default (unlocalized) template.

Loadbalancing

If you have two e-mail servers in your intranet, you can load-balance between them. You just have to add another smtp node. Here is an example configuration:

loadbalancing.nodes=server1, server2

server1.host=your.email.server0
server1.fromEmail=foo@bar.org
server1.fromName=Foo Bar
server1.authUser=foo
server1.authPassword=bar
server1.replyTo=fo@bar.org

server2.host=your.email.server1
server2.fromEmail=foo@bar.org
server2.fromName=Foo Bar
server2.authUser=foo
server2.authPassword=bar
server2.replyTo=fo@bar.org
		

The ploinMailFactory will now use either the first or the second e-mail server for sending a e-mail. Both e-mail servers will be used.

Fallback, Retry and Delay

Sometimes, an e-mail server has crashed or is not available. For this case you can define multiple fallback-servers. If the fallback-server fails too, you can define retry times and the delay (in milliseconds) between the retrys.

loadbalancing.nodes=server1, server2
fallback.nodes=fallback1, fallback2, fallback3

server1.host=your.email.server0
server1.fromEmail=foo@bar.org
server1.fromName=Foo Bar
server1.authUser=foo
server1.authPassword=bar
server1.replyTo=fo@bar.org

server2.host=your.email.server1
server2.fromEmail=foo@bar.org
server2.fromName=Foo Bar
server2.authUser=foo
server2.authPassword=bar
server2.replyTo=fo@bar.org

fallback1.host=your.fallback.server1
fallback1.fromEmail=foo@bar.org
fallback1.fromName=Foo Bar
fallback1.authUser=foo
fallback1.authPassword=bar
fallback1.replyTo=fo@bar.org
fallback1.retry=3
fallback1.delay=10000

fallback2.host=your.fallback.server2
fallback2.fromEmail=foo@bar.org
fallback2.fromName=Foo Bar
fallback2.authUser=foo
fallback2.authPassword=bar
fallback2.replyTo=foo@bar.org
fallback2.retry=3
fallback2.delay=10000

fallback3.host=your.fallback.server3
fallback3.fromEmail=foo@bar.org
fallback3.fromName=Foo Bar
fallback3.authUser=foo
fallback3.authPassword=bar
fallback3.replyTo=foo@bar.org
fallback3.retry=3
fallback3.delay=10000
		

For testing

In a test-environment, it is useful to send all email to a test email account. For this reason you can set the propertys "toEmailOverride", "toCcOverride" and "toBccOverride" in your "mail.properties" in the class root:

toEmailOverride=test@mail.de
toCcOverride=test@mail.de
toBccOverride=test@mail.de
		

If this propertys are set, the list of "toRecipients", "ccRecipients" and "bccRecipients" in the mailConfig object will be replaced with the values from the properties file.

logging

The ploinMailFactory uses intern log4j for logging. You can set the log level to debug with the following line in your log4j.properties.

log4j.logger.org.ploin.pmf DEBUG
		

Or to ERROR with the following line:

log4j.logger.org.ploin.pmf ERROR