Last Updated:

Creating email templates using XML

To date, the ability to send electronic messages is an integral part of any web-application. Basically, these are very specific types of messages - for example, messages that are sent to remind the user password, welcome messages, messages confirming orders, etc. Although the content of electronic messages varies from application to application, but the process of sending them rarely differs. You simply create a letter, send it to the mail server, and later the recipient takes it from there.

Table of Contents

When you're programming in Java, usually the JavaMail API (http://java.sun.com/products/javamail/) is used to do all the dirty work involved in connecting to a mail server and sending an email. Unfortunately, this API is extremely inconvenient to use (mainly due to the flexibility of e-mail as such), so if you are going to use it often enough, it will be much more convenient and reasonable to write a special shell for it. Depending on how you use it, the shell can be aimed at working with a specific type of email (to send letters confirming the password, for example), or it will work as usual: taking the subject of the message, the list of recipients and the body of the message as arguments.

Once you have created such a shell, you need to have a system for creating the messages themselves. Take, for example, messages reminding the user of their password if they accidentally forgot it. Almost all emails have a field for the subject of the message, a list of recipients, and the body of the message. When we send a password reminder email, the recipient's email address and password itself are usually retrieved from some kind of vault that contains information about user accounts. The subject field and the message body itself must be merged with the data from the database and stored somewhere. One of the main problems in designing such applications is to decide where it is better to store this kind of data. In many cases, these strings are stored in property files that are separate from your source code and provide a convenient localization option if necessary. This approach is used in many web applications to store templates for sending emails, but this approach is not entirely correct.

Here is a list of the main reasons why using property files is not the best way to store the strings that make up an email template:

  • Property files are displayed in a very simple data structure - key pairs and their values. This is not at all suitable if you want to bind many values to the same key. For example, an e-mail message might have four people in the To: field and three people in the Cc: field. This cannot be accomplished simply by using property files.
  • Property files have very strict formatting of their contents. Each key and its value must be on the same line. Thus, strings can cause a lot of unnecessary problems related to editing this file. For example, it would be very problematic to put the entire body of an e-mail message in one property (key=value pair). And if you need to enable hyphenation on another line within the value of this property, you will have to replace them with a special \n character.

There is an alternative approach to creating email templates. It's about using XML, and that's the approach we'll be looking at in this article. XML provides the ability to construct e-mail templates with a very flexible structure, and beyond that, it does not have the formatting restrictions that exist in property files. Therefore, with its help, we can very simply save sufficiently large lines. The only advantage of property files is that they are a little easier to work with than with XML documents. In the case of property files, it is much easier to upload a file and then access the properties without having to access the file. On the other hand, in the case of an XML file, it takes much longer to load and process. To do this, you must also use one of the many libraries for working with XML files, some of which come with Java.

This article and its accompanying examples are designed to try to simplify the process as much as possible for you by providing a common framework for creating email templates using XML documents and sending them. In this framework, we used the Common Digester package from the Jakarta project to process XML and the JavaMail API to send actual messages.

Email templates

Let's take a look at the format of the email templates themselves. Templates are regular XML files that contain the root element and the child elements of that element. The root element is <email>. The required child elements are <subject>, <body> and <from>. The optional elements are <to>, <cc> and <bcc>. If you've ever used an email client, you can easily match each of these elements to the email fields in your email program. A single template can have multiple instances of each of the optional elements. Therefore, you can easily include multiple addresses for each of the recipients of this message. Later, we'll look at how this all works and how this XML message pattern will be processed. Here's an example of a template file:

 <email>
     <from>kkk@domain.com</from>
     <to>someone@mail.com</to>
     <cc>someoneelse@mail.ru</cc>
     <bcc>litvinuke@tut.by</bcc>
     <subject>This is the subject of the email</subject>
     <body>This is the body of our email message.</body>
   </email>

Customizable templates

Property files provide one very useful feature. This is because you can use the MessageFormat class to replace the replacement IDs in the property file with real values while the application is running. For example, if you store error messages in a properties file, and one of those messages is "file not found," you can add the following property to the file:

  file.not.found.error=Ошибка, невозможно найти файл {0}.

Next, use the MessageFormat class as follows:

  
  ResourceBundle bundle = ResourceBun dle.getBundle("MyProperties", current Locale); 

  Object[] arguments = { "some_file. txt" };

  String newString = MessageFormat.format(
    bundle.getString("file.not.found. error"), arguments);

