Accéder au contenu principal

Java Mail API: Simplifiez l'Envoi de Mails depuis Votre Application

 Dans le langage Java, il existe une API (au sens littéral du terme) qui permet d'envoyer un mail depuis une application développée avec Java. Ce tutoriel a pour but de vous indiquer pas à pas comment envoyer un mail en java d'une part, mais aussi vous structurera  le code de sorte qu'il soit réutilisable et extensible facilement selon vos besoins.


1. Description de notre exemple

Il s'agit ici de construire une application (console) qui enverra un mail à différents destinataires. le type de mail, le ou les destinataires et les éventuels fichiers à attacher au mail envoyé seront indiqués à la console de manière interactive, à l'application.

2. Les dépendances nécessaires pour l'envoie de mail

Bien que la dépendance vers Java Mail API (désormais Jakarta Mail API) existe, il est préférable d'indiquer la dépendance de l'implémentation de cette API qui vous intéresse. Dans ce tutoriel, nous utiliserons l'implémentation de référence de cette API (Jakarta Mail) désormais dénommée: Eclipse Angus

2.1 Dépendances Gradle

implementation('org.eclipse.angus:angus-mail:2.0.3')

2.2 Dépendances Maven

<dependencies>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.3</version>
</dependencies>

3. Les étapes pour envoyer un mail en Java

3.1 Définir les propriétés (properties) nécessaires

  • mail.smtp.host: le nom réseau ou l'ip du serveur SMTP. Un serveur SMTP est un serveur qui se charge de l'envoie du mail. Exemple: smtp.gmail.com
  • mail.smtp.port: Le port sur lequel écoute le serveur SMTP. Exemple: 465
  • mail.smtp.auth: une valeur qui indique que le serveur SMTP a besoin d'une authentification lorsqu'on y accède pour l'envoie de mail. Exemple: true
  • mail.smtp.socketFactory.port: Le port sur lequel écoute le serveur SMTP pour les envois cryptés (SSL). Exemple: 465
  • mail.smtp.socketFactory.class: La factory qui sera utilisée par l'API Java Mail pour encrypter les envois SSL. Exemple: javax.net.ssl.SSLSocketFactory
        Properties prop = new Properties();
prop.put("mail.smtp.host", "smtp.gmail.com");
prop.put("mail.smtp.port", "465");
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.socketFactory.port", "465");
prop.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

3.2 Créer la session mail

Si votre serveur SMTP n'a pas besoin d'autentification et que vous l'avez indiqué avec la propriété mail.smtp.auth comme suit: prop.put("mail.smtp.auth", "false"), alors la création de la session mail sera:

        Session session = Session.getInstance(prop);

Dans le cas contraire, il faudra créer la session en indiquant l'authentification:

        String username = "javatutorialshub@gmail.com";
String password = "xxxxxxxxx";

Session session = Session.getInstance(prop,
new jakarta.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});

3.3 Composer le mail

Deux éléments sont très importants dans la composition du mail. Il faut nécessairement indiquer l'adresse email qui est à la source de l'envoie de mail: message.setFrom(...) et ensuite la liste des destinataires du mail: message. setRecipients(...)

        Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("myUserFrom@gmail.com"));
message.setRecipients(
Message.RecipientType.TO,
InternetAddress.parse("user1@gmail.com, user2@yahoo.com")
);
message.setSubject("Proverbe Chinois du jour");


String msg = "Celui qui sait qu'il ne sait pas, éduque-le. Celui qui sait qu'il sait, " +
"écoute-le. Celui qui ne sait pas qu'il sait, éveille-le. Celui qui ne sait pas " +
"qu'il ne sait pas, fuis-le.";

MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent(msg, "text/html; charset=utf-8");

Multipart multipart = new MimeMultipart();
multipart.addBodyPart(mimeBodyPart);

message.setContent(multipart);        

Pour indiquer des emails en copie du mail que vous envoyez via Java Mail, vous devez utiliser message.setRecipients(...) avec:
  • Message.RecipientType.CC: Copie Conforme
  • Message.RecipientType.BCC: Copie Cachée

3.4 Envoyer le mail

L'envoie de mail se fait via une seule méthode:

    Transport.send(message);

