es middlewares orientés messages ou plus simplement
MOM constituent un des
piliers du système d'information. Dans cet article nous allons essayer de
mieux comprendre la place qu'ils occupent dans nos deux plate-formes de
prédilection, J2EE et .NET.
Après avoir brièvement rappelé les principes fondamentaux des MOMs, nous étudierons l'architecture des solutions retenues puis nous comparerons les manières d'implémenter les principaux cas d'utilisation en java et en C#.
La communication entre applications a toujours été un problème majeur des systèmes distribués. Il existe de nombreuses solutions à ce problème que l'on peut regrouper en deux grandes familles :

Ce mode de communication s'appelle "point à point". Les messages sont échangés grâce à un intermédiaire qui est une file d'attente de messages : l'application productrice dépose le message dans la file qui y est conservé jusqu'à ce que l'application destinatrice le récupère. Une communication point à point est différentes d'une communication application vers application (A2A) : premièrement le producteur ne spécifie pas une application cible mais une file d'attente (généralement par son nom) et deuxièmement plusieurs applications peuvent lire ou écrire des messages dans une file donnée.

Les principales caractéristiques de ce type de middleware sont :
Les principaux produits offrant ce mode de communication sont MQSeries d'IBM et MSMQ de Microsoft.
Ce mode de communication est du point à multipoints : chaque message est associé à un sujet, quand on veut recevoir un message on l'indique en s'abonnant (subscribe) à un sujet, dès qu'un message est publié (publish) le MOM envoie une copie du message à tous les abonnés. Ce système est très proche des newsgroups (forums de discussion) bien connus des utilisateurs internet.
Les sujets sont simplement des chaînes de caractères qui décrivent un concept, par exemple on utilisera le sujet "dotnet" pour tous les messages se rapportant au framework .NET. Par ailleurs, les sujets sont généralement hiérarchiques (dotnet.messaging) et l'on peut s'abonner à des familles de sujets grâce à l'utilisation du caractère générique ('*'), ainsi un client abonné au sujet dotnet.* recevra tous les messages des sujets dotnet.messaging et dotnet.remoting mais aucun message du sujet j2ee.messaging (;-)).

