EJB: Message Driven-Bean
Présentation
Au sein d'une application d'entreprise, on peut faire communiquer entre eux les différents composants d'applications clientes par envoi de données directement interprétables et utilisables. Les Message-driven beans qui sont des composants java situés côté serveur dans un conteneur d' EJB, traitent les messages de façon asynchrone.
Un MDB, vu par l'API JMS (voir le cours sur l'API JMS 2.0), n'est rien d'autre qu'un consommateur de messages asynchrones appelé automatiquement par le conteneur d'EJB lorsqu'un message arrive. Pour un producteur de messages, un MDB est un consommateur de messages qui attend à une Destination (Queue ou Topic) l'arrivée d'un message.
Dans une application où on veut échanger des messages entre applications clientes, on peut utiliser l'API JMS; on peut utiliser les MDB si on est dans un environnement Java EE (voir utilisation MDB dans un server d'application Java EE).
Un Message-Driven Bean agit comme un Listener de messages au sein du conteneur d'EJB. Les messages peuvent être envoyés par n'importe quels composants ou applications java EE (une application cliente, une autre EJB, ou un composant web) .
Sur plusieurs aspects de son fonctionnement un Message-Driven Bean ressemble à un bean session stateless. La plus grande différence entre un bean session stateless et un Message-Driven Bean est qu'un client, pour une message-driven message, ne se sert pas d'interface (local ou remote); un Message-Driven Bean est uniquement une classe définie sur le serveur et gérée par le conteneur d'EJB.
Un MDB a les caractéristiques suivantes:
- Un Message-Driven Bean s'exécute dès réception d'un seul message.
- Un Message-Driven Bean est exécuté (s'exécute) de façon asynchrone,
- Le cycle de vie d'un MDB est relativement court.
- Un MDB ne peut représenter directement des données dans une base de données. Toutefois il peut y accéder et faire une mise à jour.
- Un MDB peut être un transaction-aware (transaction consciente).
- Un MDB est sans état comme un Bean session stateless.
Cycle de vie de message-driven bean
Cycle de vie d'un MDB |
Au début du cycle de vie d'un MDB (message-driven bean) Le conteneur d'EJB exécute les tâches suivantes:
- Créer un consommateur (un QueueReceiver or TopicSubscriber) qui recevera les messages,
- Associer le MDB à une destination et une ConnectionFactory (connexion factory) pour le déploiement,
- Enregistrer le message listener et le mode de reconnaissance du message.
Le cycle de vie d'un MDB dépend de la durée de vie du serveur d'EJB au sein duquel il est déployé. Comme un MDB est sans état, le server d'EJB crée un pool d'instances de MDB, et à chaque fois qu' on en a besoin le conteneur d'EJB s'en sert dès qu'un message est disponible; ce fonctionnement est à peu près le même pour les EJB 2 et les EJB 3.
Quelques différences existent dans la façon dont on enregistre les cycles de vie des callback; pour cela, il faut voir pour les EJB 3 comment se déroulent les annotations @PostConstruct et @PreDestroy et pour les EJB 2 les méthodes ejbCreate (Le conteneur invoque cette méthode directement avant qu'il ne crée le MDB; Un message-driven bean ne doit rien faire dans cette méthode) et ejbRemove (Un conteneur invoque cette méthode avant qu'il ne finisse le traitement d'un MDB; on se sert de cette méthode pour faire n'importe quel nettoyage nécessaire comme fermer des ressources externes).
Utilisation de message-driven bean
Différence entre Message-Driven Beans et Session Beans
Toutes les instances d'un MDB sont équivalentes et cela permet au conteneur d'EJB d'assigner à un message une quelconque de ces instances. Le conteneur peut unir ces instances et permettre à un ensemble de messages d'être traités simultanément.
Un seul MDB peut traiter plusieurs messages provenant de plusieurs clients.
Les variables d'instance de Message-Driven Bean d'une instance de Message-Driven Bean peuvent contenir des états à travers un message client comme par exemple une connexion de l' "API JMS", une connexion à une base de données, ou une référence à un objet EJB.
Les composants clients (applications clientes) ne ciblent pas directement le MDB ni n'invoquent pas leurs méthodes; par contre ils accèdent aux MDBs, par exemple pour l' API JMS, par la destination où les messages sont envoyés et où le MDB écoutent via la classe MessageListener. On associe une destination à un MDB pendant le déploiement en utilisant les ressources du serveur Glassfish.
Caractéristiques d'un Message-Driven Beans
Un Message-Driven Bean a les caractéristiques suivantes:
- Il s'exécutent dès qu'ils reçoivent un message d'un client.
- Il travaille de façon asynchrone.
- La durée de vie d'un MDB au sein du conteneur d'EJB est relativement courte.
- Ils ne peuvent pas représenter des données ( ils sont sans état) directement à partager dans la base de données, mais ils peuvent y avoir accès et faire une mise à jour de ces données.
- Les MDB peuvent être utilisés dans un mode transactionnel
- Le MDB est sans état .
Quand un message arrive à destination, le conteneur fait appel à la méthode onMessage de Message-Driven Bean pour le traiter. Cette méthode onMessage peut faire appel à des méthodes helper ou des beans session pour traiter l'information contenue dans le message ou le stocker dans une base de données.
Exemple:
(prochainement) ....
Quand utiliser Message-Driven Beans ?
Des beans session permettent d'envoyer des messages via l'API JMS et de les recevoir via l'API JMS de façon synchrone mais pas de façon asynchrone. Pour éviter de bloquer le serveur (lorsque le message est long,..) il faut éviter de traiter les messages de façon synchrone. Pour cela il est conseillé de se servir de Message-driven bean qui traite les messages de façon asynchrone.
Annotation pour Message-driven bean
@Ressource
L'annotation @javax.annotation.Resource permet d'injecter des instances de ressources gérées par le conteneur telles qu'une datasource JDBC ou une destination JMS (queue ou Topic) par exemple.
Cette annotation est utilisable sur une classe, une méthode ou un champ. Si l'annotation est utilisée sur une méthode ou un champ, le conteneur injecte les références au moment de l'initialisation du bean.
Exemple:
(prochainement) ....
@MessageDriven
Cette annotation est utilisée pour préciser que la classe qu'on est en train d'implémenter est un Message-Driven bean. Si le propriété name n'a pas de valeur le nom du MDB est le nom de la classe MDB; ici dans l'exemple ci-dessous ce serait QueueListenerMDB; par contre le fait qu'on ait donné une valeur à "name" en l'occurence "QueueMDB", fait que notre MDB portera ce nom.
Exemple
@MessageDriven( name = "QueueMDB", description = "My First Queue MDB", mappedName = "queue/mdb", messageListenerInterface= MessageListener.class, activationConfig = { @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty( propertyName = "destination", propertyValue = "queue/MyQueue") } ) public class QueueListenerMDB implements MessageListener { public void onMessage(Message message) { // Process the message } }
Détails
name - le nom du MDB. S'il est omis, le nom par défaut est le nom de la classe qui définit le MDB et dans notre cas c'est QueueListenerMDB
description - description du MDB
mappedName - The mappedName attribute specifies the JNDI name of the JMS destination from which the bean will consume the message
messageListenerInterface - MDB must directly (using implements keyword)or indirectly(using annotation) implement a message listener interface. If the MDB class implements more than one interface other than java.io.Serializable, java.io.Externalizable, or any of the interfaces defined by the javax.ejb package, the message listener interface must be specified.
activationConfig cet élément sert à préciser des propriétés de configuration; il renvoie un tableau d'annotations
@ActivationConfigProperty; chaque élément de tableau est une annotation @ActivationConfigProperty sous forme de couple ( propertyName ="NomProperty",propertyValue = "ValueProperty")
@ActivationConfigProperty
Avec MDB on peut configurer certaines propriétés comme les sélecteurs de messages, le mode d’acquittement, les abonnements durables, etc..., en se servant de l'annotation facultative @ActivationConfigProperty comme elle est présentée dans l'exemple ci-dessus (voir Exemple).
Cette annotation @javax.ejb.ActivationConfigProperty permet de préciser le nom et la valeur d'une propriété de la configuration des EJB de type MessageDriven. Elle s'utilise dans la propriété activationConfig d'une annotation de type @javax.ejb.MessageDriven.
@JMSDestinationDefinition
Une application java qui communique soit en se servant de l'API JMS soit par Message-driven bean utilise cette annotation pour préciser les ressources nécessaires pour l'environnement opérationnel de la destination au sens de JMS. Ces ressources sont des informations à utiliser pour le déploiement de l'application dans un environnement JAVA EE avec le minimum de configuration.
interfaceName: demande un nom bien qualifié de l'interface destination JMS. Les valeurs permises sont "javax.jms.Queue" ou "javax.jms.Topic".
name : nécessite que le nom JNDI de la ressource destination soit bien défini .
className: (Optionnel) Nom pleinement qualifié de l'implémentation de la classe de la destination JMS (optionnel).
description: description de cette destination JMS (Optionel).
destinationName: nom de du queue ou du topic (Optionnel).
properties: propriétés optionnelles de la destination JMS. Des propriétés qui sont spécifiées sous le format "property: Name=propertyValue" avec une propriété par chaque élément du tableau.
resourceAdapter: Nom de la ressource adapter (Optionnel)
Exemple
@JMSDestinationDefinition( name = "java:global/jms/demoQueue", description = "Queue to use in demonstration", className = "javax.jms.Queue", destinationName="demoQueue"
@JMSConnectionFactoryDefinition
Exemple
@JMSConnectionFactoryDefinition( name="java:global/jms/demoConnectionFactory", className= "javax.jms.ConnectionFactory", description="ConnectionFactory to use in demonstration")
(à suivre)..