As a result, the newString variable will contain the string "Error, unable to find file some_file.txt.". We have brought the same functionality to our system. Because the MessageFormat class can work with any string, you can very easily insert the same replacement IDs into the subject and body elements of an email template XML.

Sometimes it may be necessary to insert personal information into your templates before sending emails. For example, you may need to include the name of the recipient in the body of the letter or, perhaps, even detailed information about the order made by him.

Our system easily copes with this task by processing the contents of the subject and body elements using MessageFormat. The trick is that this class accepts only one array of arguments, which will be used to handle both the subject field and the message body. For example, the contents of the subject element can contain {0}, {2}, and {3} identifiers, and the contents of the body element can contain {0}, {1} and {4}. We chose this approach because very often the same arguments are used in both the body and subject fields, and it also simplifies the list of parameters passed to EmailSender.

Template processing

Now that we have created the template, we are ready to start processing it. As you already know, there are many libraries designed to work with XML documents. One of them - Commons Digester, part of the Jakarta Commons project - was created initially as part of the Struts project, in order to provide a quick and easy way to parse the Struts configuration file. This tool provides a simple approach to mapping the elements of an XML file into a data structure using syntax similar to XPath (http://www.w3.org/TR/xpath).  Its main advantage is that it allows you to strip the necessary elements from XML documents without having to parse them node by node using SAX or create a tree-like data structure, as the DOM does.
The following is a method that reads data from an XML file and copies it to an EmailTemplate object:

  public static EmailTemplate getEmail Template(InputStream aStream)
  {
    Digester digester = new Digester();
    digester.setValidating(false);
    digester.addObjectCreate("email", EmailTemplate.class);

    digester.addBeanPropertySetter ("email/subject", "subject");
    digester.addBeanPropertySetter ("email/body", "body");
    digester.addBeanPropertySetter ("email/from", "from");
    digester.addCallMethod("email/to", "addTo", 0);
    digester.addCallMethod("email/cc", "addCc", 0);
    digester.addCallMethod("email/bcc", "addBcc", 0);

    try
    {
        return (EmailTemplate)digester. parse(aStream);
    }
    catch (IOException e)
    {
        logger.error("Error: ", e);
        return null;
    }
    catch (SAXException e)
    {
        logger.error("Error: ", e);
        return null;
    }
  }

Now let's look at each of the lines of this example. Working with Commons Digester is to create a set of rules that will later be applied to the file that will be processed. Before setting these rules, we first set the XML document validation flag to false because we did not create or bind any DTD files to our XML file to check for the validity of our template structure. To start processing the file, create an object of the Digester class and then call the methods to set the rules for displaying the data. First, we call the addObjectCreate() method, which sets the rule for creating an EmailTemplate object as soon as we encounter an email element. The email element is the root element of our template, so each template file will be displayed in a single instance of the EmailTemplate class.

For elements that appear in our template only once, we used the addBeanPropertySetter() method. It takes two arguments: the path to the item that will be processed, and the set method that will display the contents of that element in the EmailTemplate object. In the first call, we indicated that the contents of elements that match the specified pattern ("email/subject") should be passed to the set-field method of the subject object of the EmailTemplate class. The specified pattern determines the nesting order of the elements separated by the symbols / and by which the element should be searched. In our case, the specified template corresponds to the subject element, which is a child of the email elements. When specifying such search patterns, you can also use wildcards, which can provide a more flexible search experience.

For items that might occur repeatedly in an e-mail template, calling set methods is not appropriate for these properties. Instead, we used the addCallMethod() method, which takes the contents of the element and calls a special method. We used a version of this method that takes three arguments. This is the pattern to match, the method to call, and the number of arguments that will be passed to that method. In all three cases, we specified 0 as the third argument, because only the contents of the found element would be passed to the method. In the EmailTemplate class, we wrote three methods: addTo(), addCc(), and addBcc(), which add a list of message recipients from the template file to the collection of the EmailTemplate class.

Once the rules are established for all six types of child elements of the XML e-mail template, we can proceed directly to parse our file. To do this, use the InputStream associated with the XML document file that is passed as an argument to the getEmailTemplate() method. The parse() method can take as an argument a File, InputSource object from SAX, InputStream, Reader, or a URI string that specifies the location of the file to be processed. We chose a version of the parse() method that takes an InputStream object as an argument.

The parse() method can throw IOException or SAXException exceptions. If any of these exceptions occur, we catch it, log it with log4j, and return null. If no exceptions occur, the getEmailTemplate() method will return a new instance of the EmailTemplate class that will be generated using the Digester class.

Rest of the EmailTemplate class

 

The most significant part of the EmailTemplate class is undoubtedly the getEmailTemplate() method. Everything else is just various properties and methods, designed more to make working with the class more convenient. So, this class has three properties of the String class: subject, body, sender address, as well as other properties that are stored in the structured ArrayList class: mailing lists, CC fields, and BCC. For each of these properties, the EmailTemplate class provides set- and get-methods: getToAddresses(), getCcAddresses(), and getBccAddresses(). The JavaMail API expects you to pass it old-style addresses as an array of InternetAddress objects. These methods also take care of converting ArrayList objects into an array of objects that the JavaMail API requires.

EmailSender

 

Now that we've successfully parsed the template file and got the emailTemplate class object ready, the next step is to send the email. The EmailSender class includes one static overloaded method, sendEmail(). Here's its signature:

  public static void sendEmail(
    String aTo,
    EmailTemplate aTemplate,
    String[] aArgs)

The arguments of this method probably do not require long explanations.
The first is the To: field of the e-mail message. Basically, you can set this field directly in the message template itself, but very often the recipient of the message is determined at the stage of the application. For example, if you send a message - a password reminder, then it should be addressed to the user who made the corresponding request. It is advantageous to strictly prescribe the recipient's address in the e-mail message template, for example, when the system needs to send a message for testing or for some system needs. For example, suppose a system needs to generate and send an e-mail message that switches the sequence of actions to be performed each time a corresponding request is sent. In this case, of course, it is better to strictly register the recipient's address in the message template.


The second argument is the EmailTemplate object itself.


And the third is the list of arguments that will be passed to the MessageFormat class when it processes the subject field and the body of the message. To do this, there is a special part of the code that creates an array of information that is used to personalize the template of the e-mail message. There are also several other declared methods that serve to make it easier to call this method. That way, you can call it without specifying the recipients or without any arguments at all.

The body of the sendEmail() method mainly consists of calls to the JavaMail API methods to configure the necessary parameters and send a message. First, we check to see if the EmailTemplate object is null. If equal, we do nothing. Otherwise, the first step in setting the parameters is to create a Properties property object (improved Hashtable) with smtp server settings.

After that, we create a Session object from the JavaMail API package and pass it to the constructor the Properties object created earlier with the SMTP server settings. The Session object is needed to create a MimeMessage object, which is what we do. Now set the From: address field to the address defined in the EmailTemplate object, which we pass to this method as an argument. The next step is to set the To: field.

Since all CC: and BCC addresses are defined within the template, processing them will not cause any problems. You just need to use the appropriate methods of the EmailTemplate class to add additional message recipients to the message itself.

As mentioned earlier, we use MessageFormat to apply all the arguments passed to the method to the subject and body of the message.


After that, you just need to copy the received subject and body of the message to the message object. Now all you have to do is call the Transport.send() method and pass it a MimeMessage object.


Using the framemoork


Now let's find out how to use this system. We'll look at how the servlet works, although it should work in any other normal program. The following code clearly shows the work with our system:

 // The email template
   InputStream template =
     getServlet()
         .getServletConfig()
         .getServletContext()
         .getResourceAsStream(
         "/WEB-INF/email/registration Notification.xml");

   EmailTemplate notification = Email Template.getEmailTemplate(template);

   //Create an email section containing the actual user data
   String[] args = { "Rafe" };

   EmailSender.sendEmail("email@domain.com", notification, args);

First, using the system functions, we get the InputStream associated with the template file, represented as an XML document. Since we are using a servlet, we get the file from ServletContext. There are, of course, many other ways to get the Input-Stream associated with this file, but in the case of the servlet environment, this option is more appropriate than any other. After that, all we need to do is pass the resulting inputStream object to the EmailTemplate method. getEmailTemplate(), which we described earlier. Then we simply define an array with arguments to configure the email and call the EmailSender method. sendEmail().

Instead of a conclusion

There are a number of improvements that can be made to this system. The two most obvious are the addition of the ability to send both HTML and regular emails, as well as the addition of the ability to send attached files along with the message.

To create this kind of message, just use messages like javax.mail.MimeMultipart.