Wednesday, September 7, 2011

How to send email from a NatJet WebApp

Having to send an email, it is a quite common task for a Web App : it may be a receipt, a confirmation message… This kind of email are automatically sent by the web application : there is no need for a human interaction.

This post is a tutorial on how to send an email from a NatJet application.

Architecture

The solution will rely on :

  • NatJet 4.0
  • Spring
  • JavaMail.

Testing will be achieved using jUnit 4.0.

Introducing JavaMail

The main Java library to send email is JavaMail : This is the Oracle’s (former Sun) standard to send email. JavaMail allows a Java application to behave as a client mail : you still need a mail server as Microsoft Exchange, SendMail or any email services like GMail or Yahoo Mail.

JavaMail behaves as an abstraction layer on top of different mail protocols as IMAP or SMTP that allows communication between a client mail and its server.

NatJet 4.0 Architecture with Spring

NatJet 4.0 includes Spring 3.0. Spring includes some services to configure and use easily JavaMail.

As always with Spring, this solution is a nice and easy way to separate the configuration of the communication between the client and the server from you application code.

The Bean org.springframework.mail.javamail.JavaMailSenderImpl from Spring will allows the configuration of the service. It’s part of NatJet 4.0.

Remember that the NatJet application and NatJet application context are two NatJet beans created by Spring : so it will be quite easy to inject the Spring mail service inside a NatJet application.

Spring Beans

The solution relies on 3 beans

  • mailSender : this is the Spring Bean that handles the JavaMail configuration. The implementation is org.springframework.mail.javamail.JavaMailSenderImpl as seen before.
  • sendMailManager : this is a bean that we will develop to render the service of sending an email. This bean relies on the previous bean. This is where we will code using the JavaMail API
  • NatJet Application bean (called applicationInstance) : This bean can be simply retrieved from NatJet with the getApp() method. That’s why we will simply inject the bean sendMailManager there allowing an easy access to it.

 

Configuration of the Project

The first step is to create a NatJet project for the demo. Then we will add the necessary JavaMail jars. And we will configure Spring to create and inject the correct beans.

Creating a new NatJet Project

In the NatJet Perspective, select in the File menu New –> NatJet Project Wizard. The name of the new project will be NatJetMail. Press 3 times the button Next to get the “Configure NatJet Project” screen.

Select Custom Package root and enter the following indication :

  • Package root : fr.natsystem.demo.email
  • Main Module Name : NatJetMail
  • Package name for the main Module : fr.natsystem.demo.email.ui

TE02Creationprojet

And then press the Finish button.

The project is created.

Add JavaMail Jar

For the demo, we need to download JavaMail.jar and the Activation.jar if you use Java 5.0 as I do.

Go to the Oracle website : www.oracle.com/technetwork/java/javamail. Select on the right part the Download link.

I’ve used the JavaMail 1.4.4 version. Press the Download button. Read and accept the Licence Agreement to get access to the link : javamail1_4_4.zip. Download and save the file.

TE07ConfigureDownload

Extract the file, we will use only the jar file mail.jar in javamail1_4_4.

If you are using Java 5.0 we need as specified on Oracle website to download the JavaBean Activation Framework. The best, is starting from www.oracle.com/technetwork/java/javamail, click on link “Javabeans Activation Framework (JAF)”.

Then on the new page, select JAF_1.1.1 link. On the next page, select Download button. On the new page, Accept License Agreement et click on link jaf-1_1_1.zip.

Download and save on your disk, then extract files. We will use only the jar file activation.jar in jaf-1_1_1.

To add the two jars to your NatJet project, in Eclipse open the node WebContent/WEB-INF/lib. Select the jar mail.jar in javamail1_4_4 in the Windows Explorer and drag and drop it on the lib node. Do the same with the file activation.jar in jaf-1_1_1.

TE08ConfigureAjoutMail.jar

You’ve just added two new jars in your NatJet project :

  • activation.jar
  • mail.jar

 

Defining beans in natjetInstanceContext.xml

In WebContent/WEB-INF you can find several configuration xml file for Spring. We will modified natjetInstanceContext.xml to add there the declaration of the two new beans : mailSender et sendMailManager.

This is also where you can find the declaration of the NatJet Application bean applicationInstance that we need to modify : we need to inject the new sendMailManager bean.

In NatJetMail project select natjetInstanceContext.xml node in WebContent/WEB-INF, click right and select Open with… –> XML editor.

The XML file is opened. Modify the file as following (new or modified sections are in bold).

