Tuesday, September 29, 2009

Spring Mail API Wrapper

Recently it was needed at my working place to have a module to send SMTP mails. I found out that Spring provides a nice wrapper around the Java Mail API so you do not need to deal with boilerplate code. Hence after a few hours of Googling and coding I was able to come up with an API which sends mail using Spring. Following I give you the code snippets required.


package com.test.commons.mailservices.core.remoting.ejb.service;

import com.test.commons.mailservices.core.exceptions.MailSenderException;

public interface MailSenderService {

/**
* This method sends the mail to multiple recipients with the given subject
* @param mailMsg The message needed to be send as plain text
* @param recipientAddresses The mail addresses in an array to which the mail has to be sent
* @param subject The subject of the mail to be sent
* @throws MailSenderException this exception wraps the MailException throw by the Spring framework
*/
public void sendMessage(String mailMsg,String[] recipientAddresses,String subject)throws MailSenderException;

/**
* This method sends the mail to a single recipient with the given subject
* @param mailMsg The message needed to be send as plain text
* @param recipientAddresses The mail addresses in an array to which the mail has to be sent
* @param subject The subject of the mail to be sent
* @throws MailSenderException this exception wraps the MailException throw by the Spring framework
*/
public void sendMessage(String mailMsg,String recipientAddress,String subject)throws MailSenderException;

/**
* This method sends the mail to multiple recipients which are given as comma separated values, with the given subject
* @param mailMsg The message needed to be send as plain text
* @param recipientAddresses The mail addresses in an array to which the mail has to be sent
* @param subject The subject of the mail to be sent
* @throws MailSenderException this exception wraps the MailException throw by the Spring framework
*/
public void sendMessageWithCommaSeparatedMailAddresses(String mailMsg,String recipientAddress,String subject)throws MailSenderException;
}

This is the main contract which any calling party can use to send mails. The method is overloaded so that clients can call with different functionality. Next I show you the implmentation of this interface as well as the exception class that i have defined which wraps the MailException thrown by Spring. I wrapped it so that any calling party only needs to know about the MailSending module and not about Spring exceptions.


package com.test.commons.mailservices.core.remoting.ejb.bl;

import org.apache.log4j.Logger;
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;

import com.test.commons.mailservices.core.exceptions.MailSenderException;
import com.test.commons.mailservices.core.remoting.ejb.service.MailSenderService;

/**
* This is the Main class involved in sending email message to the outside world<br>
* The class uses the Spring Framework which wraps the java mail API to send mails.
* Common functionality of obtaining a SimpleMailMessage object is given by the parent<br>
* class named MailSenderCommon. The instance variables of the class are instantiated with<br>
* through a spring config. The spring config file responsible is mailconfig.spring.xml found <br>
* under the resources directory.
*
* @author dinuka
*
*/
public class MailSenderImpl extends MailSenderCommon implements MailSenderService {

private static final Logger log = Logger.getLogger(MailSenderImpl.class);

private MailSender mailSender;

private String fromAddress;

public MailSenderImpl() {

}

/**
* {@inheritDoc}
*/
public void sendMessage(String mailMsg, String[] recipientAddresses, String subject)throws MailSenderException {
if (mailMsg == null || recipientAddresses == null) {
log.error("Input parameters received for sendMessage(String,String[]) are null");
}

sendEmail(mailMsg, subject, recipientAddresses);

}

/**
* {@inheritDoc}
*/
public void sendMessage(String mailMsg, String recipientAddress, String subject)throws MailSenderException {
if (mailMsg == null || recipientAddress == null) {
log.error("Input parameters received for sendMessage(String,String) are null");
throw new IllegalArgumentException("Received Input Parameters Are Null");
}
sendEmail(mailMsg.trim(), subject, recipientAddress);
}

/**
* {@inheritDoc}
*/
public void sendMessageWithCommaSeparatedMailAddresses(String mailMsg, String recipientAddress, String subject)throws MailSenderException {

if (mailMsg == null || recipientAddress == null) {
log
.error("Input parameters received for sendMessageWithCommaSeparatedMailAddresses(String,String) are null");
throw new IllegalArgumentException("Received Input Parameters Are Null");
}
sendEmail(mailMsg, subject, recipientAddress.split(","));

}

public MailSender getMailSender() {
return mailSender;
}

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

public String getFromAddress() {
return fromAddress;
}

public void setFromAddress(String fromAddress) {
this.fromAddress = fromAddress;
}

private void sendEmail(String mailMsg, String subject, String... address)throws MailSenderException {

try {
mailSender.send(getSimpleMessage(mailMsg, fromAddress, subject, address));
} catch (MailException ex) {
log.error("MailSenderImpl: sendMessage() Exception occured" + ex.getMessage());
throw new MailSenderException(ex.getMessage());
}
}

}





package com.test.commons.mailservices.core.remoting.ejb.bl;

import java.util.Date;

import org.springframework.mail.SimpleMailMessage;

public class MailSenderCommon {

/**
* This is the common method which creates a simple message object which is used to send mails out
* Note that var arg is used as the second parameter to facilitate String[] and normal String object parsing
* to the same method. Also please note that you should always keep the var arg parameter as the last parameter in
* this method as the specification requires it.
* @param mailMsg
* @param addresses
* @return
*/
protected SimpleMailMessage getSimpleMessage(final String mailMsg, final String fromAddress,String subject,final String... addresses) {
SimpleMailMessage message = new SimpleMailMessage();
message.setSentDate(new Date());
message.setTo(addresses);
message.setFrom(fromAddress);
message.setSubject(subject);
message.setText(mailMsg);


return message;
}

}




package com.test.commons.mailservices.core.exceptions;

