Grails uses mailService from Spring. That service is synchronous, which means if SMTP goes down temporarily, application functioning is affected badly (HTTP 500).
I want to decouple the application from SMTP server.
The plan is to save ready-to-be-sent emails into an outbound queue and send them by timer, with retries. For my own code, when I call mailService directly, it is rather trivial – make a wrapper service and call it instead. But some of the plugins that my application uses (e.g. EmailConfirmation plugin) use the same mailService, and still fail, effectively blocking sign-up process, for instance.
I wonder how can I replace/wrap the definition of mailService to make all code, my own and plugins, transparently use my own service?
I.e.
- Plugin code injects mailService
- But instead of Spring default mailService my own code is injected
- When plugin sends a email the email object is saved to DB instead
- On timer a job wakes up, gets next N emails and tries to send them
Any ideas how to approach this problem?
P.S. I know about AsynchronousMail plugin. Unfortunately, its service must be called explicitely, i.e. it is not a drop-in replacement for mailService.
OK, it wasn’t that hard, after all. Easy steps first:
Step one: prepare database table to store pending email records:
Step two: create a periodic task to send the pending emails. Note
mailSenderinjection – it is part of original Grails Mail Plugin, so the sending (and configuration of it!) is made via Mail Plugin:Step three: create a drop-in replacement of mailService that could be used by any Grails code, including plugins. Note mmbf injection: this is
mailMessageBuilderFactoryfrom Mail Plugin. The service uses the factory to serialize the incoming Closure calls into a valid MIME message, and then save it to the file system:Step four: replace Mail Plugin’s mailService with the modified version, injecting it with the factory. In
grails-app/conf/spring/resources.groovy:Done!
From now on, any plugin or Grails code that uses/injects mailService will get a reference to MyMailService. The service will take requests to send the email, but instead of sending it it will serialize it onto disk, saving a record into database. A periodic task will load a bunch of such records every 30s and try to send them using the original Mail Plugin services.
I have tested it, and it seems to work OK. I need to do cleanup here and there, add transactional scope around sending, make parameters configurable and so on, but the skeleton is a workable code already.
Hope that helps someone.