1. Abdellaziz Walid Département GI
a.walid@uiz.ac.ma 2022-2023
MoM : Message Oriented Middleware
JMS : Java Messaging Service
Architecture Orientée Message
MoM : JMS
2. Contexte asynchrone dans les applications
distribués
2
❑ Parfois un client souhaite envoyer un message sans
attendre une réponse de la part de serveur.
(communication par message).
❑ Dans ce cas, le client et serveur ne sont pas obligés de
partager une interface.
❑ Le serveur n’est pas censé d’être disponible au moment
de l’envoi d’un message par un client.
❑ Le serveur n’est pas censé de consommer le message
immédiatement.
❑ Les applications client doivent communiquer entre eux
de façon asynchrone.
3. Les limites des middlewares de type RPC
3
❑ Les (Objet RMI, EJB de type session, service web Soap/Rest)
assurent une communication de type RPC seulement
(synchrone).
❑ Dans ce type de communication, Un client attend pendant que
le serveur effectue le traitement d'une requête. (bloquante)
❑ Couplage fort entre client et serveur (partage la même
interface)
❑ Le client doit connaître l’emplacement du serveur (référence).
❑ Dépendance temporelle entre serveur et client.
❑ Pas de diffusion (Unicast seulement).
Comment dépasser ces limites ??
Solution : Middleware orienté message (MoM)
4. Middleware orienté message (MoM)
4
❑ Proposent un modèle simple et fiable pour l’échange de
messages dans une application répartie.
❑ Utilisent un des modèles de communication les plus
anciens.
❑ Sont utilisés dans les systèmes de grande dimension
✓ Réseaux bancaires
✓ Télécommunications
✓ Systèmes de réservation, commerce
✓ … etc.
5. MoM : caractéristiques
5
→ Garantie de délivrance de messages
→ Support des transactions
→ Gestion du routage
→ Passage à l’échelle
→ Support pour la configuration (politiques de QoS)
→ Composants faiblement couplés
- Pas de dépendance d’interface
- Pas de référence directe
- Pas de dépendance temporelle – l’exécution de l’émetteur n’est
pas interrompue si le destinataire n’est pas disponible.
- Communication asynchrone / non-bloquante (pas de réponse
implicite, sauf ack.)
7. MoM : modèles de communication par message
7
Modèle point à point (Point to Point)
Modèle Publication/Abonnement (Publish/Subscribe)
8. MoM : Modèle point à point
8
❑ Chaque message est stocké dans une fille (« Queue ») jusqu’à ce que le destinataire
le lise
❑ Chaque message est consommé une seule fois (par un seul destinataire)
❑ Pas de dépendance temporelle entre émetteur et destinataire du message
❑ Le destinataire peut acquitter les messages reçus
9. MoM : Modèle Publication/Abonnement
9
❑ Sujet (« Topic ») gère l’envoi de messages pour un ensemble de lecteurs abonnés
❑ Découplage entre les « publishers » et les « subscribers »
❑ Les fournisseurs n’ont pas besoins de connaître les consommateurs
❑ Dépendance temporelle
❑ Un client ne peut lire un message qu’après s’être abonné à un topic,
❑ Un client abonné à un topic doit continuer à être actif pour recevoir les message du
topic.
10. MoM : Exemples
10
❑ Websphere MQ IBM
❑ ActiveMQ
❑ Open MQ
❑ Open JMS
❑ Apache Kafka
❑ Apache Active MQ
❑ RabbitMQ
11. Java Message Service (JMS) - concepts
11
❑ JMS : L’implémentation d’un Middleware orienté message
(spécifications JMS).
❑ JMS : API standard de Java EE qui assure la
communication par message (asynchrone).
❑ Il existe plusieurs implémentations externe de JMS tant
commerciales que open source ( Websphere MQ IBM,
ActiveMQ, Open MQ,Open JMS…etc)
❑ Les applications utilisent généralement JMS dans les
architectures de type B2B (Business to Business).
❑ En effet, cette API permet d'interconnecter n'importe quel
système utilisant le principe de messagerie où l'envoi et la
réception de message sont asynchrones.
❑ Il permet d’assurer de communications de type unicast,
multicast.
13. Architecture JMS
13
❑ Un fournisseur : (Provider) :
❑ C'est l'élément qui a la charge de la livraison des messages entre les
différents intervenants.
❑ Il s'occupe de traiter les envois et de faire en sorte qu'ils soient bien
reçus.
❑ Il s'agit d'un service qui implémente l'API JMS.
❑ Un client :
❑ C'est une application ou un composant d'application intervenant lors des
échanges.
❑ Il envoie ou reçoit les messages.
❑ Un client envoie un message vers une file d'attente, et le client
destinataire reste à l'écoute d'une file d'attente pour recevoir le message.
❑ Le transfert du message et sa persistance sont assurés par le fournisseur.
14. Architecture JMS
14
❑ Un message :
❑ L'élément qui va transiter via une communication entre les clients.
❑ Un fournisseur sert toujours d'intermédiaire; nous ne les envoyons
donc pas directement d'un client à un autre.
❑ Un message est un ensemble de données échangées de manière
asynchrone entre les composants.
❑ Il existe plusieurs types de messages (texte, objet, binaire, etc.).
❑ Les destinations :
❑ Ce sont des objets configurés au niveau du fournisseur qui sont à
disposition des clients et qui seront utilisés par ces derniers pour l'envoi
et la réception des messages.
❑ Pour schématiser, nous pouvons dire qu'il s'agit de boîtes à lettres dans
lesquelles sont placées les messages en attendant qu'un client vienne les
réclamer.
❑ Ce sont des ressources à rechercher dans l'annuaire JNDI du
fournisseur.
15. Modèles de messagerie
15
JMS offre deux modèles de messagerie :
❑ Le mode point à point utilise les files d'attente (javax.jms.Queue) pour
communiquer.
❑ Le mode publication/abonnement utilise des sujets (javax.jsm.Topic) pour
échanger des messages.
❑ Chaque mode utilise une interface différente pour envoyer des messages :
▪ javax.jms.QueueSender dans le mode point à point.
▪ javax.jms.TopicPublisher pour le mode publication/abonnement.
❑ Tous les deux héritent de la super interface javax.jms.MessageProducer
16. Modèle de messagerie : Mode point à point
16
❑ Chaque message est envoyé par un producteur dans une file
d'attente, et est reçu par un seul consommateur.
❑ Une fois le message consommé, il disparaît de la file
d'attente.
17. Modèle de messagerie : Mode publication/abonnement
17
❑ Un producteur peut envoyer un message à plusieurs consommateurs
par le biais d'un sujet (topic).
❑ Chaque consommateur doit cependant être préalablement inscrit à
ce sujet sinon il ne reçoit rien.
❑ l'émetteur du message ne connait pas les destinataires qui se sont
abonnés.
❑ Il existe deux types de souscription : temporaire et durable.
❑ Dans le cas d'une souscription durable, on oblige le fournisseur à
enregistrer les messages lors d'une déconnexion, et à les envoyer
lors de la nouvelle connexion du consommateur.
18. Les composants JMS
18
Il existe un certain nombre de composants qui s'occupe de la gestion
globale de JMS, et de permettre ainsi une communication asynchrone
entre applications clientes.
20. Les étapes de création des composants JMS
1. Localiser le driver JMS
❑ lookup JNDI. Le driver est une connection factory
2. Créer une connection JMS
❑ obtenir une connection à partir de la connection factory
3. Créer une session JMS
❑ Il s'agit d'un objet qui va servir à recevoir et envoyer des messages. On
l'obtient à partir de la connection.
4. Localiser la destination JMS
❑ Il s'agit du canal, c'est réglé par le déployeur. On obtient la destination
via JNDI.
5. Créer un producteur ou un consommateur JMS
❑ Utilisés pour écrire ou lire un message. On les obtient à partir de la de la
session et destination.
6. Envoyer ou recevoir un message
21. ConnectionFactory et Destination
21
❑ Pour travailler avec JMS, la première étape consiste d'abord à
se connecter au fournisseur JMS.
❑ Pour cela, nous devons récupérer un
objet ConnectionFactory via JNDI qui rend ainsi la connexion
possible avec le fournisseur.
❑ Cet objet peut être assimilé à une DataSource (en JDBC).
❑ une ConnectionFactory fournit une connexion JMS au service
de routage de message.
❑ L'autre élément à récupérer est la destination.
❑ Les destinations (Destination) sont des objets qui véhiculent
les messages.
❑ JMS comporte deux types de destination, comme nous venons
de le découvrir, les Queue et les Topic.
22. ConnectionFactory et Destination : exemple
22
Voici le code à proposer côté application cliente (plate-forme
indépendante Java SE) :
Context ctx = new InitialContext();
ConnectionFactory fabrique = (ConnectionFactory)
ctx.lookup("ConnectionFactory");
Destination destination = (Destination) ctx.lookup("queue/MaFile");
Voici une autre code où nous passons par un bean session qui nous
permet d'utiliser l'injection. Il suffit alors de spécifier
l'annotation @Resource :
@Resource(mappedName="ConnectionFactory")
private ConnectionFactory fabrique;
@Resource(mappedName="queue/MaFile")
private Destination destination;
23. Création du contexte JNDI
23
❑ Pour obtenir une ConnectionFactory, une Queue, ou un Topic, il faut les
rechercher par leur nom dans l'annuaire JNDI ou utiliser l'injection.
❑ Cela suppose donc que ces ressources soient préalablement mis en œuvre
et qu'elles soient recensées au travers du service d'annuaire JNDI.
❑ Les propriétés pour la création du contexte JNDI sont dépendantes du
fournisseur utilisé
jndi.properties (JBoss)
# Accès au serveur d'application JBoss
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=portable:1099
24. Connection et Session
24
❑ L'objet ConnectionFactory permet de créer une connexion avec le
fournisseur JMS.
❑ Une fois la connexion créée, elle est ensuite utilisée pour créer une
session.
❑ La session sert à regrouper toutes les opérations d'envoi et de
réception des messages.
❑ Dans la majorité des cas, une session unique est suffisante.
❑ La création de plusieurs sessions est utile seulement dans le cas
d'applications multi-tâches qui produisent et reçoivent des messages
en même temps.
❑ Effectivement, l'objet Session est mono-tâche, c'est-à-dire que ses
méthodes n'autorisent pas l'accès concurrent.
❑ Généralement, le thread qui crée l'objet Session utilise le
producteur et le consommateur de cette session.
25. Connection et Session : exemple
25
Connection connexion = fabrique.createConnection();
Session session = connexion.createSession(true, 0); //
createSession(transaction, accuséRéception);
❑ La méthode createSession() prend deux paramètres.
❑ Une session est un contexte transactionnel utilisé pour grouper un ensemble d'envois
ou de réceptions de messages dans une même unité de travail.
❑ Si vous désirez travailler avec plusieurs messages pour une même session, vous
devez autoriser le mode transactionnel dans le premier argument de la fonction.
❑ Le deuxième argument est utile pour savoir si vous désirez qu'un accusé
réception soit renvoyé afin de préciser que message est bien arrivé à sa
destination. (AUTO_ACKNOWLEDGE).
N.B : N’oublier pas de fermer les connexions une fois le travail terminé.
Connection connexion = fabrique.createConnection();
connexion.close();
26. MessageProducer et MessageConsumer
26
❑ La dernière étape nous sert à préciser le sens du transfert du message, est-ce
pour envoyer ou est-ce pour recevoir ?
❑ Deux objets correspondent à ces deux situations,
respectivement MessageProducer et MessageConsumer :
//envoi
MessageProducer envoi = session.createProducer(destination);
envoi.send(message);
//réception
MessageConsumer reception = session.createConsumer(destination);
…………………………..
❑ Chacune des méthodes de l'objet session prend en paramètre la destination sur
laquelle l'objet est connecté.
27. Composants JMS suivant le mode de
communication
27
❑ ConnectionFactory,Destination,MessageProducer et MessageConsumer sont
en réalité des interfaces génériques, que nous pouvons utiliser directement.
❑ Il est toutefois possible, dès le départ, de proposer des interfaces plus
spécifiques, qui héritent d'ailleurs de ces interfaces, correspondant
respectivement au mode point à point ou au mode publication/abonnement.
Générique point à point publication/abonnement
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiver TopicSuscriber
29. Les messages
29
❑ Les clients JMS s'échangent des messages, c'est-à-dire
qu'un client expédie un message vers une file d'attente, et
qu'un client destinataire exécutera un traitement à la
réception de ce message.
❑ Dans JMS, un message est un objet Java qui doit
implémenter l'interface javax.jms.Message. Il est composé
de trois parties :
• L'en-tête (header) : qui se compose des informations de
destination, d'expiration, de priorité, date d'envoi, etc.
• Les propriétés (properties) : qui représentent les caractéristiques
fonctionnelles du message.
• Le corps du message (body) : qui contient les données à
transporter.
30. Les messages : en-tête
30
Nom Description
JMSMessageID identifiant unique de message
JMSCorremationID
Utilisé pour associer de façon applicative deux
messages par leur identifiant.
JMSDeliveryMode
Il existe deux modes d'envoi : persistant et non
persistant (le message peut ne pas être délivré en cas
de panne puisqu'il n'est pas rendu persistant).
JMSDestination File d'attente destinataire du message.
JMSExpiration Date d'expiration du message.
JMSPriority
Priorité du message. Cet attribut indique la priorité de
façon croissante à partir de 0 (les messages de niveau
9 ont plus de priorité que les messages de niveau 0).
JMSRedelivered
Booléen qui signifie que le message a été redélivré au
destinataire.
JMSReplyTo File d'attente de réponse du message.
JMSTimestamp
L'heure d'envoi du message est affecté
automatiquement par le fournisseur.
31. Les messages : Les propriétés
31
❑ Cette section du message est optionnelle et agit
comme une extension des champs d'en-tête.
❑ Les propriétés d'un message JMS sont des couples
(nom, valeur), où la valeur est un type de base du
langage Java (entiers, chaînes de caractères,
booléens, etc.).
❑ L'interface javax.jms.Message définit des accesseurs
pour manipuler ces valeurs. Ces données sont
généralement positionnées par le client avant l'envoi
d'un message.
32. Les messages : le corps
32
Interface Description
javax.jms.BytesMessage
Pour les messages sous forme de flux
d'octets.
javax.jms.TextMessage Echange de données de type texte.
javax.jms.ObjectMessage
Messages composés d'objets Java
sérialisés.
javax.jms.MapMessage
Echange de données sous la
forme clé/valeur. La clé doit être
une String et la valeur de type primitif.
javax.jms.StreamMessage
Echange de données en provenance d'un
flux.
❑ Le corps du message contient les données.
❑ Ces données sont formatées selon le type du message qui est défini
par les interfaces suivantes (qui héritent toutes de javax.jms.Message) :
34. Exemple : Envoyer un objet
34
class Personne implements Serializable { ... }
////////////////////////////////////////////////////////////////////////////////////////
Personne moi = new Personne();
Session session = connexion.createSession(true, 0);
MessageProducer envoi = session.createProducer(destination);
ObjectMessage message = session.createObjectMessage();
message.setObject(moi);
envoi.send(message);
35. Comment envoyer un message ?
35
❑ Tout d'abord, la fabrique de connexion (ConnectionFactory) et
la destination (Destination) doivent être connues par le client
JMS.
❑ Une fois la référence de la ConnectionFactory obtenue, on se
connecte au provider (fournisseur) JMS via
l'objet Connection.
❑ A partir de cette connexion, nous devons obtenir une session
(Session).
❑ A partir de cette session, nous devons créer
un MessageProducer qui va permettre d'envoyer des messages
auprès d'une destination.
❑ La session permet également de créer le message suivant le
type choisi.
36. Exemple
36
Context ctx = new InitialContext();
ConnectionFactory fabrique = (ConnectionFactory)ctx.lookup("ConnectionFactory");
Destination destination = (Destination)ctx.lookup("queue/MaFile");
Connection connexion = fabrique.createConnection();
Session session = connexion.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer envoi = session.createProducer(destination);
TextMessage message = session.createTextMessage();
message.setText("Bienvenue");
message.setText(" à tout le monde");
envoi.send(message);
connexion.close();
37. Comment recevoir un message ?
37
❑ Comme l'envoi d'un message, la fabrique de connexion
(ConnectionFactory) et la destination (Destination) doivent être connues
par le client JMS.
❑ Une fois la référence de la ConnectionFactory obtenue, le consommateur
doit se connecter au provider (fournisseur) JMS via l'objet Connection.
❑ A partir de cette connexion, nous devons obtenir une session (Session).
❑ A partir de la session, on crée un MessageConsumer qui va permettre de
consommer les messages.
❑ Pour ce faire, nous associons un listener MessageListener pour traiter les
messages de façon asynchrone. Ainsi, à chaque réception d'un nouveau
message, la méthode onMessage() est automatiquement invoquée et peut
effectuer le traitement désiré.
N.B : Il ne faut surtout pas oublier de démarrer la connexion avec la
méthode start() sinon aucun message ne sera reçu.
39. Exemple (Suite)
39
public static void main(String[] args) throws Exception {
new Réception();
}
public void onMessage(Message arg) {
try {
TextMessage message = (TextMessage) arg;
System.out.println(message.getText());
System.out.println(message.getText());
} catch (Exception ex) { }
}
}
40. Problématique
Dans le cas de l’exécution d’une méthode métier à l’écoute
d’un nouveau message (ex: modification de la base de
données, envoi d’email, fax ou autres), il serait nécessaire
de coder une application à l’écoute des messages, qui
délègue ensuite le traitement métier à un EJB de type session
bean.
❑ Cette solution est difficile à développer, peu fiable, car
l’application intermédiaire ajoute un niveau architecturel
❑ Susceptible de tomber en panne. Il est donc nécessaire de
réaliser l’écoute JMS directement au niveau de l’EJB métier.
❑ Ce mécanisme utilisant un EJB serait également envisageable,
mais un EJB est conçu pour répondre à des sollicitations
externes, non pour être à l’écoute d’évènements.
41. Solution : Message-Driven Bean
MDB consomme des messages depuis les queues ou topics,
envoyés par les clients JMS.
42. Qu'est-ce qu'un Message-Driven Bean ?
❑ Un client n'accède pas à un MDB via une interface, il utilise l'API
JMS.
❑ Un MDB n'a pas d'interface.
❑ Les MDB possèdent une seule méthode, faiblement typée :
onMessage()
- Elle accepte un message JMS (BytesMessage, ObjectMessage,
TextMessage, StreamMessage ou MapMessage)
❑ Les MDB n'ont pas de valeur de retour, ils sont découplés des
producteurs de messages.
43. Qu'est-ce qu'un Message-Driven Bean ?
❑ Les MDB ne renvoient pas d'exceptions au client (mais au
contenueur EJB)
❑ Les MDB sont stateless.
❑ Les MDB peuvent être des abonnés durables ou non-durables à un
topic. (par défaut non durable)
❑ Annotation pour les rendre durable :
@ActivationConfigProperty(propertyName="subscriptionDurability",
propertyValue="Durable") })
45. Développer un Message-Driven Bean
1) Nous devons en premier lieu utiliser l'annotation @MessageDriven et
spécifier le paramètre mappedName dont la valeur correspond à la
destination du service JMS.
2) Une fois que vous avez réalisé cette opération, vous pouvez choisir le
nom de votre classe.
3) Il faut que le MDB implémente l'interface MessageListener et redéfinisse
donc la méthode associée onMessage() qui sera donc automatiquement
sollicité à chaque arrivée d'un nouveau message.
4) Remarquez qu'avec les MDB, vous n'avez plus à vous préoccuper des
objets annexes comme Connection, Session, MessageConsumer. Tout se
fait automatiquement en tâche de fond.
5) Comme tous les autres beans, le MDB possède des méthodes callback
interceptor qui sont appelées respectivement à la suite de la construction
de l'objet et juste avant sa destruction. Ces méthodes possèdent alors les
annotations suivantes : @PostConstruct et @PreDestroy.
48. Le client
import javax.naming.*;
import javax.jms.*;
import java.util.*;
public class Client {
public static void main(String[] args) throws Exception{
// Initialize JNDI
Context ctx =
new InitialContext(System.getProperties());
// 1: Lookup ConnectionFactory via JNDI
TopicConnectionFactory factory = (TopicConnectionFactory)
ctx.lookup("javax.jms.TopicConnectionFactory");
// 2: Create JMS connection
TopicConnection connection =factory.createTopicConnection();
49. Le client (suite)
// 3: Use Connection to create session
TopicSession session = connection.createTopicSession( false,
Session.AUTO_ACKNOWLEDGE);
// 4: Lookup Destination (topic) via JNDI
Topic topic = (Topic) ctx.lookup("testtopic");
// 5: Create a Message Producer
TopicPublisher publisher =session.createPublisher(topic);
// 6: Create a text message, and publish it TextMessage msg = session.createTextMessage();
msg.setText("This is a test message.");
publisher.publish(msg);
}
}