Order of beans are important, so we need to add the two new beans declaration before the import declaration.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- <bean/> definitions here –>

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="smtp.gmail.com"/>
<property name="username" value="myemail@mycompany.fr"/>
<property name="password" value="mypassword"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">true</prop>
<!-- used by gmail smtp server -->
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>

<bean id="sendMailManager" class="fr.natsystem.demo.email.servicemail.SendMailManager">
<property name="mailSender" ref="mailSender"/>
</bean>


<import resource="ContextBean/emptyContextBean.xml" />


<bean id="applicationInstance" class="fr.natsystem.demo.email.NatJetMail"
        scope="prototype"> 
   <property name="context"> 
        <ref bean="natjetSessionContext" /> 
   </property> 
   <property name="sendMailManager" ref="sendMailManager"/>         
</bean>

<bean id="mainLayout" class="fr.natsystem.demo.email.ui.Layout3Panels"
        scope="prototype"> 
</bean>
</beans>

Be careful ! You need to adapt at least the username and password fields, but you may need to change more parameters depending of your email server.

For GMail or Google Apps Gmail, username is your email : myaccouunt@gmail.com for a  personnal gmail account or myemail@mycompany.fr if you are using Google Apps. The password is the one of your email account.

Save and close the file.

Modifying Application Class : NatJetMail.java

In the src node of your project, you can find the Application class NatJetMail in fr.natsystem.demo.email.

We need to add a new property and setter to store and retrieve the sendMailManager bean. Be careful to be consistent with the declaration we’ve made in the previous chapter : the name of the property in the java class should match the name of the property in the bean.

Edit the class and add the sendMailManager property and its getter and setter as follow.

public class NatJetMail extends AbstractNatJetMail {
    private static final long serialVersionUID = -4417253215596027617L;
    //Spring Bean : Email service
    private SendMailManager sendMailManager;
   
  public NatJetMail () {
    super();
  }
 
  public fr.natsystem.natjet.window.NsMainLayout initApp() {
    NsLogger.debug(this, " init application ");
    fr.natsystem.natjet.window.NsMainLayout natJetMainWindow = (fr.natsystem.natjet.window.NsMainLayout) NsConfig.getSpringBean("mainLayout");
    setMainWindow(natJetMainWindow);
    return natJetMainWindow;
  }

public SendMailManager getSendMailManager() {
    return sendMailManager;
}

public void setSendMailManager(SendMailManager sendMailManager) {
    this.sendMailManager = sendMailManager;
}
 
 
}

The class is in error as the SendMailManager as not yet been defined. Save and close.

Creating SendMailManager class

This is the main part of the coding. This is where we send email using JavaMail API.

We will do 2 samples :

  • a simple email message
  • a mail with an attachment as a JPEG file or a PDF file

Create the class SendMailManager

Select in the File menu, the New –> Class item.

Enter as follow :

  • Package : fr.natsystem.demo.email.servicemail
  • Name : SendMailManager

This has to map the Spring declaration of the bean sendMailManager.

TE05ClassMailManager

Press Finish button. The class is created, we need now to complete it.

We need, first to add the property we specified in the bean declaration : mailSender and it setter.

At this point, the class should look like this :

package fr.natsystem.demo.email.servicemail;

import org.springframework.mail.javamail.JavaMailSender;

public class SendMailManager {
    private JavaMailSender mailSender;

    public void setMailSender(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }
   
}

 

Update NatJetMail

Before continuing, edit the class NatJetMail.java and do an Organize Import (ctrl Shift O). Save. This will add the correct import for SendMailManager. You shouldn’t have any error left.

Method to send a simple mail

The first sample we are going to do is sending a simple email in which we will specify :

  • Recipient
  • Subject
  • Body

For this we will create the method sendSimpleMail:

public void sendSimpleMail(final String pEmail, final String pSubject,  final String pEmailText) {
    
     MimeMessagePreparator preparator = new MimeMessagePreparator() {
   
        public void prepare(MimeMessage mimeMessage) throws Exception {
   
            mimeMessage.setRecipient(Message.RecipientType.TO,
                    new InternetAddress(pEmail));
            mimeMessage.setText(pEmailText);
            mimeMessage.setSubject(pSubject);
        }
    };
    try {
        this.mailSender.send(preparator);
    }
    catch (MailException ex) {
        // simply log it and go on...
        System.err.println(ex.getMessage());           
    }
}

We need to add the following import declaration :

import javax.mail.Message;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;

Save the java class.

Using the service form NatJet

To use the service we will modify the MainPanel panel.

MainPanel.xml

In WebContent/resources/NatJetMail select the MainPanel.xml and double click on it to open it with NatJet editor.