3.5 Le code tout en un

        try {
Properties prop = new Properties();
prop.put("mail.smtp.host", "smtp.gmail.com");
prop.put("mail.smtp.port", "465");
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.socketFactory.port", "465");
prop.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");


String username = "javatutorialshub@gmail.com";
String password = "xxxxxxxxx";

Session session = Session.getInstance(prop,
new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});

Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("myUserFrom@gmail.com"));
message.setRecipients(
Message.RecipientType.TO,
InternetAddress.parse("user1@gmail.com, user2@yahoo.com")
);
message.setSubject("Proverbe Chinois du jour");


String msg = "Celui qui sait qu'il ne sait pas, éduque-le. Celui qui sait qu'il sait, " +
"écoute-le. Celui qui ne sait pas qu'il sait, éveille-le. Celui qui ne sait pas " +
"qu'il ne sait pas, fuis-le.";

MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent(msg, "text/html; charset=utf-8");

Multipart multipart = new MimeMultipart();
multipart.addBodyPart(mimeBodyPart);

message.setContent(multipart);

Transport.send(message);
} catch (MessagingException e) {
log.log(Level.SEVERE, e.getMessage(), e);
}

Comme vous pouvez le voir, mettre tout le code en un pour l'envoie de mail, n'est pas une bonne idée, et n'est à aucun moment une bone pratique. Raison pour laquelle dans application ci dessous, nous allons désigner différemment afin de rendre plus modulaire les différents composants.

4. Le design de notre application d'envoi de mail



Notre application a été structurée de sorte que chaque classe/interface n'ai qu'un seul role (une seule responsabilité), bien défini, conformément aux règles de développement SOLID (nous y reviendrons dans un autre tutoriel)

MailComposer: Classe abstraite qui se charge de composer le mail en collectant des informations sur les adresses de destination, le sujet du mail et son contenu.

    public Message compose(Session session) throws MailComposerException {
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(fromEmail));
message.setRecipients(
Message.RecipientType.TO,
toEmails.stream().map(t -> {
try {
return new InternetAddress(t);
} catch (AddressException e) {
logger.log(Level.SEVERE, "Error occurred when converting '" +
                                                        t + "' email");
throw new RuntimeException(e);
}
}).toList().toArray(new InternetAddress[0])
);
return doCompleteCompose(message);
} catch (MessagingException e) {
logger.log(Level.SEVERE, "Error convert email to internet addresses");
throw new MailComposerException(e);
}
}

TextMailComposer: il s'agit d'une implémentation basique du MailComposer qui se charge de positionner le contenu du mail en tant que texte dans l'objet Message de l'API Jakarta Mail.

    public Message doCompleteCompose(Message message) throws MailComposerException {
try {
message.setSubject(subject);
message.setText(content);
return message;
} catch (MessagingException e) {
logger.log(Level.SEVERE, "Error occurred when composing the text message");
throw new MailComposerException(e);
}
}

HtmlMailComposer: Cette implémentation du MailComposer, quant à elle se charge de positionner le contenu du mail en tant que contenu html afin que le message soit formatté et affiché comme une page web(html)

    public Message doCompleteCompose(Message message) throws MailComposerException {
try {
Multipart multipart = new MimeMultipart();
addBodyPart(multipart);
message.setSubject(subject);
message.setContent(multipart);
return message;
} catch (MessagingException e) {
logger.log(Level.SEVERE, "Error occurred when composing the text message");
throw new MailComposerException(e);
}
}

protected void addBodyPart(Multipart multipart) throws MessagingException {
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent(content, TEXT_HTML_CHARSET_UTF_8);
multipart.addBodyPart(mimeBodyPart);
}

