Niveau : | Intermédiaire |
Le courrier électronique repose sur le concept du client/serveur. Ainsi, l'utilisation d'e-mails requiert deux composants :
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 J2SE. Pour l'utiliser, il est possible de la télécharger sur le site de SUN : http://java.sun.com/products/javamail. Elle est intégrée au J2EE.
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 :
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 :
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.
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.
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.
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 ...
NNTP est l'acronyme de Network News Transport Protocol. Ce protocole est utilisé par les forums de discussions (news).
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.
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 :
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.
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 :
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.
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 |
La classe abstraite Message encapsule un Message. Le message est composé de deux parties :
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 :
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 :
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);
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é |
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 :
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.
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.
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().
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.
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.
Pour envoyer un e-mail via SMTP, il faut suivre les principales étapes suivantes :
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
|
Cette section sera développée dans une version future de ce document
|
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 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.
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;
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