Module mails
Spring fournit de façon standard un socle permettant d'envoyer des mails, ce module vient cependant apporter quelques fonctionnalités supplémentaires :
- La persistance des mails dans la base de données
- L'envoi asynchrone
- Le filtrage des mails a envoyer
Modélisation
Le module s'organise autour de 3 tables :
- is_mail
- is_mail_recipient
- is_mail_attachement
qui sont ensuite manipulées au travers des classes EmailSpec, MailRecipientSpec et MailAttachementSpec.
Pourquoi avoir créé de nouvelles classes ?
Tout d'abord pour enrichir le modèle de base offert par Spring (SimpleMailMessage par exemple) et fournir un niveau d'indirection en découplant la fonction de création de l'objet modélisant le mail et son envoi. L'implémentation standard dans ce module utilise une passerelle SMTP, mais on pourrait facilement implémenter un service qui s'appuyerait sur un service mail mettant à disposition une API (exemple avec mailgun, sendGrid, mailjet...).
Configuration
Le module s'appuyant sur le starter spring-boot-starter-mail, la configuration du module reprend naturellement la configuration de base du starter
spring.mail.host=localhost
spring.mail.port=25 # SMTP server port
spring.mail.username= # Login used for authentication
spring.mail.password= # Password for the given login
spring.mail.protocol=smtp
spring.mail.defaultEncoding=UTF-8 # Default message encoding
A ces éléments viennent s'ajouter des clés spécifiquement créées dont la manipulation sera détaillée ci-après
isotope.mail.send-mail-cron-expression=0 0/5 * * * *
## Configuration de l'exécution de la tâche de nettoyage des mails
isotope.mail.delete-mail-cron-expression=0 0 4 * * *
## Durée de rétention exprimée en jours
isotope.mail.max-retention-before-delete=365
#Filtre des mails
isotope.mail.filter=ipsosenso.com
Il faut également ajouter l'annotation @EnableScheduling.
@SpringBootApplication
@EnableScheduling
public class IsotopeApplication extends isotope.IsotopeApplication {
...
}
Planification de la tâche d'envoi
L'envoi asynchrone s'appuie sur une tâche programmée pour s'exécuter toutes les 20s, cette exécution peut être modifiée via le fichier properties de l'application
Exemple pour programmer l'exécution toutes les 5 minutes :
isotope.mail.send-mail-cron-expression=0 0/5 * * * *
Pour plus de détail sur la syntaxe acceptée voir la classe CronSequenceGenerator
Nettoyage de la table de mail
Une fois envoyé, le mail est conservé en base de donnée pour une durée de 365j (NB sans distinction du statut envoyé/en erreur...), une tâche s'exécute donc tous les jours pour effectuer la suppression. Par défaut cette tâche s'exécute à 4H du matin tous les jours mais son exécution peut être modifiée via le paramétrage suivant :
isotope.mail.delete-mail-cron-expression=0 0 4 * * *
Pour plus de détail sur la syntaxe acceptée voir la classe CronSequenceGenerator
La durée de rétention (365j) est également paramétrable selon les besoins des projets :
isotope.mail.max-retention-before-delete=365
Filtrage des mails
Il peut être intéressant de filtrer les mails envoyés par la plateforme, ceci est particuilèrement vrai pour les cycles de développement
Exemple autoriser uniquement les mails ipsosenso.com
isotope.mail.filter=ipsosenso.com
Autoriser les mails *.ipsosenso.com et *.sample.com
isotope.mail.filter=ipsosenso.com,sample.com
Autoriser uniquement les mails vers otu@ipsosenso.com
isotope.mail.filter=otu@ipsosenso.com
Templates de mail
Les templates de mails sont stockés sous le répertoire resources/templates, le nom du template est déterminant car manipulé par la suite dans le code (sans l'extension) exemple pour le template NomDuTemplate.html stocké sous l'aborescence rsources/templates/mail :
EmailSpec s = service
.createBuilder()
.subject("My Subject")
.from(applicationConfig.getMailFrom())
.recipients(recipients)
.withTemplate("mail/NomDuTemplate", model)
.build();
NB : le template n'est pas obligatoire il est tout à fait possible de manipuler directement le contenu du mail via la méthode EmailSpec.body()
EmailSpec s = service
.createBuilder()
.subject("My subject")
.from(applicationConfig.getMailFrom())
.recipients(recipients)
.body("Hello World")
.build();
Exemples d'utilisation
Envoyer un mail immmédiatement
Il existe des cas de figure où le caractère asynchrone de l'envoi peut devenir problématique, par exemple dans le cas d'une fonction de perte de mot de passe, si le mail ne part pas immédiatement il est possible que l'utilisateur imagine un dysfonctionnement de l'application:
List<MailRecipientSpec> recipients = Collections.singletonList(new MailRecipientSpec(mailTo, MailRecipientSpec.TYPE.TO));
// build the mail content with dynamic vars
Map<String, Object> model = new HashMap<>();
model.put("name", "John DOE");
model.put("address", "1 rue saint georges 75009 PARIS");
EmailSpec mailSpecification = service
.createBuilder()
.subject("Welcome !")
.from("no-reply@ipsosenso.com")
.recipients(recipients)
.withTemplate("mail/Welcome", model)
.deferred(false)
.build();
// instance of MailService
service.sendMail(mailSpecification);