Tuesday, August 10, 2010

Embedding SMTP server in Seam for testing

Developîng enterprise web applictions sometimes involves implementing email functionality within the application. There is often a need to send out automated emails from the application.

In a lot of cases, there is no SMTP server available to test this email communication. In the company I'm currently working for, SMTP servers are available on most non-production environments, however it's impossible to access them from a development PC. As most of the development occurs on those development PCs, the ability to test emails is vital, as the turnaround time needs to be a short as possible. (code to test).

In additionan to that, email functionality doesn't lend itself well for unit testing. Offcourse it is possible to mock your email service, but at some point you also want to verify (even on a local development pc) that your email is working.





Although I consider TDD one of the most valuable software development techniques, with web development we also resort to manual page testing in the browser to verify functonality. A certain use-case can involve executing business-logic, and sending out an email before being able to continue with the use-case. Obviously, we don't want get stuck at a "Cannot connect to email server" message in our browser at that point.

As far as testing goes, we have the following approaches:

  • Mocking in a unit test using a mocking framework like Mockito
  • Integration testing using SeamTest to validate the email rendering
  • Using an embedded SMTP server to verify end-to-end email functionality.

We'll be focussing on the third option here. In the example below I'll be using Seam (and it's built-in email functionality) as a test-case for an embedded SMTP server. The advantage of using Seam is that we can make full use of email templating using xhtml pages. See the email section in the Seam manual for more information on this.

Setting up the environment
Configuring Tomcat - Email libraries
As discussed here, in the Install the JavaMail libraries section, you need the following 2 JARs in $CATALINA_HOME/common/lib
  • mail.jar
  • activation.jar
Configuring Tomcat - Context
Defining the following element will register the JavaMail Session.
<resource name="mail/Session" auth="Container" type="javax.mail.Session" host="smtp.server">
</resource>

Later on, we'll see how our application will this resource through a web.xml entry.
<resource-ref>
<res-ref-name>mail/Session</res-ref-name>
<res-type>javax.mail.Session</res-type>
<res-auth>Container</res-auth>
</resource-ref>

Configuring Seam - components.xml
<mail:mail-session session-jndi-name="java:comp/env/mail/Session"/>

Seam Email component
Our Seam email component can be injected in any other Seam component. When invoking the sendMail method, the correct template (xhtml page) will be rendered.
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.faces.Renderer;

import be.telenet.releasemgmt.pmc.domain.communication.MailService;

@Name("mailService")
public class SeamMailService implements MailService {

@In(create = true)
private Renderer renderer;

@In
private FacesMessages facesMessages;

public void sendMail(String template) {
try {
renderer.render(template);
facesMessages.add("Email sent successfully");
} catch (Exception e) {
facesMessages.add("Email sending failed: " + e.getMessage());

}
}
}

Seam Email Template
The Seam email template is an xhtml page, allowing us to use the same templating technology as we use for our web pages. We have full access to the Seam scopes (via EL expressions) in these templates.
<m:message xmlns="http://www.w3.org/1999/xhtml"
xmlns:m="http://jboss.com/products/seam/mail"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">

<ui:repeat value="#{recipients}" var="recipient">
<m:to name="#{recipient.userId}" address="#{recipient.email}" />
</ui:repeat>
<m:from name="#{sender.userId}" address="#{sender.email}" />
<m:subject>Email Title</m:subject>
<m:body>
<h1>Email body, feel free to use HTML tags and EL Expressions here</h1>
</m:body>
</m:message>

Wiser Smtp Server Bootstrapper
We'll be using Wiser as the embedded SMTP server. We'll wrap it in a Seam component to have proper lifecycle management (start/stop), and also use it later on to have a view on the message processed by the SMTP server.
  • Starts the SMTP server (on port 25) after Seam startup.
  • Stops SMTP server when the Seam Application Context is released.
  • Provides access to the messages in our SMTP server
  • Contains a helper method to display the body of the message

@Name("wiserSmtpServer")
@Scope(ScopeType.APPLICATION)
public class WiserSmtpBootstrapper {

private Wiser wiser;

public Wiser getWiser() {
return wiser;
}

@Observer("org.jboss.seam.postInitialization")
public void startSmtpServer() {
this.wiser = new Wiser(25);
this.wiser.start();
}

@Destroy
public void stopSmtpServer() {
this.wiser.stop();

}

public List<WiserMessage> getMessages() {
return this.wiser.getMessages();
}

public String getBody(byte[] content) {
return new String(content);
}

}



Display email messages
Our SMTP Wrapper component (Seam component) that is put in application scope is also used to display the actual email messages.
<rich:dataTable id="emailList" value="#{wiserSmtpServer.messages}" var="email"
onRowMouseOver="this.style.backgroundColor='#F1F1F1'"
onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'">

<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_from}" />
</f:facet>
<h:outputText value="#{email.envelopeSender}" />
</rich:column>

<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_to}" />
</f:facet>
<h:outputText value="#{email.envelopeReceiver}" />
</rich:column>

<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_subject}" />
</f:facet>
<h:outputText value="#{email.mimeMessage.subject}" />
</rich:column>   

<rich:column>
<f:facet name="header">
<h:outputText value="#{messages.email_body}" />
</f:facet>
<h:outputText value="#{wiserSmtpServer.getBody(email.data)}" escape="false" />
</rich:column>

</rich:dataTable>

2 comments:

  1. Great! This certainly appears to be very informative. It's beneficial to know that SMTP server embedded in Seam can open the possibility of email functionality within the system. Would there be any instances where this method would not work as intended?

    Stanley
    Mark from chauffe eau gaz 

    ReplyDelete
  2. Since Office 2007, Microsoft changed their rendering engine from Internet Explorer’s to that of Word.

    email rendering

    ReplyDelete