Using Spring Integration to Create a Async Service in Grails
26th June 2012 by jt 3 CommentsA common action in a web application is to send an email based on a user some user action. This is a nice candidate for a async service to improve the response time to the end user. In this example, we’ll use Spring Integration to support sending a an email via Amazon’s Simple Mail Service.
Setup
You will need the following:
- A Grails 2.0+ project
- AWS Plugin (Using 1.2.12.2 in this example)
- Spring Security Core Plugin
Spring Integration
There is no plugin for Spring Integration. We can add it as a dependency to the project. Add the following to your BuildConfig.Groovy file (under /conf)
dependencies { // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg. compile('org.springframework.integration:spring-integration-mail:2.1.2.RELEASE') { excludes "spring-aop", "spring-beans", "spring-context", "spring-context-support", "spring-core", "spring-tx" } } |
In my example, I’ve setup a simple form and controller to handle the form post. I’m going to assume you already have a working knowledge of Grails and not cover this area.
Spring Integration supports using an interface for a messaging gateway.
My gateway is defined as:
public interface SendMailGateway { @Gateway(requestChannel='mailChannel') void asyncMailSender(Map emailParams) } |
I want this to call a service to send an mail via AWS. I’ll be using the AWS plugin for this, which has excellent documentation you can refer to for configuration. In my example, I want to send an HTML email. The best way to do this is through the use of a GSP template. Grails 2.0 added a PageRender service you can use to generate HTML from templates in services. My service class is as follows:
class AwsEmailService { PageRenderer groovyPageRenderer def sendMail(Map params) { log.info('Mail Request Received by AWS Service') log.debug(params?.dump()) try { String htmlBody = groovyPageRenderer.render(template:params.template, model: params.model) //call to AWS Mail Plugin def mailId = sesMail { to params.user.username subject params.subject html htmlBody } log.info("Mail Sent. ID: ${mailId}") } catch(e) { log.error('Sending Mail Failed') log.error(e.message) e.stackTrace.each{log.error(it)} } } } |
This class is in the Grails services folder, thus will be automatically wired up as a Spring service and the @Service annotation is not required.
We want to use Spring Integration to provide a service using the gateway to call the AwsEmailService asynchronously. While you can use the Grails bean builder DSL to wire this up, its easier to just use the XML config so you have IDE support for the syntax. Getting the Groovy code correct for the DSL can be a little tricky.
The following XML defines the gateway interface and assigns it to a channel called ‘mailChannel’. The mail channel is defined as a queue, with a depth of 10. We configure the AwsEmailService with a poller to poll the mailChannel once every second.
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:int="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd"> <int:gateway id="sendMailGateway" service-interface="mailsender.SendMailGateway" default-request-channel="mailChannel"/> <int:channel id="mailChannel"> <int:queue capacity="10"/> </int:channel> <int:service-activator id="mailer" ref="awsEmailService" method="sendMail" input-channel="mailChannel"> <int:poller fixed-rate="1000"/> </int:service-activator> </beans:beans> |
To bring this XML Spring config into your application, add the following to /conf/spring/config.groovy
beans = {
importBeans('file:grails-app/conf/spring/integration.xml')
}
My use case in this example is to create a new user from a signup form, and send a verification email. I’m using the Spring Security Core plugin and its default domain objects to create the user. My Grails Service class is as follows:
class SignUpService { def springSecurityService def sendMailGateway def signUp(def params) { log.debug(params?.dump()) // check for user def user = User.findByUsername(params?.email) if (user) { throw new Exception('User Already Exists') } user = new User(username: params?.email, password: springSecurityService.encodePassword(RandomStringUtils.randomAlphanumeric(30)), validationPhrase: RandomStringUtils.randomAlphanumeric(30)).save(failOnError : true, flush: true) if (user && !user?.hasErrors()) { def role = Role.findByAuthority('ROLE_USER') new UserRole(role: role, user: user).save(failOnError: true, flush: true) user.save(failOnError : true, flush: true) // send verification email sendVerificationEmail(user) // log user into application - this creates a session springSecurityService.reauthenticate(user.username, user.password) } } def sendVerificationEmail(User user) { def map = [:] map.user = user map.subject = "Welcome to JTs Mail Sender App" map.template = "/email-templates/verifyAccount" map.model = [validationPhrase: user.validationPhrase, now: new Date()] log.info('Sending Mail Request to SI Gateway') sendMailGateway.asyncMailSender(map) } } |
You can see that I’m autowiring the SpringSecurityService in at the top. I’m doing the same thing for the sendMailGateway. The SI gateway we configured is registered with Spring and will autowire just like any other bean in Grails.
The source code for this example is on google code. You can get a copy with:
svn checkout http://jts-blog.googlecode.com/svn/trunk/june26Post/mailSender/ jts-blog-read-only



A bunch of random technology stuff that has my attention. I work with a lot of Oracle, Java, and dabble with various open source software packages.