IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

 

Développons en Java   2.30  
Copyright (C) 1999-2022 Jean-Michel DOUDOUX    (date de publication : 15/06/2022)

[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

 

69. JavaMail

 

chapitre    6 9

 

Niveau : niveau 3 Intermédiaire 

 

Le courrier électronique repose sur le concept du client/serveur. Ainsi, l'utilisation d'e-mails requiert deux composants :

  • un client de mails (Mail User Agent : MUA) tel que Outlook, Messenger, Eudora, ...
  • un serveur de mails (Mail Transport Agent : MTA) tel que SendMail

Les clients de mails s'appuient sur un serveur de mails pour obtenir et envoyer des messages. Les échanges entre clients et serveurs sont normalisés par des protocoles particuliers.

JavaMail est une API qui permet d'utiliser le courrier électronique (e-mail) dans une application écrite en Java (application cliente, applet, servlet, EJB, ... ). Son but est d'être facile à utiliser, de fournir une souplesse qui permette de la faire évoluer et de rester le plus indépendant possible des protocoles utilisés.

JavaMail est une extension au JDK qui n'est donc pas fournie avec Java SE. Elle est intégrée à Java EE.

Les classes et interfaces sont regroupées dans quatre packages : javax.mail, javax.mail.event, javax.mail.internet, javax.mail.search.

Il existe deux versions de cette API :

  • 1.1.3 : version fournie avec J2EE 1.2
  • 1.2 : version courante

Les deux versions fonctionnent avec un JDK dont la version est au moins 1.1.6.

Cette API repose sur une abstraction assez forte de tout système de mails, ce qui lui permet d'ajouter des protocoles non gérés en standard. Pour ces différents protocoles, il faut utiliser une implémentation particulière pour chacun d'eux, implémentation fournie par des tiers. En standard, JavaMail 1.2 implémente les protocoles SMTP, POP3 et IMAP4. JavaMail 1.1.3 ne fournit une implémentation que pour les protocoles SMTP et IMAP : l'implémentation pour le protocole POP3 doit être téléchargée séparément.

Ce chapitre contient plusieurs sections :

 

69.1. Le téléchargement et l'installation

Pour le J2SE, il est nécessaire de télécharger les fichiers utiles et de les installer.

Pour les deux versions de l'API, il faut télécharger la version correspondante, décompresser le fichier dans un répertoire et ajouter le fichier mail.jar dans le CLASSPATH.

Ensuite il faut aussi installer le framework JAF (Java Activation Framework) : télécharger le fichier, décompresser et ajouter le fichier activation.jar dans le CLASSPATH

Pour pouvoir utiliser le protocole POP3 avec JavaMail 1.1.3, il faut télécharger en plus l'implémentation de ce protocole et inclure le fichier POP3.jar dans le CLASSPATH.

Pour le J2EE 1.2.1, l'API version 1.1.3 est intégrée à la plate-forme. Elle ne contient donc pas l'implémentation pour le protocole POP3. Il faut la télécharger et l'installer en plus comme avec le J2SE.

Pour le J2EE 1.3, il n'y a rien de particulier à faire puisque l'API version 1.2 est intégrée à la plate-forme.

 

69.2. Les principaux protocoles

 

69.2.1. Le protocole SMTP

SMTP est l'acronyme de Simple Mail Transport Protocol. Ce protocole défini par la recommandation RFC 821 permet l'envoi de mails vers un serveur de mails qui supporte ce protocole.

 

69.2.2. Le protocole POP

POP est l'acronyme de Post Office Protocol. Ce protocole défini par la recommandation RFC 1939 permet la réception de mails à partir d'un serveur de mails qui implémente ce protocole. La version courante de ce protocole est 3. C'est un protocole très populaire sur Internet. Il définit une boîte aux lettres unique pour chaque utilisateur. Une fois que le message est reçu par le client, il est effacé du serveur.

 

69.2.3. Le protocole IMAP

IMAP est l'acronyme de Internet Message Acces Procol. Ce protocole défini par la recommandation RFC 2060 permet aussi la réception de mails à partir d'un serveur de mails qui implémente ce protocole. La version courante de ce protocole est 4. Ce protocole est plus complexe car il apporte des fonctionnalités supplémentaires : plusieurs répertoires par utilisateur, partage de répertoires entre plusieurs utilisateurs, maintien des messages sur le serveur, etc ...

 

69.2.4. Le protocole NNTP

NNTP est l'acronyme de Network News Transport Protocol. Ce protocole est utilisé par les forums de discussions (news).

 

69.3. Les principales classes et interfaces de l'API JavaMail

JavaMail propose des classes et interfaces qui encapsulent ou définissent les objets liés à l'utilisation des mails et les protocoles utilisés pour les échanger.

 

69.3.1. La classe Session

La classe Session encapsule pour un client donné sa connexion avec le serveur de mails. Cette classe encapsule les données liées à la connexion (options de configuration et données d'authentification). C'est à partir de cet objet que toutes les actions concernant les mails sont réalisées.

Les paramètres nécessaires sont fournis dans un objet de type Properties. Un objet de ce type est utilisé pour contenir les variables d'environnements : placer certaines informations dans cet objet permet de partager des données.

Une session peut être unique ou partagée par plusieurs entités.

Exemple :
    // creation d'une session unique

    Session session = Session.getInstance(props,authenticator);
    // creation d'une session partagee

    Session defaultSession = Session.getDefaultInstance(props,authenticator);

Pour obtenir une session, deux paramètres sont attendus :

  • un objet Properties qui contient les paramètres d'initialisation. Un tel objet est obligatoire
  • un objet Authenticator optionnel qui permet d'authentifier l'utilisateur auprès du serveur de mails

La méthode setDebug() qui attend en paramètre un booléen est très pratique pour debugger car avec le paramètre true, elle affiche des informations lors de l'utilisation de la session notamment le détail des commandes envoyées au serveur de mails.

 

69.3.2. Les classes Address, InternetAddress et NewsAddress

La classe Address est une classe abstraite dont héritent toutes les classes qui encapsulent une adresse dans un message.

Deux classes filles sont actuellement définies :

  • InternetAddress
  • NewsAddress

Le classe InternetAddress encapsule une adresse email respectant le format de la RFC 822. Elle contient deux champs : address qui contient l'adresse e-mail et personal qui contient le nom de la personne. La classe possède des constructeurs, des getters et des setters pour utiliser ces attributs.

Le plus simple pour créer un objet InternetAddress est d'appeler le constructeur en lui passant en paramètre une chaîne de caractères contenant l'adresse e-mail.

Exemple :
    InternetAddress vInternetAddresses = new InternetAddress("moi@chez-moi.fr");

Un second constructeur permet de préciser l'adresse e-mail et un nom en clair.

La méthode getLocalAddress(Session) permet de déterminer si possible l'objet InternetAddress encapsulant l'adresse e-mail de l'utilisateur courant, sinon elle renvoie null.

La méthode parse(String) permet de créer un tableau d'objets InternetAddress à partir d'une chaîne contenant les adresses e-mail séparées par des virgules.

Un objet InternetAddress est nécessaire pour chaque émetteur et destinataire du mail. L'API ne vérifie pas l'existence des adresses fournies. C'est le serveur de mails qui vérifiera les destinataires et éventuellement les émetteurs selon son paramétrage.

La classe NewsAddress encapsule une adresse news (forum de discussion) respectant le format RFC1036. Elle contient deux champs : host qui contient le nom du serveur et newsgroup celui du nom du forum.

La classe possède des constructeurs, des getters et des setters pour utiliser ces attributs.

 

69.3.3. L'interface Part

Cette interface définit un certain nombre d'attributs commun à la plupart des systèmes de mails et un contenu.

Le contenu peut être renvoyé sous trois formes : DataHandler, InputStream et Object.

Cette interface définit plusieurs méthodes principalement des getters et des setters dont les principaux sont :

Méthode

Rôle

int getSize()

Renvoyer la taille du contenu ou -1 si elle ne peut être déterminée

int getLineCount()

Renvoyer le nombre de lignes du contenu ou -1 s'il ne peut être déterminé

String getContentType()

Renvoyer le type du contenu sinon null

String getDescription()

Renvoyer la description

void setDescription(String)

Mettre à jour la description

InputStream getInputStream()

Renvoyer le contenu sous la forme d'un flux

DataHandler getDataHandler()

Renvoyer le contenu sous la forme d'un objet DataHandler

Object getContent()

Renvoyer le contenu sous la forme d'un objet. Un cast est nécessaire selon le type du contenu.

void setText(String)

Mettre à jour le contenu sous forme d'une chaîne de caractères fournie en paramètre

 

69.3.4. La classe Message

La classe abstraite Message encapsule un Message. Le message est composé de deux parties :

  • une en-tête qui contient des attributs
  • un corps qui contient les données à envoyer

Pour la plupart de ces données, la classe Message implémente l'interface Part qui encapsule les attributs nécessaires à la distribution du message (auteur, destinataire, sujet ...) et le corps du message.

Le contenu du message est stocké sous forme d'octets. Pour y accéder, il faut utiliser un objet du JavaBean Activation Framework (JAF) : DataHandler. Ceci permet une séparation des données nécessaires à la transmission et du contenu du message qui peut ainsi prendre n'importe quel format. La classe Message ne connaît pas directement le type du contenu du corps du message.

JavaMail fournit en standard une classe fille nommée MimeMessage qui implémente la recommandation RFC 822 pour les messages possédant un type MIME.

Il y a deux façons d'obtenir un objet de type Message : instancier une classe fille pour créer un nouveau message ou utiliser un objet de type Folder pour obtenir un message existant.

La classe Message définit deux constructeurs en plus du constructeur par défaut :

Constructeur

Rôle

Message(session)

Créer un nouveau message

Message(Folder, int)

Créer un message à partir d'un message existant


La classe MimeMessage est la seule classe fille qui hérite de la classe Message. Elle dispose de plusieurs constructeurs.

Exemple :
      MimeMessage message = new MimeMessage(session);

Elle possède de nombreuses méthodes pour initialiser les données du message :

Méthode

Rôle

void addFrom(Address[])

Ajouter des émetteurs au message

void addRecipient(RecepientType, Address[])

Ajouter des destinataires à un type (direct, en copie ou en copie cachée)

Flags getFlags()

Renvoyer les états du message

Adress[] getFrom()

Renvoyer les émetteurs

int getLineCount()

Renvoyer le nombre de lignes du message

Address[] getRecipients(RecepientType)

Renvoyer les destinataires du type fourni en paramètre

Address getReplyTo()

Renvoyer l'adresse e-mail pour la réponse

int getSize()

Renvoyer la taille du message

String getSubject()

Renvoyer le sujet

Message reply(boolean)

Créer un message pour la réponse : le booléen indique si la réponse ne doit être faite qu'à l'émetteur

void setContent(Object, String)

Mettre à jour le contenu du message en précisant son type MIME

void setFrom(Address)

Mettre à jour l'émetteur

void setRecipients(RecepientsType, Address[])

Mettre à jour les destinataires d'un type

void setSendDate(Date)

Mettre à jour la date d'envoi

void setText(String)

Mettre à jour le contenu du message avec le type MIME « text/plain »

void setReply(Address)

Mettre à jour le destinataire de la réponse

void writeTo(OutputStream)

Envoyer le message au format RFC 822 dans un flux. Très pratique pour visualiser le message sur la console en passant en paramètre (System.out)


La méthode addRecipient() permet d'ajouter un destinataire et le type d'envoi.

Le type d'envoi est précisé grâce à une constante pour chaque type :

  • destinataire direct : Message.RecipientType.TO
  • copie conforme : Message.RecipientType.CC
  • copie cachée : Message.RecipientType.BCC

La méthode setText() permet de facilement mettre une chaîne de caractères dans le corps du message avec un type MIME « text/plain ». Pour envoyer un message dans un format différent, par exemple HTML, il faut utiliser la méthode setContent() qui attend en paramètres un objet et une chaîne de caractères indiquant son type MIME.

Exemple :
    String texte = "<H1>bonjour</H1><a href=\"mailto:moi@moi.fr\">mail</a>";
    message.setContent(texte, "text/html");

Il est possible de joindre avec le mail des ressources sous forme de pièces jointes (attachments). Pour cela, il faut :

  • instancier un objet de type MimeMessage
  • renseigner les éléments qui composent l'en-tête : émetteur, destinataire, sujet ...
  • instancier un objet de type MimeMultiPart
  • instancier un objet de type MimeBodyPart et alimenter le contenu de l'élément
  • ajouter cet objet à l'objet MimeMultiPart grâce à la méthode addBodyPart()
  • répéter l'instanciation et l'alimentation pour chaque ressource à ajouter
  • utiliser la méthode setContent() du message en passant en paramètre l'objet MimeMultiPart pour associer le message et les pièces jointes au mail
Exemple :
    Multipart multipart = new MimeMultipart();
    
    // creation partie principale du message

    BodyPart messageBodyPart = new MimeBodyPart();
    messageBodyPart.setText("Test");
    multipart.addBodyPart(messageBodyPart);
    
    // creation et ajout de la piece jointe

    messageBodyPart = new MimeBodyPart();
    DataSource source = new FileDataSource("image.gif");
    messageBodyPart.setDataHandler(new DataHandler(source));
    messageBodyPart.setFileName("image.gif");
    multipart.addBodyPart(messageBodyPart);
    
    // ajout des éléments au mail

    message.setContent(multipart);

 

69.3.5. Les classes Flags et Flag

La classe Flags encapsule un ensemble d'états pour un message.

Il existe deux types d'états : les états prédéfinis (System Flag) et les états particuliers définis par l'utilisateur (User Defined Flag)

Un état prédéfini est encapsulé par la classe Internet Flags.Flag. Cette classe définit plusieurs états statiques :

Etat

Rôle

Flags.Flag.ANSWERED

 

Flags.Flag.DELETED

Le message est marqué pour la suppression

Flags.Flag.DRAFT

Le message est un brouillon

Flags.Flag.FLAGGED

Le message est marqué dans un état qui n'a pas de définition particulière

Flags.Flag.RECENT

Le message est arrivé récemment. Le client ne peut pas modifier cet état

Flags.Flag.SEEN

Le message a été visualisé : positionné à l'ouverture du message

Flags.Flag.USER

Le client a la possibilité d'ajouter des états particuliers


Tous ces états ne sont pas obligatoirement supportés par le serveur.

La classe Message possède plusieurs méthodes pour gérer les états d'un message. La méthode getFlags() renvoie un objet de type Flags qui contient les états du message. Les méthodes setFlag(Flag, boolean) permettent d'ajouter un état du message. La méthode contains(Flag) vérifie si l'état fourni en paramètre est positionné pour le message.

La classe Flags possède plusieurs méthodes pour gérer les états. Les principales sont :

Méthode

Rôle

void add(Flags.Flag)

Permet d'ajouter un état

void add(Flags)

Permet d'ajouter un ensemble d'états

void remove(Flags.Flag)

Permet d'enlever un état

void remove(Flags)

Permet d'enlever un ensemble d'états

boolean contains(Flags.Flag)

Permet de savoir si un état est positionné

 

69.3.6. La classe Transport

La classe Transport se charge de réaliser l'envoi du message avec le protocole adéquat. C'est une classe abstraite qui contient la méthode static send() pour envoyer un mail.

Il est possible d'obtenir un objet Transport dédié au protocole particulier utilisé par la session en utilisant la méthode getTransport() d'un objet Session. Dans ce cas, il faut :

  1. établir la connexion en utilisant la méthode connect() avec le nom du serveur, le nom de l'utilisateur et son mot de passe
  2. envoyer le message en utilisant la méthode sendMessage() avec le message et les destinataires. La méthode getAllRecipients() de la classe Message permet d'obtenir ceux contenus dans le message.
  3. fermer la connexion en utilisant la méthode close()

Il est préférable d'utiliser une instance de Transport tel qu'expliqué ci-dessus lorsqu'il y a plusieurs mails à envoyer car on peut maintenir la connexion avec le serveur ouverte pendant les envois.

La méthode static send() ouvre et ferme la connexion à chacun de ses appels.

 

69.3.7. La classe Store

La classe abstraite Store représente un système de stockage de messages. Pour obtenir une instance de cette classe, il faut utiliser la méthode getStore() d'un objet de type Session en lui donnant comme paramètre le protocole utilisé.

Pour pouvoir dialoguer avec le serveur de mails, il faut appeler la méthode connect() en lui précisant le nom du serveur, le nom d'utilisateur et le mot de passe de l'utilisateur.

La méthode close() permet de libérer la connexion avec le serveur.

 

69.3.8. La classe Folder

La classe abstraite Folder représente un répertoire dans lequel les messages sont stockés. Pour obtenir une instance de cette classe, il faut utiliser la méthode getFolder() d'un objet de type Store en lui précisant le nom du répertoire.

Avec le protocole POP3 qui ne gère qu'un seul répertoire, le seul possible est « INBOX ».

Pour pouvoir être utilisé, il faut appeler la méthode open() de la classe Folder en lui précisant le mode d'utilisation : READ_ONLY ou READ_WRITE.

Pour obtenir les messages contenus dans le répertoire, il faut appeler la méthode getMessages(). Cette méthode renvoie un tableau de Message qui peut être null si aucun message n'est renvoyé.

Une fois les opérations terminées, il faut fermer le répertoire en utilisant la méthode close().

 

69.3.9. Les propriétés d'environnement

JavaMail utilise des propriétés d'environnement pour recevoir certains paramètres de configuration. Ils sont stockés dans un objet de type Properties.

L'objet Properties peut contenir un certain nombre de propriétés qui possèdent des valeurs par défaut :

Propriété

Rôle

Valeur par défaut

mail.store.protocol

Protocole de stockage du message

le premier protocole concerné dans le fichier de configuration

mail.transport.protocol

Protocole de transport par défaut

le premier protocole concerné dans le fichier de configuration

mail.host

Serveur de mails par défaut

localhost

mail.user

Nom de l'utilisateur pour se connecter au serveur de mails

user.name

mail.protocol.host

Serveur de mails pour un protocole dédié

mail.host

mail.protocol.user

Nom de l'utilisateur pour se connecter au serveur de mails pour un protocole dédié

 

mail.from

adresse par défaut de l'expéditeur

user.name@host

mail.debug

mode de débogage par défaut

 

Attention : l'utilisation de JavaMail dans une applet implique de fournir explicitement toutes les valeurs des propriétés utiles car une applet ne peut pas accéder à ces valeurs.

L'usage de certains serveurs de mails nécessite l'utilisation d'autres propriétés.

 

69.3.10. La classe Authenticator

Authenticator est une classe abstraite qui propose des méthodes de base pour permettre d'authentifier un utilisateur. Pour l'utiliser, il faut créer une classe fille qui se chargera de collecter les informations. Plusieurs méthodes sont à redéfinir selon les besoins :

Méthode

Rôle

String getDefaultUserName()

 

PasswordAuthentication getPasswordAuthentication()

 

int getRequestingPort()

 

String getRequestingPort()

 

String getRequestingProtocol()

 

InetAddress getRequestingSite()

 

Par défaut, la méthode getPasswordAuthentication() de la classe Authentication renvoie null. Cette méthode renvoie un objet PasswordAuthentication à partir d'une source de données (boîte de dialogue pour saisie, base de données, ...).

Une instance d'une classe fille de la classe Authenticator peut être fournie à la session. L'appel à Authenticator sera fait selon les besoins par la session.

 

69.4. L'envoi d'un e-mail par SMTP

Pour envoyer un e-mail via SMTP, il faut suivre les principales étapes suivantes :

  • Positionner les variables d'environnement nécessaires
  • Instancier un objet Session
  • Instancier un objet Message
  • Mettre à jour les attributs utiles du message
  • Appeler la méthode send() de la classe Transport
Exemple :
import javax.mail.internet.*;
import javax.mail.*;
import java.util.*;

/**
 * Classe permettant d'envoyer un mail.
 */
public class TestMail {
  private final static String MAILER_VERSION = "Java";
  public static boolean envoyerMailSMTP(String serveur, boolean debug) {
    boolean result = false;
    try {
      Properties prop = System.getProperties();
      prop.put("mail.smtp.host", serveur);
      Session session = Session.getDefaultInstance(prop,null);
      Message message = new MimeMessage(session);
      message.setFrom(new InternetAddress("moi@chez-moi.fr"));
      InternetAddress[] internetAddresses = new InternetAddress[1];
      internetAddresses[0] = new InternetAddress("moi@chez-moifr");
      message.setRecipients(Message.RecipientType.TO,internetAddresses);
      message.setSubject("Test");
      message.setText("test mail");
      message.setHeader("X-Mailer", MAILER_VERSION);
      message.setSentDate(new Date());
      session.setDebug(debug);
      Transport.send(message);
      result = true;
    } catch (AddressException e) {
      e.printStackTrace();
    } catch (MessagingException e) {
      e.printStackTrace();
    }
    return result;
  }
   
  public static void main(String[] args) {
    TestMail.envoyerMailSMTP("10.10.50.8",true);
  }
}

javac -classpath activation.jar;mail.jar;smtp.jar TestMail.java

java -classpath .;activation.jar;mail.jar;smtp.jar TestMail

 

69.5. La récupération des messages d'un serveur POP3

 

en construction
Cette section sera développée dans une version future de ce document



 

69.6. Les fichiers de configuration

Ces fichiers permettent d'enregistrer des implémentations de protocoles supplémentaires et des valeurs par défaut. Il existe 4 fichiers répartis en deux catégories :

  • javamail.providers et javamail.default.providers
  • javamail.address.map et javamail.default.address.map

JavaMail recherche les informations contenues dans ces fichiers dans l'ordre suivant :

1.  $JAVA_HOME/lib

2.  META-INF/javamail.xxx dans le fichier jar de l'application

3.  MATA-INF/javamail.default.xxx dans le fichier jar de javamail

Il est aussi possible d'utiliser son propre fichier sans faire de modification dans le fichier jar de JavaMail. Cette utilisation peut se faire sur le poste client ou dans le fichier jar de l'application, ce qui offre une grande souplesse.

 

69.6.1. Les fichiers javamail.providers et javamail.default.providers

Ce sont deux fichiers au format texte qui contiennent la liste et la configuration des protocoles pour lesquels le système dispose d'une implémentation. L'application peut ainsi rechercher la liste des protocoles utilisables.

Chaque protocole est défini en utilisant des attributs avec la forme nom=valeur suivi d'un point virgule. Cinq attributs sont définis (leurs noms doivent être en minuscules) :

Nom de l'attribut

Rôle

Présence

protocol

nom du protocole

obligatoire

type

type protocole : « store » ou « transport »

obligatoire

class

nom de la classe contenant l'implémentation du protocole

obligatoire

vendor

nom du fournisseur

optionnelle

version

numéro de version

optionnelle


Exemple : le contenu du fichier META-INF/javamail.default.providers
# JavaMail IMAP provider Sun Microsystems, Inc
protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc;
# JavaMail SMTP provider Sun Microsystems, Inc
protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems,
 Inc;
# JavaMail POP3 provider Sun Microsystems, Inc
protocol=pop3; type=store; class=com.sun.mail.pop3.POP3Store; vendor=Sun Microsystems, Inc;

 

69.6.2. Les fichiers javamail.address.map et javamail.default.address.map

Ce sont deux fichiers au format texte qui permettent d'associer un type de transport avec un protocole. Cette association se fait sous la forme nom=valeur suivie d'un point virgule.

Exemple : le contenu du fichier META-INF/javamail.default.address.map
rfc822=smtp

 

 


[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

78 commentaires Donner une note à l´article (5)

 

Copyright (C) 1999-2022 Jean-Michel DOUDOUX. Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU, Version 1.1 ou toute autre version ultérieure publiée par la Free Software Foundation; les Sections Invariantes étant constitués du chapitre Préambule, aucun Texte de Première de Couverture, et aucun Texte de Quatrième de Couverture. Une copie de la licence est incluse dans la section GNU FreeDocumentation Licence. La version la plus récente de cette licence est disponible à l'adresse : GNU Free Documentation Licence.