ОК, это было не так сложно, в конце концов. Сначала легкие шаги:
Шаг первый : подготовить таблицу базы данных для хранения ожидающих записей электронной почты:
class PendingEmail {
Date sentAt = new Date()
String fileName
static constraints = {
sentAt nullable: false
fileName nullable: false, blank:false
}
}
Шаг второй : создайте периодическое задание для отправки ожидающих писем. Примечание mailSender
инъекция - это часть оригинального почтового плагина Grails, поэтому отправка (и его настройка!) Осуществляется через почтовый плагин:
import javax.mail.internet.MimeMessage
class BackgroundEmailSenderJob {
def concurrent = false
def mailSender
static triggers = {
simple startDelay:15000l, repeatInterval: 30000l, name: "Background Email Sender"
}
def execute(context){
log.debug("sending pending emails via ${mailSender}")
// 100 at a time only
PendingEmail.list(max:100,sort:"sentAt",order:"asc").each { pe ->
// FIXME: do in transaction
try {
log.info("email ${pe.id} is to be sent")
// try to send
MimeMessage mm = mailSender.createMimeMessage(new FileInputStream(pe.fileName))
mailSender.send(mm)
// delete message
log.info("email ${pe.id} has been sent, deleting the record")
pe.delete(flush:true)
// delete file too
new File(pe.fileName).delete();
} catch( Exception ex ) {
log.error(ex);
}
}
}
}
Шаг третий : создать замену mailService, которая может использоваться любым кодом Grails, включая плагины. Обратите внимание на инъекцию mmbf: это mailMessageBuilderFactory
из почтового плагина. Служба использует фабрику для сериализации входящих вызовов Closure в действительное сообщение MIME, а затем сохраняет его в файловой системе:
import java.io.File;
import org.springframework.mail.MailMessage
import org.springframework.mail.javamail.MimeMailMessage
class MyMailService {
def mmbf
MailMessage sendMail(Closure callable) {
log.info("sending mail using ${mmbf}")
if (isDisabled()) {
log.warn("No mail is going to be sent; mailing disabled")
return
}
def messageBuilder = mmbf.createBuilder(mailConfig)
callable.delegate = messageBuilder
callable.resolveStrategy = Closure.DELEGATE_FIRST
callable.call()
def m = messageBuilder.finishMessage()
if( m instanceof MimeMailMessage ) {
def fil = File.createTempFile("mail", ".mime")
log.debug("writing content to ${fil.name}")
m.mimeMessage.writeTo(new FileOutputStream(fil))
def pe = new PendingEmail(fileName: fil.absolutePath)
assert pe.save(flush:true)
log.debug("message saved for sending later: id ${pe.id}")
} else {
throw new IllegalArgumentException("expected MIME")
}
}
def getMailConfig() {
org.codehaus.groovy.grails.commons.ConfigurationHolder.config.grails.mail
}
boolean isDisabled() {
mailConfig.disabled
}
}
Шаг четвертый : замените почтовый плагин MailService на модифицированную версию, добавив его на завод. В grails-app/conf/spring/resources.groovy
:
beans = {
mailService(MyMailService) {
mmbf = ref("mailMessageBuilderFactory")
}
}
Готово!
С этого момента любой плагин или код Grails, который использует / внедряет mailService, будет получать ссылку на MyMailService. Служба будет принимать запросы на отправку электронной почты, но вместо отправки она будет сериализовать ее на диск, сохраняя запись в базе данных. Периодическая задача будет загружать кучу таких записей каждые 30 секунд и пытаться отправить их, используя оригинальные сервисы Mail Plugin.
Я проверил это, и, кажется, работает нормально. Мне нужно сделать очистку здесь и там, добавить область транзакций для отправки, сделать параметры настраиваемыми и так далее, но скелет уже является работоспособным кодом.
Надеюсь, это кому-нибудь поможет.