Saturday, December 28, 2013

Sending email using java mail and spring

It's been quite a while since my last post published and I did not have time to finish planed few blog post. Today I would like to demonstrate how to configure java mail and spring with real project. I did this configuration for my recent project.

How can we send email using java?

 There are lot of example codes you can find from google regarding email. You just need java mail api , email account , few line of codes and internet access to send email. But we cannot use these code directly for enterprise level application email. Those codes are not configurable, reusable , flexible or maintainable. So I used spring and java mail configuration for our application.

First you need to add java mail dependency to your maven pom file.


     javax.mail
     mail
     1.4.3


Now we need Bean configuration file for email.
spring-mail.xml

 
     
    
    
        
        
        
        
        
            
                smtp
                true
                true
                false
            
        
    
     
    
    
         
        
    

Import this spring-mail.xml file in your applicationContext.xml file.
I configured MailService class and it uses the beans configured in spring-mail.xml file and use them to send messages.
MailService.java

@Service("mailService")
public class MailService {

    @Autowired
    private JavaMailSender mailSender;
    @Autowired
    private SimpleMailMessage preConfiguredMessage;

    /**
     * This method will send compose and send the message
     *
     */
    public void sendMail(String to, String subject, String body) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(preConfiguredMessage.getFrom());
            helper.setTo(to.trim());
            helper.setSubject(subject);
            helper.setText(body,true);

        } catch (MessagingException ex) {
            ex.printStackTrace();
        }
        mailSender.send(message);

    }
}

This sendMail method takes mail receiver address ,message subject and the message. I can send email directly using this method.But in my application I have few locations to send email. So I decide write another class for those send email location and without adding these codes to business logic.
MailServiceSender.java

@Service("mailServiceSender")
public class MailServiceSender {

    @Autowired
    private MailService mailService;
    @Autowired
    private MailBodyCreator mailBodyCreator;

    public void sendUserCreateEmail(User user, String password) {
        String to = user.getEmail();
        String subject = "User Registration";
        String mailBody = mailBodyCreator.buildCreateUserEmail(user, password);
        mailService.sendMail(to, subject, mailBody);
    }

    public void sendUserPasswordChangeEmail(User user, String password) {
        String to = user.getEmail();
        String subject = "User Password Change";
        String mailBody = mailBodyCreator.buildForgotPasswordEmail(user, password);
        mailService.sendMail(to, subject, mailBody);
    }

    public void sendWebPackageAppointmentReciptEmail(PackageAppointment packageAppointment) {
        String to = packageAppointment.getEmail();
        String subject = "Package Appointment Receipt";
        String mailBody = mailBodyCreator.buildWebPackageAppointmentReciptEmail(packageAppointment);
        mailService.sendMail(to, subject, mailBody);
    }
    
    public void sendWebServiceAppointmentReciptEmail(ServiceAppointment serviceAppointment) {
        String to = serviceAppointment.getEmail();
        String subject = "Service Appointment Receipt";
        String mailBody = mailBodyCreator.buildWebServiceAppointmentReciptEmail(serviceAppointment);
        mailService.sendMail(to, subject, mailBody);
    }
    
    public void sendWebDoctorAppointmentReciptEmail(DoctorAppointment doctorAppointment) {
        String to = doctorAppointment.getEmailAddress();
        String subject = "Doctor Appointment Receipt";
        String mailBody = mailBodyCreator.buildWebDoctorAppointmentReciptEmail(doctorAppointment);
        mailService.sendMail(to, subject, mailBody);
    }
}

Now you can clearly see the email send locations and configurations.

 You can see there is a mailBodyCreator object in that code sample.I used another class for email message generation part. This class extends from abstract class called AbstractMailBodyCreator.It contains email header and body information method.

 AbstractMailBodyCreator.java

public abstract class AbstractMailBodyCreator {
    
    public String buildEmailHeader() {
        StringBuilder emailHeader = new StringBuilder();
        //Impement mail header
        emailHeader.append( "---------------------Auto generated email ---------------------");
        return emailHeader.toString();
    }
    
    public String buildEmailFooter() {
        StringBuilder emailFooter = new StringBuilder();
        //Impement mail footer
        emailFooter.append( "---------------------Do not reply to this email----------------");
        return emailFooter.toString();
    }
    }

I have added mail header and footer part to AbstractMailBodyCreator class because those parts are common to all message.

Now create MailBodyCreator class.
MailBodyCreator.java

@Component("mailBodyCreator")
public class MailBodyCreator extends AbstractMailBodyCreator {
    
    public String buildForgotPasswordEmail(User user, String password) {
        
        StringBuilder emailBody = new StringBuilder();
        emailBody.append(buildEmailHeader());
        
        emailBody.append("Dear ").append(user.getFirstName() + " " + user.getLastName()).append(",").append("");
        emailBody.append("This is a system generated email to notify that your password has been changed. ");
        emailBody.append("Your username is: ").append("").append(user.getUserName()).append("");
        emailBody.append("Your new password is: ").append("").append(password).append("");
        emailBody.append("");
        emailBody.append(" Please login with the new password given above.");
        emailBody.append(buildEmailFooter());
        return emailBody.toString();
    }
    
    public String buildCreateUserEmail(User user, String password) {
        StringBuilder emailBody = new StringBuilder();
        emailBody.append(buildEmailHeader());
        emailBody.append("Dear ").append(user.getFirstName() + " " + user.getLastName()).append(",").append("");
        emailBody.append("This is a system generated email to notify you that a 'User Account' has been created.");
        emailBody.append("Your username is: ").append("").append(user.getUserName()).append("");
        emailBody.append("");
        emailBody.append("Your password is: ").append("").append(password).append("");
        emailBody.append("");
        emailBody.append(" Please login with the user name and new password given above.");
        emailBody.append(buildEmailFooter());
        return emailBody.toString();
    }
}

Mail body of the message is important part of this project. So I decide to separate this mail body creation part from business logic and create new new class for that.
 Now we can test our email send code.

@Service
public class UserBoImpl implements UserBo {
    @Autowired
    private MailServiceSender mailServiceSender;
    
    @Transactional
    public String saveOrUpdateUser(User user, User logedUser) {
               //save logic

                try {
                    mailServiceSender.sendUserCreateEmail(user, password);
                } catch (Exception e) {
                    e.printStackTrace();
                } return “success”;
    }
}


There are better solutions than this. But I came up with this design.Using this design I achieve more configurability, reusability, flexibility and maintainability. I can change this email setting using spring-mail.xml file without recompiling.

One drawback of this is I cannot change email body template without changing the code. But we can use email template file for email body creation and that template can be change without code change. It will solve the problem.

Another thing we should consider when we send email is queuing. If we do not use queue for email, user have to wait to continue until email send. We can use queue server like Apache ActiveMQ for that.

2 comments :

  1. You have some good stuff pointed out here. It can really be useful. Love the designs. Thanks a lot for sharing. :)
    - 2020Connect.net

    ReplyDelete