Grails Mail Plugin provides e-mail sending capability to Grails application by configuring Spring MailSender. Plugin is simple to configure and easy to use. But plugin doesn’t provide a clear way to set-up multiple SMTP servers.
Let’s dig into the inner workings of the plugin and check if it is easy to add this functionality.
Mail can be sent using the mailService via thesendMail method. We will take a closer look atMailService class. It provides two sendMailmethods.
takes a single Closure argument that uses a DSL to configure the message to be sent. But it uses default configuration which you can override i.e. inapplication.groovy file.
The second one:
gives us possibility to create MailMessageBuilder object with our custom configuration (by the way, the first method uses the second one).
One thing we can do is to extend MailMessageBuildFactory class and add our custom createBuilder method implementation. Let’s do it.
This code is almost entirely copied from MailGrailsPlugin#configureMailSender. Main difference is in the way we create session(JavaMailSenderImpl can lazily initiate session from javaMailProperties. You can find it here). We don’t set javaMailProperties. Instead of this, we create Session object from props.
What do we do here? We want to create MailMessageBuilder object with our custom-configured mailSender (only if passed configuration contains props property, otherwise it uses default mailSender). So we need to provide our configuration. Here is example from my application.yml file:
Now we need to register our bean (more precisely, override existing one). We need to modify grails-app/conf/spring/resources.groovy file:
Ok. It’s almost done but now we need to use it. We’ll implement simple services:
MailSenderService introduces two methods: sendFromPrimaryAccount and sendFromSecondaryAccount to send email using default configuration and custom-configured respectively. As you can see each of methods call magic notify method that takes two parameters: string and event object. Services in Grails 3 have the Events trait which gives us an interface into Reactor. First parameter is the name of the event which will be fired and the second parameter is the object which will be sent to below consumer:
But go back to our email notification service. In postInit method we initialize our custom configuration. It takes config from grailsApplication (interface to application configuration), next looks for ‘extendedMail’ property and then creates configuration object. Let’s check out methods implementations:
- sendMailUsingPrimaryAccount calls sendMail method without passing configuration so default configuration should be used
- sendMailUsingSecondaryAccount calls sendMail method with our custom configuration so gmail SMTP server should be used
The sendMail method takes a single Closure argument that uses DSL to configure the message to be sent. Grails mail documentation is quite stale (covers 1.0.8-SNAPSHOT version) but it contains all basic DSL keywords and options. If you need more details, you can see available methods here — MailMessageBuilder.
It’s time to see how it works in action. You can download source code from my github repository — https://github.com/rgorzkowski/grails-multiple-mail-senders
We’ll use Green Mail plugin to simulate email server for default configuration and gmail SMTP for custom configuration. First thing we need to do is green mail configuration. Plugin configuration is as simple as defining the grails.mail.portproperty. I’ll do this in application.groovy file:
Green mail plugin will be enabled in development and test environment. Furthermore, default sender address, email@example.com, is also set here.
Second thing is to configure our additional sender to send with a Gmail account. To do this, open grails-app/conf/application.yml file and locate this configuration:
You need to change from, username and password property.
Firstly we’ll run our application (pl.stepwise.Application) in development mode, and then we’ll write some integration tests.
The easiest way to run application is to download source code or clone repository from github repository — https://github.com/rgorzkowski/grails-multiple-mail-senders then simply run grailsw command from command line:
As a result you should see something similar to this:
Now open your browser and navigate to http://localhost:8080
You can see that there’re available two controllers: GreenmailController and MailController. Go to MailController:
Choose ‘Send from primary account’ and voila your email has been sent an you’re redirected to greenmail list.
What exactly has happened? The MailSenderService.sendFromPrimaryAccount method has been called and notified EmailNotificationService that email from primary account should be sent and then mailService’s sendMail method has been invoked. Green mail has received our email and we can open the email and scratch beneath the surface (check email headers).
If you choose ‘Send from second account’ you’ll send an email from gmail account. Of course if you configured application.yml before. You should receive similar email to below:
We don’t have firstname.lastname@example.org account in our domain so it’s the reason why we got the ‘550 error’ but you can see original email which was sent from our application.
Now let’s write some integration tests. Integration tests are using properties from test environment configuration from application.yml file:
and from application groovy:
First test, called ‘should send an email from primary account’, checks whether an email is sent from primary account. We check here if triggered event is type of PrimaryMailAccountEvent. If it is, it means that event should be handled by sendMailUsingPrimaryAccount method from EmailNotificationService and as you remember primary account is correlated with greenmail so we can check if greenmail service contains our mail. We’re checking it in ‘then’ section of our test.
The second test, called ‘should send an email from secondary account’ assumes that email will be sent from gmail SMTP server. I don’t want to put real data for gmail account configuration so I expect that MailAuthenticationException will be thrown.