Les principales caractéristiques de ce type de middleware sont :
Le principal produit de cette famille est TIBCO-RV de Tibco qui a été l'inventeur de cette technologie.
Ces deux familles de technologies répondent à des besoins précis et il leur faudrait un article dédié pour les comparer dans le détail, nous allons simplement évoquer le problème du couplage entre application.
Un des critères que l'on peut appliquer lors d'une analyse ou d'une définition d'une architecture est le degré de liberté (ou de couplage possible) entre plusieurs applications, pour cela on se pose un ensemble de questions telles que :
Les appels de procédure à distance sont typiquement des technologies imposant un couplage fort entre les applications parce qu'elles imposent aux clients et aux serveurs d'être actif en même temps et qu'elles ne savent pas gérer l'évolution des composants (toute modification de l'interface du serveur implique généralement une modification du client). A l'opposé, les MOMs favorisent le découplage car elles proposent des modes de communication très souples, les applications ne communiquent plus directement entre elles mais s'échangent des messages par l'intermédiaire d'un médiateur. Ainsi ce genre d'outils est une brique indispensable pour l'intégration de système ou d'application.
Dans ce chapitre nous allons présenter à haut niveau les possibilités offertes par les deux plates-formes J2EE et .NET.
Sun a choisi vis à vis des éditeurs de MOM une politique similaire à celle de JDBC vis à vis des éditeurs de base de données : définir un modèle de programmation basé sur un ensemble d'interfaces portables quel que soit l'implémentation utilisée. Le but est de normaliser l'accès aux MOM indépendamment de la structure et de l'architecture interne de l'outil.
La spécification de SUN s'appelle JMS pour "Java Messaging Service", elle traite les deux modes de communication que nous avons vu précédemment, elle a été développée par les principaux acteurs de ce domaine (IBM, Tibco, BEA, ...) et possède de nombreuses implémentations.
Le schéma suivant nous montre les principales interfaces de cette API pour la gestion des files de messages, elles sont regroupées dans le package javax.jms, et nous les étudierons par la pratique dans le chapitre suivant.

Il existe le même type de schéma pour le mode publication/abonnement de JMS, le modèle de programmation est équivalent, il suffit de remplacer les concepts fondamentaux (une Queue par un Topic, etc ...).
Microsoft a intégré dans sa gamme de produit un middleware à base de file de messages nommé MSMQ pour MicroSoft Message Queuing. Ce service a fait son apparition dans Windows NT4 en octobre 98 (workstation et server) mais son architecture a été complètement modifiée dans sa version 2.0 pour mieux s'intégrer à la plate-forme Windows 2000.
Les grands principes de cette architecture sont les suivants : les files publiques sont accessibles par n'importe quel type de client, elles sont gérées par les serveurs MSMQ et enregistrées dans l'annuaire Active Directory. Chaque serveur gère physiquement un ensemble de files, les émissions de messages vers des serveurs distants utilisent le mécanisme de Store and Forward. Il existe deux types de clients : les clients dépendants qui ne peuvent fonctionner que s'ils sont connectés à un serveur MSMQ et les clients indépendants. Les client indépendants peuvent travailler en mode déconnecté, les messages sont stockés dans une file locale puis re-synchronisé avec le serveur quand la connexion est disponible ("store and forward"). Il est de plus possible de définir des files privées, ces files sont disponibles sur les clients indépendants et ne sont pas publiées dans la topologie du réseau, on peut les utiliser indépendamment des serveurs MSMQ, on a dans ce cas des communications directes.
Le schéma suivant résume la nouvelle architecture de MSMQ.

Les serveurs MSMQ doivent absolument être installés sur des machines Windows 2000 Server et au moins l'un des serveurs doit être contrôleur de domaine (PDC, BDC). De plus, les clients doivent être dans le même domaine que le serveur qu'ils utilisent ce qui peux poser des problèmes de configuration ou de déploiement d'applications. Lors de l'installation, si aucun serveur n'est disponible dans le domaine, on ne pourra créer que des clients indépendants fonctionnant avec des files privées.
Une fois installé MSMQ nous propose un ensemble d'outils d'administration très puissant nous permettant de gérer l'ensemble de nos files. En particulier nous pouvons créer des files ou éditer un message comme le montre la copie d'écran suivant :

Ce produit est intégré au Framework .NET sous la forme d'une interface de programmation composée des classes du module System.Messaging, dans la suite de cet article nous allons reprendre dans le détail l'ensemble de ces classes.
La gestion du mode publication/abonnement n'est pas supportée par MSMQ. Microsoft propose une technologie similaire dans sa gamme d'outils COM+ (plus précisément les évènements COM+), même si cette technologie implémente ce mode de communication il ne s'agit pas d'échange de messages mais d'invocation de méthodes (c'est un système de délégation d'évènements répartis). Cette technologie n'est de toute façon pas intégrée au Framework .NET et les tentatives que nous avons faites ont montré trop de limitation pour que nous nous y intéressions plus dans cet article.
Dans ce chapitre nous allons comparer les possibilités des deux approches pour la gestion des files d'attentes. Pour cela, nous nous intéresserons aux différentes étapes de création et d'envoi d'un message (recherche d'une file d'attente, émission d'un message, ...).
La première activité consiste à créer une instance de l'objet matérialisant la file de message avec laquelle nous voulons communiquer. Nous pouvons, soit la rechercher, soit en créer une nouvelle. A noter que cette action est généralement prise en charge par les administrateurs du MOM (Remarque : on ne crée d'habitude que des files de messages temporaires utilisées pour simuler un mode requête / réponse).
Dans .NET les files de messages sont représentées par la classe MessageQueue, elle possède un constructeur prenant en paramètre le nom complet (path) de la file d'attente. Celui ci est une chaîne de caractère ayant la format "<nom machine>\private$\<nom de la file>" pour les files privées et "\\<nom de la file>" pour les files publiques
// -- Instanciation directe
Cette même classes possède des méthodes statiques permettant de lister l'ensemble des files d'attentes publiques ou privées en fonction de certains critères.
// -- Recherche des files publiques
Dans JMS les files de messages sont représentées par l'interface Queue, on y accède par le service d'annuaire JNDI comme pour tous objets distribués dans la plate-forme J2EE (EJB, Pool de connexion jdbc, ...). Sans rentrer dans les détails l'API JNDI permet de rechercher des objets dans des systèmes d'annuaire et ceci indépendamment de l'implémentation. Ainsi, il existe des implémentations pour des bases de données relationnelles, des systèmes de fichiers ou des annuaires LDAP tels que Active Directory.
Comme nous pouvons le voir dans l'exemple de code suivant, il est nécessaire des passer des informations aux MOM utilisé lors de l'initialisation du contexte JNDI, le code reste portable, il suffit simplement de changer cette propriété (on peut par exemple les externaliser dans un fichier de configuration ou dans des variables d'environnement spécifiques).
import javax.jms.*;
// package des interfaces JMS
Une fois la file destination récupéré nous devons créer le message qui sera envoyé par la suite.
Dans .NET les messages sont représentés par la classe System.Messaging.Message, les principales propriétés à spécifier sont :
// -- Création de l'objet du message
Dans JMS il existe un type d'objet message par type de contenu, tous ces objets héritent d'une interface commune javax.jms.Message.
Nous ne pouvons pas instancier directement l'un de ces objets comme nous l'impose le modèle de programmation JMS, il faut impérativement utiliser l'objet QueueSession (design pattern Factory). Cette démarche semble "lourde" mais elle sépare les responsabilités des classes et elle nous permet entre autre de spécifier des paramètres par défaut pour la construction d'un message (paramètre gérés par les outils d'administration du MOM).
Les headers (Propriétés) du message peuvent être positionnées par l'utilisateur ou par le MOM (valeur par défaut ou à l'émission du message), on y accède par des propriétés (set/get) de l'interface Message, il est également possible de définir ces propres propriétés.
InitialContext ic = ...
QueueConnectionFactory qcf = (QueueConnectionFactory) ic.lookup("primaryQCF");
QueueConnection qc = qcf.createQueueConnection();
QueueSession qs = qc.createQueueSession(
Dorénavant nous avons un message et une file, il nous suffit donc d'envoyer ce message en précisant les qualités de service pour la transmission du message envoyé.
.NET choisi la facilité une fois de plus, pour envoyer un message il suffit d'invoquer la méthode send sur la file d'attente, on peut également spécifier un label sur le message qui sera généralement utilisé pour identifier le type de message lors de la réception.
Dans JMS il nous faut créer un objet de type QueueSender pour émettre le message.
// -- Initialisation des objets (comme précédemment)
Maintenant que notre message a été posté, il nous faut développer un programme pour le recevoir. Il y a deux modes pour recevoir un message, soit on demande explicitement la réception d'un message, soit on demande d'être notifié par le système quand il y a réception d'un message dans la file. De plus, on désire parfois lire le message sans pour autant l'enlever de la file (rappelez vous que le message ne peut être reçu que par une application, ce n'est pas un mode publication/abonnement).
Dans .NET la réception d'un message se fait par l'intermédiaire de l'objet Queue que l'on initialise comme précédemment. Le framework offre deux méthodes pour lire un message : la méthode receive qui extrait le message de la file et la méthode peek qui lit le message sans l'extraire. Ces deux méthodes sont synchrones et bloque l'appelant si il n'y a pas de message, il est cependant possible de spécifier un time-out.
MessageQueue mq =
new MessageQueue (@".\private$\MyQueue");Ce mode est très simple mais il est difficilement utilisable avec des interfaces graphiques car on ne veut pas les bloquer, c'est pourquoi on utilisera une réception asynchrone qui est basée sur le principe des delegate (ce sont l'équivalent des pointeurs de fonctions) : on enregistre une méthode qui sera appelée sur la réception d'un évènement particulier, par exemple la réception d'un message.
public
void asynchronousReceive() {Une fois le message reçu nous allons vouloir récupéré son contenu et la méthode change en fonction du mode de formatage (cf. émission du message). Si le message a été formaté en binaire il n'y a rien à faire sinon préciser explicitement le type de l'objet afin de le désérialiser.
// -- Message contenant un objet du type SampleClass
Dans JMS nous retrouvons le même type de fonctionnalités, cependant il nous faut créer un objet supplémentaire (QueueReceiver) qui gère la réception sur une file de messages donnée, ainsi nous pouvons faire des récupérations de messages synchrones bloquantes ou non et avec ou sans time-out.
QueueSession qs = ...
Queue queue = ...
QueueReceiver qr = qs.createReceiver(queue);
Message msg1 = qr.receive();
La réception asynchrone se fait par l'intermédiaire d'une interface listener que l'on enregistre auprès du QueueReceiver. Ensuite, à chaque réception d'un message, la méthode onMessage est invoquée sur le listener.
// Listener pour la réception asynchrone
Jusqu'a présent nous recevons toujours tous les messages d'une file or plusieurs applications peuvent attendre sur une même file des messages de nature différente. Pour traiter ce problème, nous pouvons toujours lire les messages et les remettre s'ils ne nous intéressent pas, cependant cette approche n'est pas très efficace. C'est pourquoi JMS introduit la notion de filtre ou de sélecteur de messages que l'on peut associer à un récepteur de message. Ainsi, dans le cas d'une réception asynchrone, on ne recevra que les messages correspondant au filtre. Pour définir un filtre on utilise une expression booléenne basée sur les valeurs des headers du message et dont la syntaxe est inspirée du langage SQL, ces headers pouvant être défini dans la spécification (JMSType, JMSPriority), par le produit ou par l'utilisateur (ex : Color).
String filter = "JMSType = 'car' AND
color = 'blue' AND weight > 2500" ;
QueueReceiver qr = qs.createReceiver(queue, filter);
Microsoft et Java ont abordé les MOMs avec une approche complètement différentes. D'un côté, nous avons un produit dédié uniquement à l'échange de message en environnement Windows et de l'autre une spécification offrant un niveau d'abstraction vis à vis du fournisseur. Certes pour le mode file de messages, les deux plate-formes offrent des fonctionnalités équivalentes même si l'on regrettera simplement l'absence du mode publication/abonnement pour .NET.
Comme nous l'avons vu, les MOMs sont un des piliers de l'intégration d'application en particulier en raison du faible couplage qu'ils introduisent. A ce titre, la spécification JMS représente une véritable avancée dans le monde de l'EAI (Enterprise Application Integration) car on a enfin la possibilité de développer des applications portables. Malgré ces qualités, le Framework .NET n'est pas disposé à offrir ce niveau d'abstraction en privilégiant une implémentation spécifique. A quand un changement de philosophie ?
Spécification JMS http://java.sun.com/products/jms/index.html
Site sur MSMQ http://www.microsoft.com/msmq
Site sur le messaging en général http://www.messageq.com
Auteur : Guillaume RENAUD
Copyright : DotNetGuru ©