public class MailSenderException extends Exception{

/**
*
*/
private static final long serialVersionUID = -6281925344129197510L;

private String message;





public MailSenderException(String message){
this.message = message;
}

@Override
public String getMessage() {
return message;
}
}



Then we have the SMTP Authenticator class which is used for mail authentication. It is as follows;


package com.jkcs.commons.mailservices.core.remoting.ejb.bl;

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

public class SmtpAuthenticator extends Authenticator {
private String username;
private String password;

public SmtpAuthenticator(String username, String password) {
super();
this.username = username;
this.password = password;
}

public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}

}


And finally you the following spring xml shows how to configure and integrate all this together.



<?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-2.5.xsd">


<!-- our Authenticator implementation -->
<bean id="smtpAuthenticator"
class="com.test.commons.mailservices.core.remoting.ejb.bl.SmtpAuthenticator">
<constructor-arg>
<value>${outgoing.mail.server.userid}</value>
</constructor-arg>
<constructor-arg>
<value>${outgoing.mail.server.password}</value>
</constructor-arg>

</bean>

<!-- now setup an authenticated session -->
<bean id="mailSession" class="javax.mail.Session"
factory-method="getInstance">
<constructor-arg>
<props>
<prop key="mail.smtp.auth">true</prop>
<!-- <prop key="mail.smtp.socketFactory.port">465</prop>
<prop key="mail.smtp.socketFactory.class">
javax.net.ssl.SSLSocketFactory
</prop>
<prop key="mail.smtp.socketFactory.fallback">
false
</prop> -->
</props>
</constructor-arg>
<constructor-arg ref="smtpAuthenticator" />
</bean>


<bean id="mailService" class="com.test.commons.mailservices.core.remoting.ejb.bl.MailSenderImpl">
<property name="mailSender" ref="mailSender"/>
<property name="fromAddress">
<value>${outgoing.mail.server.userid}</value>
</property>
</bean>

<!-- Mail service -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host">
<value>${outgoing.mail.server.ip}</value>
</property>
<property name="port">
<value>${outgoing.mail.server.port}</value>
</property>
<property name="username">
<value>${outgoing.mail.server.userid}</value>
</property>
<property name="password">
<value>${outgoing.mail.server.password}</value>
</property>
<property name="session" ref="mailSession" />
<property name="javaMailProperties">
<props>
<!-- Use SMTP-AUTH to authenticate to SMTP server -->
<prop key="mail.smtp.auth">true</prop>
<prop key="mail.smtp.sendpartial">true</prop>
<!-- Use TLS to encrypt communication with SMTP server -->
<!-- <prop key="mail.smtp.starttls.enable">true</prop>
<prop key="mail.smtp.sendpartial">true</prop> -->

</props>
</property>
</bean>

</beans>


As you can see i have used parameters as ${var_name}. This is because i have used Spring property file loading mechanism. Hence those variables are taken from a .property file. To configure that use the following;


<bean id="PropertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!--
The order of these properties files is important, as properties
will override one another
-->
<value>xxx.properties</value>
<value>mail.properties</value>
</list>
</property>
</bean>


Thats about it. Hope this would be helpful to someone who is looking to do the same.

11 comments:

  1. Very useful, thank you for the info. There isn't a lot that Spring doesn't offer these days.

    ReplyDelete
  2. Thx for appreciating my work :) ... Yes you are true, they have so many plug ins which one should consider before they try and re-invent the wheel...

    ReplyDelete
  3. Thanks for sharing

    ReplyDelete
  4. "after a few hours of Googling and coding" - Serious, don't you think it would have been much easier to just use the plain Java Mail API?

    "you do not need to deal with boilerplate code" - Pardon me, but your Spring configuration alone contains more than enough boilerplate code ...

    Sorry to say, but to me this looks like a very bad example of framework overuse. An intriguing lesson in how to make simple things complicated anyway.

    Still thank you for these insights.

    ReplyDelete
  5. Thank you for sharing your thoughts. I have personally used the Java Mail API alone for SMPT communication. What i loved about spring was anyone could use this code without getting into much details because it is well abstracted by spring. And by less code i meant only code which did not involve XML configurations. I understand what you mean because there is a quite alot of XML config involved, but for me it just looks cleaner this way rather than using the plain old Java Mail API because this is well abstracted and im a fan of DI :) ..

    ReplyDelete
  6. Hi Dinuka I loved ur aticle, but I was just womdering I had seen one bean called "smtpAuthenticator"in the config file but you have not posted the code for that class. Can you please let me know whether I am missing something or I am overlooking something?

    ReplyDelete
  7. Hi,
    Thx for the point out. Really appreciate it. I have added the missing class. You can now find it within the post. Thx again for the review. And glad you enjoyed reading it :)

    ReplyDelete
  8. Hi Dinuka,
    Thanks for your prompt response. I actually was able to figure out by simply googling the class name it self. Anyways I appreciate it for you help. I am using it .
    By
    NaiveGeek

    ReplyDelete
  9. Hey Allyn,

    Thats gud to hear that i was able to help :) ... If u see any other improvements which i can make in my other posts pls do share cos blogging for me is all about knowledge sharing...

    Cheers

    Dinuka

    ReplyDelete
  10. hi dinuka sir,
    it's a nice article. iam facing problem with sending emails.
    prob: normally emails are going properly through my appl but when the many number of emails are trying to send it is given connection failed or connection reset exceptions.? can you give any suggestions on this?

    thx in advance.

    ReplyDelete
  11. hi,
    Thx for your comment. Can you provide me a stack trace of the error? Are you reusing the same connection or trying to create new connections every time you send emails? A stack trace would be helpful.

    Cheers
    Dinuka

    ReplyDelete