Aaah les transactions et JMS. Cela fait longtemps que j’avais envie de remettre le nez dans les spécifications de JMS. 5 mn sur Google et je trouve un bon article sur JMS API(Java? Message Service Application Programming Interface) qui présente en quelques pages le principe de JMS. JMS est une API Java destinée à l’échange de messages asynchrones entre application. JMS propose ce que JDBC a réussi à faire pour les bases de données: établir un standard pour l’échange de messages en Java. Cette API repose sur la présence d’un serveur qui répond à la définition JMS et qui joue le tiers de confiance entre l’application qui émet un message et l’application qui consomme ce message. Ceci permet de fournir des services à valeur ajoutée comme la gestion des transactions, la qualité de service et la sécurité.
L’article complet sur le site de Core Developers Network vaut le coup d’être lu car si vous ne connaissez pas JMS, vous en aurez ensuite un bon aperçu.
Je connaissais déjà le système des Queues et des Topics et je me demandais quel serait l’interêt des transactions avec JMS. Les Topics et les Queues permettent d’envoyer des messages en étant certain que le client en face l’a lu complétement. C’est le système d’acquitement. Donc pourquoi des transactions ? Voici quelques élements de réponses: les transactions JMS permettent à un client d’envoyer plusieurs messages JMS à différent destinataire et d’annuler (rollback) la transaction si l’un des destinataires provoque une exception. Prenons le cas suivant: une application doit envoyer un bon de commande à un entrepôt et doit envoyer un avis de facturation à la comptabilité. Cependant si le bon de commande n’arrive pas à l’entrepôt, nous ne voulons pas que la compta soit activée. Et inversement ! C’est là que JMS propose une solution simple pour résoudre facilement ce genre de situation: lorsque de la création de la session, il suffit de spécifier que celle-ci requiert un contexte transactionnel.
// pour une connexion point à point:
ses = connection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
// pour une connexion de type Topc (publish-subscribe)
ses = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
Le fait de forcer un contexte transactionel va évidemment ralentir le traitement du message. Mais à mon avis le coup sur les performances est minime par rapport au service que JMS offre ici. Evidemment nous pouvons recoder tout ce système mais sommes-nous certain de pouvoir faire mieux ? combien de temps pour le faire ? pour le maintenir ?
Pour revenir aux transactions et JMS il faut juste noter quelques détails (traduction en français de l’article de CoreDevelopers):
- les messages envoyés à une destination ne sont pas délivrés à celle-ci tant que la session côté client n’est pas commité. Si la session est annulé, aucun message n’est envoyé aux clients.
- les messages reçus sur une destination ne seront pas acquités tant que la session n’aura pas été commitée.
- N’importe quel travail en cours sera annulé lorsque la session sera fermée. Si votre application plante, n’importe quelle transaction qui n’a pas été commitée est alors annulé.
Si nous reprenons notre problème d’entrepôt et de service comptabilité, voici comment Je reprends un exemple de code qui est dans l’article de Core Developers Network pour expliquer le principe des transactions:
(code adapté de l'article sur Core Developer Networks)
InitialContext ctx;
QueueConnectionFactory cf;
QueueConnection connection;
QueueSession session;
Queue destination;
QueueSender sender;
TextMessage message1,message2;
ctx = new InitialContext();
cf = (QueueConnectionFactory)ctx.lookup("ConnectionFactory");
destination = (Queue)ctx.lookup("queue/testQueue");
connection = cf.createQueueConnection();
// Creation d'une session avec transaction
session = connection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
sender = session.createSender(destination);
message1 = session.createTextMessage();
message1.setText("Test customer order");
message2 = session.createTextMessage();
message2.setText("End of customer order");
System.out.println("Sending Message.");
try{
sender.send(message1);
sender.send(message2);
session.commit();
}catch (Throwable e){
session.rollback();
}
System.out.println("Done.");
connection.close();
Simple, efficace et souple. Un autre exemple sur le site de SUN permet aussi de voir l’intérêt des transactions. Voici le schéma de principe de l’application:
Bref voilà un système qui permet de se passer de moteur de règles ou tout du moins d’en limiter la complexité. A noter que JBoss avec JBossMQ offre un serveur JMS gratuit et open-source. Pour en savoir plus c’est par là.
0 no like