MultiPartMailComposer: quant à cette dernière implémentation étend (hérite) HtmlMailComposer et permet d'attacher plusieurs fichiers au mail.

    protected void addBodyPart(Multipart multipart) throws MessagingException {
super.addBodyPart(multipart);
if(filePaths != null && !filePaths.isEmpty()) {
filePaths.forEach(filePath -> {
MimeBodyPart attachmentBodyPart = new MimeBodyPart();
try {
attachmentBodyPart.attachFile(new File(filePath));
multipart.addBodyPart(attachmentBodyPart);
} catch (IOException | MessagingException e) {
logger.log(Level.SEVERE, "Error occurred when attaching file to
                                                mail body: '" + filePath);
throw new RuntimeException(e);
}
});
}
}

Ensuite dans la partie gauche de notre design apparait la classe qui se chargera de faire l'envoie, rien que l'envoie.

L'interface MailSender est implémentée par MailSenderImpl qui au final se charge d'invoquer la méthode compose du MailComposer puis utilise le message produit dans l'invocation de Transport.send()

public void send(MailComposer mailComposer) throws MailSenderException {
try {
Message message = mailComposer.compose(session);
Transport.send(message);
} catch (MailComposerException e) {
throw new MailSenderException(e);
} catch (MessagingException e) {
logger.log(Level.SEVERE, "Error occurred when send the mail message to
                                                            the recipients");
throw new MailSenderException(e);
}
}

Et enfin l'application principale(main) ne manipulera que le MailSender d'un côté et collectera des données de saisie de l'utilisateur au sein d'un MailComposer qu'elle passera ensuite au MailSender pour action.

Vous remarquerez aussi que des exceptions spécifiques ont été à chaque fois créées afin de donner du sens et spécialisées les exceptions et erreurs. Et tout ceci en suivant notre tutoriel sur la gestion des exceptions que vous trouverez en cliquant sur ce lien


Le code source de ce tutoriel et de notre application est présent sur github à cette adresse: simple-mail-sender

Ne pas hésiter à commenter ou à poser une question ou à demander de l'aide autour de java et des technologies connexes. Nous nous ferons un plaisir de vous répondre.


Commentaires

Posts les plus consultés de ce blog

Comment valider les données utilisateur dans votre application Java : Guide pratique avec Jakarta Bean Validation

Lorsque vous développez une application qui interagit avec un utilisateur, en lui demandant de saisir des données, il est primordial de vérifier et valider ces données avant tout traitement. Et ceci pour plusieurs raisons: vous aurez la possibilité d'éviter des traitements inutiles si les données reçues ne sont pas conformes aux données attendues et aussi cela permet de protéger votre application en cas d'attaque orchestrée par des hackers. Dans le langage java, il existe une spécification qui vous permet de valider tout type de données transmis dans un bean: Java Bean Validation (ou plus récemment: Jakarta Bean Validation). Dans ce tutoriel, au travers d'une petite application de gestion d'une bibliothèque de quartier, nous allons voir ensemble comment utiliser Jakarta Bean Validation. 1. Pourquoi Jakarta Bean Validation L'objectif de Jakarta Bean Validation est de standardiser la validation des champs au travers de specification (JSR 380). Aussi cette standardisat...

Comprendre l'Inversion de Contrôle : Un Guide Pratique de Guice sur l'Injection des Dépendances dans le Développement de Logiciels

L'un des principes fondamentaux dans le développement de logiciels (ou d'applications) est la mise en oeuvre des principes SOLID. Le D ici indique: Dependency Inversion ou Dependency Injection ou encore Inversion Of Control (IoC). Autrement dit: Inversion de Contrôle ou Injection des Dépendances. L'injection des dépendances à pour principal objectif le découplage des modules entre eux. Cela implique donc de créer et développer les différents composants à base de classes abstraites ou d'interfaces, comme le permet Java. Ce faisant, l'application ou le logiciel devra utiliser une librairie ou un framework qui permette d'injecter les fameuses dépendances en question. Nous verrons dans la suite du tutoriel comment une librairie (ou framework léger) comme Guice (Google Guice) nous aidera dans cet objectif. Note: Spring Framework est l'un des framework les plus connus qui permettent de mettre en oeuvre l'Inversion de Contrôle.  1. Qu'est ce que l'Injec...

Gestion des exceptions en Java: Comprendre, Traiter et Créer

Apprenez à gérer efficacement les exceptions d'exécution en Java, améliorant ainsi la fiabilité et la stabilité de vos applications et programmes. 1. Qu'est ce qu'une exception Comme dans la plus part des languages de programmation, Java vous permet de gérer avec précision des erreurs imprévues (exceptions) qui pourraient survenir lors de l'exécution d'une instruction ou d'un traitement. Une exception est une erreur imprévue qui survient lors de l'exécution d'une instruction de votre code (de votre programme). Imaginons que vous souhaitiez faire la division de deux nombres dans votre programme: x = a / b.  Malheureusement si le diviseur b vaut zéro lors de l'exécution de votre programme, cette situation génèrera une erreur imprévue. On parlera alors d'une exception. Il faut garder à l'esprit que toute instruction dans un programme peut générer une exception (à moins que vos instructions soient des plus basiques et simples). Parfois lorsqu...