Le Java Message Service (JMS) est une API standard de la plateforme Java permettant aux applications de communiquer de manière asynchrone. En tant qu'interface pour les middlewares orientés messages (MOM), JMS permet de découpler les composants d'un système distribué : l'expéditeur et le destinataire n'ont pas besoin d'interagir simultanément ni de connaître les détails techniques l'un de l'autre.
1. Architecture Fondamentale de JMS
L'écosystème JMS repose sur plusieurs composants clés qui structurent la communication entre les clients :
- Fournisseur JMS (Provdier) : L'implémentation concrète du système de messagerie (ex: ActiveMQ, RabbitMQ avec plugin, IBM MQ).
- Producteur JMS : L'application ou l'objet chargé de créer et d'expédier les messages.
- Consommateur JMS : L'entité qui reçoit et traite les messages.
- Destination : L'adresse logique où les messages sont envoyés (File ou Thème).
Les objets programmatiques
| Objet | Rôle |
|---|---|
| ConnectionFactory | Objet configuré par l'administrateur pour créer des connexions vers le fournisseur. |
| Connection | Représente le canal de communication actif (souvent une socket TCP/IP) entre le client et le serveur. |
| Session | Contexte monothreadé pour produire et consommer des messages. Elle gère également les transactions. |
| MessageProducer / Consumer | Objets créés par la session pour envoyer ou recevoir des données vers une destination précise. |
2. Modèles de Messagerie
JMS définit deux modèles principaux pour l'échange de données :
Modèle Point-à-Point (PTP)
Ce modèle utilise des Files (Queues). Chaque message envoyé à une file est destiné à un seul consommateur. Une fois le message traité et acquitté, il est supprimé de la file. Ce modèle garantit que le travail n'est effectué qu'une seule fois, même si plusieurs consommateurs écoutent la même file.
Modèle Publication/Abonnement (Pub/Sub)
Ce modèle repose sur des Thèmes (Topics). Un message publié sur un thème est diffusé à tous les abonnés actifs. Il existe une dépendance temporelle : par défaut, un abonné ne reçoit que les messages publiés pendant qu'il est connecté, sauf si un "abonnement durable" est configuré.
3. Anatomie d'un Message JMS
Un message JMS est composé de trois sections distinctes : l'en-tête, les propriétés et le corps.
public interface Message {
// Gestion de la destination et du mode de livraison
Destination getJMSDestination() throws JMSException;
void setJMSDestination(Destination destination) throws JMSException;
int getJMSDeliveryMode() throws JMSException;
void setJMSDeliveryMode(int deliveryMode) throws JMSException;
// Identification et horodatage
String getJMSMessageID() throws JMSException;
void setJMSMessageID(String id) throws JMSException;
long getJMSTimestamp() throws JMSException;
void setJMSTimestamp(long timestamp) throws JMSException;
// Corrélation pour les flux requête-réponse
String getJMSCorrelationID() throws JMSException;
void setJMSCorrelationID(String correlationID) throws JMSException;
Destination getJMSReplyTo() throws JMSException;
void setJMSReplyTo(Destination replyTo) throws JMSException;
// Métadonnées supplémentaires
String getJMSType() throws JMSException;
void setJMSType(String type) throws JMSException;
}
A. L'En-tête (Header)
Certains champs sont remplis automatiquement par le fournisseur au moment de l'appel à la méthode send(). Toute tentative manuelle de modification de ces champs par le développeur avant l'envoi sera généralement écrasée par le système.
- JMSDeliveryMode : PERSISTENT (le message survit au redémarrage du serveur) ou NON_PERSISTENT.
- JMSExpiration : Définit la durée de vie du message.
- JMSPriority : De 0 (basse) à 9 (haute).
- JMSRedelivered : Indique si le message a déjà été envoyé mais n'a pas été acquitté avec succès.
D'autres champs sont à la charge du développeur, comme JMSReplyTo (pour spécifier une file de retour) ou JMSCorrelationID (pour lier une réponse à une requête spécifique).
B. Les Propriétés (Properties)
Les propriétés permettent d'ajouter des métadonnées personnalisées au message. Elles sont souvent utilisées pour le filtrage via des Sélecteurs de messages. Une fois le message envoyé, ces propriétés deviennent en lecture seule.
// Exemple de définition de propriétés personnalisées
TextMessage alerte = session.createTextMessage("Signal d'erreur système");
alerte.setStringProperty("NiveauCritique", "HAUT");
alerte.setIntProperty("CodeErreur", 503);
producteur.send(alerte);
C. Le Corps du Message (Body)
JMS propose cinq types de corps pour s'adapter aux différents besoins de données :
| Type de Message | Description |
|---|---|
| TextMessage | Contient une chaîne de caractères (JSON, XML, texte brut). |
| MapMessage | Ensemble de paires clé/valeur (types primitifs). |
| BytesMessage | Flux de données binaires brutes. |
| StreamMessage | Flux de types primitifs Java lus de manière séquentielle. |
| ObjectMessage | Contient un objet Java sérialisable. |
4. Réception des Messages
Le consommateur peut récupérer les messages de deux façons :
- Synchrone : En appelant la méthode
receive(). Le thread est bloqué jusqu'à l'arrivée d'un message ou l'expiration d'un délai. - Asynchrone : En enregistrant un
MessageListener. La méthodeonMessage(Message m)est alors invoquée automatiquement par le fournisseur JMS dès qu'un message est disponible.
// Exemple de réception asynchrone
consommateur.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message msg) {
try {
if (msg instanceof TextMessage) {
String contenu = ((TextMessage) msg).getText();
System.out.println("Traitement : " + contenu);
}
} catch (JMSException e) {
e.printStackTrace();
}
}
});