We will add :

  • 2 labels : “Email recipient” and “Text email”
  • 1 Textfield emailTF for the email address with the Regex Type email.
  • 1 Textarea textTA for the text
  • 1 PushButton sendSimpleMailPB to send the email

It could look to the following panel :

TE03Ecran

Save the panel.

Calling the mail service from NatJet

Select the pushbutton Send, and in the Properties view select Executed event and press the arrow. The java code for MainPanel.java is displayed.

To complete the action, we need to retrieve the service bean to send email : this is quite simple as there is a method getApp() to retrieve the NatJet application instance. Then as we add a getter to the bean in this class, we have the bean and we just need to trigger the method handling the correct parameters :

  • emailTF.getText() to retrieve the email input by the user
  • For the subject we will use a static value : “Simple mail”
  • textTA.getText() to retrieve the text.

The java code of the method is as follow :

@Override
public void sendSimpleMailPB_ExecutedEvent(NsExecutedEvent arg0){
    getApp().getSendMailManager().sendSimpleMail(emailTF.getText(), "Simple mail", textTA.getText());
}

Save the java class.

Testing

Testing from NatJet Web App

First we will test from the NatJet Web App

At this point it is easy to test your NatJet Web App as usual : just select your NatJetMail node and select Run As >  Run on server. Select you server and run.

You should get an opened browser with your NatJet page.

Enter an email and a text message and press Send button. A quick “Please wait…” message may appear and then you should receive an email with subject “Simple mail”.

Testing from JUnit

As we build a service class, it can be efficient to test the service from JUnit.

The first step is to add the JUnit library to the project. Then we will build a JUnit test class and the test configuration file.

Adding JUnit 4 to a NatJet project

In the Package Explorer view, select the node of NatJetMail project and click right. Select “Build path” menu and “Add library…” menu item. The dialog “Add Library” is opened.

Select JUnit and press “Next>” button. On next screen, select JUnit 4 as JUnit library version, and then press Finish.

TE34junitAddJarJunit

A JUnit 4 node will appear in Package Explorer view under your project node NatJetMail.

Adding a JUnit test class

In the File menu, select New –> Other… The dialog New is opened.

Select JUnit Test Case in the folder JUnit and press Newt> button.

On the New JUnit Test Case dialog :

  • Select radio button “New JUnit 4 test”
  • Package : fr.natsystem.test.demo.email.servicemail : this naming convention allows to exclude easily all test package when packaging the final production delivery.
  • Name: TestSendMailManager

You can select class SendMailManager for class under test, but this will only create an empty test method for SendSimpleMail.

The class is created, then we will add some Spring plumbing :

  • First we will add the RunWith and ContextConfiguration annotations to tell JUnit that we will used Spring to run the test and use a Spring Configuration file
  • Then, we will add a Autowired property to store and retrieve the bean sendMailManager that we want to test.

To finish, we need to call the service. In our case, this is quite easy we just need to call the method with some parameters.

The final java code for the class is :

package fr.natsystem.test.demo.email.servicemail;

import static org.junit.Assert.fail;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import fr.natsystem.demo.email.servicemail.SendMailManager;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"EmailTestConfig.xml"})
public class TestSendMailManager {
   
    @Autowired
    private SendMailManager sendMailManager;

    /**
     * Test method for {@link fr.natsystem.demo.email.servicemail.SendMailManager#sendSimpleMail(java.lang.String, java.lang.String, java.lang.String)}.
     */
    @Test
    public final void testSendSimpleMail() {
        sendMailManager.sendSimpleMail("testaccount@mycompany.fr", "Suject : testSendSimpleMail",
"This is a simple JUnit test");
    }

}

Save the java class.

Creating JUnit Spring Configuration file

In the ContextConfiguration annotation we specified a file name : this is the configuration file Spring will use. This file need at least to specify the sendMailManager to allow the Autowired annotation to work.

Select the package fr.natsystem.test.demo.email.servicemail, then right click and select New –> Other… The dialog New is opened.

Select XML in the folder XML and press Newt> button.

Enter EmailTestConfig.xml in the file name and press Finish.

The file is created, closed the tab and reopen it with a right click and “open wit XML editor”.

