A Devoxx, j’ai assisté à la présentation de Nigel Deakin, spec lead de JMS 2.0, sur l’état actuel des travaux de l’expert group JMS.
Les propositions de l’expert group sont toujours en discussion, il nous a indiqué que sa présentation était déjà un peu différente de celle qu’il avait faite à JavaOne et que ce qu’il nous présentait n’était que provisoire.
Sa présentation complète est disponible depuis mardi soir ici : JMS2.0Devoxx2011.pdf
Nigel a commencé par nous rappeler que JMS est une spécification stable et ayant eut beaucoup de succès (JMS 1.02b à 10 ans).
Mais depuis l’éco-système Java EE a évolué, et il était temps de faire une mise à jour.
Buts de cette version
- Simplification de l’API
- Intégration avec CDI
- Clarifier les ambiguités de l’API et les relations avec les autres spécifications Java EE
- PAAS / multi-tenancy
- Standardisation de l’interface entre les fournisseurs JMS et les serveurs d’application
- Standardisation de fonctionnalités proposées par les différentes implémentations JMS disponibles
Je reprends ici une partie de ce qu’il nous présenté.
Simplications de l’API existante
Voici quelques exemples qui nous ont été présentés :
Création des sessions JMS
Toutes les combinaisons de la signature actuelle de createSession n’ont pas forcément de sens. De plus dans un environnement Java EE les valeurs des paramètres de cette méthode sont simplement ignorés (la sémantique transactionnelle de l’EJB étant celle qui prévaut alors) :
- signature actuelle :
connection.createSession(transacted, deliveryMode)
- nouvelle signature pour Java SE :
connection.createSession(sessionMode)
- nouvelle signature pour Java EE :
connection.createSession()
Implémentation de AutoCloseable
Les javax.jms.Connection implémenteront java.lang.AutoCloseable ce qui permettra de les utiliser dans la construction try-with-resources du jdk 7
Méthode raccourcie pour envoyer un message
Lorsqu’il n’est pas nécessaire de manipuler une instance de javax.jms.Message : en particulier pour positionner un entête ou une propriété ; il sera possible d’utiliser une nouvelle signature de la méthode send sur javax.jms.MessageProducer :
producer.send(String body)
au lieu de devoir créer un message JMS avant de l’envoyer via :
producer.send(javax.jms.Message message)
Création d’une nouvelle API JMS
Un des problèmes de l’API JMS est qu’elle demande beaucoup de code qui n’est pas toujours utile.
Ainsi rien que pour envoyer un message, elle nécessite l’obtention de nombreux objets : en particulier une ConnectionFactory,
une Connection, une Session, un MessageProducer…
Depuis quelques semaines, l’Expert Group réfléchit donc à la création d’une nouvelle classe : MessagingContext regroupant les responsabilités de : Connection + Session + MessageProducer
Voici l’exemple d’utilisation que Nigel nous a donné :
@Resource(mappedName="JMS/contextFactory") ContextFactory contextFactory; @Resource(mappedName="JMS/orderQueue") Queue orderQueue; public void sendMessage(String payload) { try (MessagingContext mCtx = contextFactory.createContext();){ TextMessage textMessage = mCtx.createTextMessage(payload); mCtx.send(orderQueue,textMessage); } }
Cette nouvelle API lancerait des exceptions runtime plutôt que l’exception checkée JMSException actuellement utilisée
Intégration avec CDI
L’Expert Group est toujours en pleine discussion sur la façon d’intégrer JMS à CDI (JSR 299 – Contexts and Dependency Injection for Java EE).
J’ai repris ci-dessous les exemples d’injection qui nous ont été présentés. Mais Nigel nous a indiqué qu’ils devaient se rajouter de l’Expert Group de CDI pour valider la faisabilité de cette approche. A suivre donc.
Proposition pour l’injection de la nouvelle API MessageContext :
@Resource(mappedName="JMS/orderQueue") Queue orderQueue; @Inject @MessagingContext(lookup="JMS/contextFactory") MessagingContext mCtx; @Inject TextMessage textMessage; public void sendMessage(String payload) { textMessage.setText(payload); mCtx.send(orderQueue,textMessage); }
Proposition pour l’injection des objets de l’ancienne API :
@Inject @JMSConnection(lookup="JMS/connFactory") @JMSDestination(lookup="JMS/inboundQueue") MessageProducer producer; @Inject TextMessage textMessage; public void sendMessage (String payload){ try { textMessage.setText(payload); producer.send(textMessage); } catch {JMSException e} // do something } }
De nouvelles fonctionnalités
Nigel nous a présenté aussi de nouvelles fonctionnalités qui pourraient être standardisées dans cette version de la spec :
- “Livraison retardée” (Delivery delay) :
Permettant de spécifier la date à laquelle un message sera consommable.
(Weblogic Integration implèmente de cette manière les timers afin de permettre de déclencher des opérations à une date ultérieure.
J’ai déjà parlé de ce type de message dans mon billet sur le gestion des ressources JMS)
- Accusé de réception asynchrone (Asynchronous acknowledgement) :
La méthode send redonnerait alors la main immédiatement au thread l’appelant sans attendre que le fournisseur JMS (JMS provider)
valide la prise en charge du message. Une fois pris en charge, le fournisseur JMS appelerait un objet callback pour notifier l’application.
- la propriété JMSXDeliveryCount devient obligatoire
En cas d’erreur de traitement, un message peut être remis dans une queue (lors du rollback de la transaction du consommateur).
A chaque fois qu’un message est relivré par le fournisseur, celui-ci incrémente la propriété JMSXDeliveryCount.
En se basant sur la valeur de cette propriété, les applications pourront prendre des décisions particulières et en particulier se prémunir
contre des retry infinis. (Toutefois, la plupart des fournisseurs JMS permettent déjà de définir un nombre maximal de retry)
- Hiérarchies de Topic
Pour reprendre l’exemple de la présentation, il est possible de définir des Topic suivant une règle de nommage hiérarchique :
- STOCK.NASDAQ.TECH.ORCL
- STOCK.NASDAQ.TECH.GOOG
- STOCK.NASDAQ.TECH.ADBE
- STOCK.NYSE.TECH.HPQ
Les consommateurs pourront alors souscrire à plusieurs Topic liés en utilisant des wildcards. Par exemple :
-
STOCK..TECH.
-
STOCK.NASDAQ.TECH.*
-
Consommateurs multiples sur une souscription à un Topic
Actuellement hormis dans le cadre des multiples instances d’un MDB (Message Driven Bean) sur un serveur unique, chaque consommateur d’un Topic reçoit une copie de chaque message qui y est publié (un consommateur = une souscription) ce qui limite la scalabilité de chaque souscription.
Avec cette fonctionnalité, il serait possible d’associer une unique souscription à plusieurs consommateurs (plusieurs threads) potentiellement sur plusieurs JVMs : un seul des consommateurs d’une souscription recevrait alors chaque message.
- Réception de messages par lot (Batch delivery)
Pour la réception, cela passerait par une nouvelle interface (en plus de MessageListener ) :
void onMessages(Message[] messages)
Cela permettrait d’améliorer les performances de réception (une seule transaction par lot de messages).
La suite
Reportez-vous à la présentation référencé plus haut, pour l’ensemble des aspects que Nigel nous a présenté.
La finalisation de la spécification est prévue pour le 3ème trimestre 2012. Il reste encore pas mal de possibilités d’évolution. Par exemple, il semble qu’ils réfléchissent à l’introduction d’annotation permettant de cacher complètement les API JMS cf : http://java.net/projects/messageAPI/pages/Annotations
Si vous êtes intéressé, vous pouvez aller sur http://JMS-spec.java.net et vous abonner à la mailing list users@JMS-spec.java.net pour recevoir les discussions de l’expert group. Elles contiennent en particulier des liens vers des pages du wiki qui expliquent les propositions en cours de discussion.