Enter the following code:

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

    <!-- <bean/> definitions here
        <property name="loggerFormat" value="[%id:%ip] %M : ATtention %M est très couteux" />
    -->
    <bean id="nslogger" class="fr.natsystem.nslogger.NsLogger">
        <property name="loggerFormat" value="***[%id:%ip]" />
        <property name="storeRequest" value="true" />
        <property name="dumpRequest" value="false" />
        <property name="logElapsedTime" value="false" />
    </bean>
   
    <!-- Pour JUnit -->
    <bean id="nsconfig" class="fr.natsystem.nsconfig.NsConfig">
    </bean>

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
          <property name="host" value="smtp.gmail.com"/>
          <property name="username" value="testaccount@mycompany.fr"/>
          <property name="password" value="mypassword"/>
        <property name="javaMailProperties">
        <props>
            <prop key="mail.smtp.auth">true</prop>
            <!-- used by gmail smtp server -->
            <prop key="mail.smtp.starttls.enable">true</prop>
        </props>
        </property>
    </bean>


    <bean id="sendMailManager" class="fr.natsystem.demo.email.servicemail.SendMailManager">
          <property name="mailSender" ref="mailSender"/>
    </bean>
</beans>

Save the file.

Be careful to change at least the username and password properties of the mailSender bean.

Run JUnit test

This is as for all JUnit test, right click on the TestSendMailManager java class, select the menu Run As -> JUnit Test.

The test is launch and you should receive quickly a new email on your test account.

This test does not allow checking the result : thus you may have noticed that we didn’t use the assertEqual method. The only errors you can get is an exception or not receiving an email.

Sending an attached File

To finish, this tutorial we will add the case of sending an email with an attached file.

For this, we will create a new method in SendMailManager.java class and a new test method in TestSendMailManager java.

Sending email with attachment in JavaMail

Open SendMailManager.java and add the following method

public void sendMailWithAttachment( String pEmail,  String pSubject, String pEmailText,  String pFileName) throws IOException {

    // Retrieve the mimeMessage from Spring
    MimeMessage mimeMessage = mailSender.createMimeMessage();

    try {
        mimeMessage.setRecipient(Message.RecipientType.TO,
                new InternetAddress(pEmail));
        // In MultiPart email : setText in the MimeMessage is useless
        // It has to be done on one of the part (usually the first)
         mimeMessage.setSubject(pSubject);

         // --------------------------------Message Part
        MimeBodyPart mbp1 = new MimeBodyPart();
        mbp1.setText(pEmailText);
        // --------------------------------Attachment
        MimeBodyPart mbp2 = new MimeBodyPart();
        mbp2.attachFile(pFileName);

        // --------------------------------Merging message + Attachment
        Multipart mp = new MimeMultipart();

        mp.addBodyPart(mbp1);
        mp.addBodyPart(mbp2);

        mimeMessage.setContent(mp);
        // --------------------------------------------------------//
       
        // Sending the email
        this.mailSender.send(mimeMessage);


    }
    catch (MessagingException e) {
        NsLogger.error(this, "sendMailWithAttachment MessagingException:"+e.getMessage());
        NsLogger.printStack(e);
    }
}

Notice the instruction to retrieve a JavaMail MimeMessage : generally it is created by coding. Working with Spring we retrieve it from the mailSender bean with the correct initialization.

It is then possible to use all standard JavaMail API.

MimeMessage mimeMessage = mailSender.createMimeMessage();

The method can throw an IOException when the filename is incorrect.

Save the java class.

JUnit test.

We add to the JUnit test class 2 new methods :

  • one to test a correct use of the method
  • one to test the reaction with an invalid email address

 

@Test
public final void testSendAttachment() {
    try {
        sendMailManager.sendMailWithAttachment("testaccount@mycompany.fr", "Suject : testSendAttachment", "This is a  JUnit test with a file attached","C:/yourJpeg.jpg" );
    } catch (IOException e) {
        NsLogger.printStack(e);
        fail("incorrect file IOException"+e.getMessage());
    }
}

@Test
public final void testSendAttachmentInvalidEmail() {
    try {
    sendMailManager.sendMailWithAttachment("invalidemail", "Suject : testSendAttachmentInvalidEmail", "This is a  JUnit test with a file attached","C:/yourJpeg.jpg" );
} catch (IOException e) {
    NsLogger.printStack(e);
    fail("incorrect file IOException"+e.getMessage());
} catch (MailException e) {
    assertEquals("Check error message when invalid adress :"  ,"Failed messages: javax.mail.SendFailedException: Invalid Addresses;",e.getMessage().substring(0, 67));
    assertNull("testSendAttachmentInvalidEmail MailException getRootCause:",e.getCause());
}
}

The file C:/yourJpeg.jpg has to exist to work, thus you need to adapt your test with the correct email address (for the first one) and a valid filename for both.

Notice that in the second test we analyze the exception to confirm that it’s an invalid email address.

Conclusion

You have through a complete project using JavaMail, Spring and NatJet. The coding of JavaMail is standard programming minus the configuration part that can be handled transparently by Spring.