Envoi de message d'un Bean Session à un MDB
Ici on va illustrer l'utilisation d'une Session bean stateless par une application s'exécutant dans un serveur Java EE pour envoyer des messages; cette application, qu'on va appeler clientsessionmb, va envoyer un certain nombre de messages dans un Topic; cette application clientsessionmb a les composants suivants:
- L'application MyAppClient.java qui va invoquer l'EJB Session stateless
- L'EJB Session Stateles qui va publier les différents messages dans le Topic
- Le MDB (Message-driven Bean) qui reçoit les messages et qui les traite - le MDB utilise le concept de souscription durable et se sert d'un selecteur de messages pour récupérer les messages -
Codage des composants de l'application clientsessionmb
Ici, avec cette application, on démontre qu'il est facile de se servir d'un EJB session stateless pour envoyer des messages plutôt que le faire directement par l'application client elle même. On verra que envoyer des messages par un EJB session est identique à l'envoi de message par Managed-bean.
Codage du composant MyAppClient.java
Quand on a traité l'exemple "recevoir des messages de façon asynchrone en utilisant MDB", on a mis en œuvre un certain nombre d'opérations de l' API JMS; ici on va voir que ce ne sera plus le cas. En effet, l'application cliente MyAppClient.java va uniquement utiliser l'Injection de dépendance pour se servir du bean session Publisher via la classe "PublisherRemote" comme le montre les deux lignes de code ci-dessous:
@EJB(name="PublisherRemote") private static PublisherRemote publisher;
Pour l'injection de dépendance on se sert de l'annotation @EJB (name="PublisherRemote") et avec la déclaration de la variable "publisher" se servir de la classe qui implémente l'interface PublisherRemote ( voir le code ci-dessous).
MyAppClient.java
/* * */ package aomara.clientsessionmdb.appclient; import java.util.logging.Level; import java.util.logging.Logger; import javaeetutorial.clientsessionmdb.sb.PublisherRemote; import javax.ejb.EJB; /* * * The MyAppClient class is the client program for this application. It calls * the publisher's publishNews method twice. */ public class MyAppClient { static final Logger logger = Logger.getLogger("MyAppClient"); @EJB private static PublisherRemote publisher; public MyAppClient(String[] args) { } public static void main(String[] args) { MyAppClient client = new MyAppClient(args); client.doTest(); System.exit(0); } public void doTest() { try { publisher.publishNews(); publisher.publishNews(); System.out.println("To view the bean output,"); System.out.println( " check <install_dir>/domains/domain1/logs/server.log."); } catch (Exception ex) { logger.log(Level.SEVERE, "Exception:{0}", ex.toString()); } } }
On voit bien que le code de MyAppClient est assez succinct, le travail étant fait par l'EJB session stateless ( voir le code de l'EJB session stateless Publisher.java la classe qui implémente l'interface distant PublisherRemote).
Codage du composant session bean PublisherBean.java
L'EJB session stateless PublisherBean possède une seule méthode et c'est lui qui implémente l'interface distante PublisherRemote; le faite qu'on veuille atteindre l'EJB session stateless avec l'application cliente qui est, par définition distante, impose l'utilisation d'un interface distant (voir le cours sur les EJB). La classe PublisherBean injecte les ressources SessionContext et Topic (le Topic est aussi défini dans le MDB) ainsi que JMSContext. Le code ci-dessous de la classe PublisherBean donne tous les détails:
PublisherBean.java
/* * */ package aomara.clientsessionmdb.sb; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Resource; import javax.ejb.Remote; import javax.ejb.SessionContext; import javax.ejb.Stateless; import javax.inject.Inject; import javax.jms.JMSContext; import javax.jms.JMSException; import javax.jms.TextMessage; import javax.jms.Topic; /** * Bean class for Publisher enterprise bean. Defines publishNews business method * as well as required methods for a stateless session bean. */ @Stateless @Remote({ PublisherRemote.class }) public class PublisherBean implements PublisherRemote { @Resource private SessionContext sc; @Resource(lookup = "java:module/jms/newsTopic") private Topic topic; @Inject private JMSContext context; static final String[] messageTypes = { "Nation/World", "Metro/Region", "Business", "Sports", "Living/Arts", "Opinion" }; static final Logger logger = Logger.getLogger("PublisherBean"); public PublisherBean() { } /** * Chooses a message type by using the random number generator found in * java.util. Called by publishNews(). * * @return the String representing the message type */ private String chooseType() { int whichMsg; Random rgen = new Random(); whichMsg = rgen.nextInt(messageTypes.length); return messageTypes[whichMsg]; } /** * Creates producer and message. Sends messages after setting their NewsType * property and using the property value as the message text. Messages are * received by MessageBean, a message-driven bean that uses a message * selector to retrieve messages whose NewsType property has certain values. */ @Override public void publishNews() { TextMessage message; int numMsgs = messageTypes.length * 3; String messageType; try { message = context.createTextMessage(); for (int i = 0; i < numMsgs; i++) { messageType = chooseType(); message.setStringProperty("NewsType", messageType); message.setText("Item " + i + ": " + messageType); logger.log(Level.INFO, "PUBLISHER: Setting message text to: {0}", message.getText()); context.createProducer().send(topic, message); } } catch (JMSException t) { logger.log(Level.SEVERE, "PublisherBean.publishNews: Exception: {0}", t.toString()); sc.setRollbackOnly(); } } }
Codage du composant session bean MessageBean.java
Cet EJB MDB MessageBean est à tout point identique à celle qu'on a vu dans l'exemple "recevoir des messages de façon asynchrone en utilisant un MDB"; toutefois, l'annotation @MessageDriven est quelque peu différente; en effet, ici comme Destination on a un Topic et en plus on va se servir d'une souscription durable et d 'un Selecteur de message ; il faut donc en tenir compte. On définit donc un "Topic" pour l'application; dans la définition on a à la fois l'EJB Session et le MDB qui sont dans le même module; donc on se sert de " java:module"; le faite que la Destination est définie dans le MDB l'annotation @MessageDriven se sert de l'activation de configuration de propriétés "destinationLookup"; dans le code ci-dessous on a aussi les annotations pour MessageSelector, SubscriptionDurability, clientId et subscriptionName :
/* * */ package aomara.clientsessionmdb.mdb; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Resource; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.ejb.MessageDrivenContext; import javax.jms.JMSDestinationDefinition; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * The MessageBean class is a message-driven bean. It implements the * javax.jms.MessageListener interface. It is defined as public (but not final * or abstract). */ /* At present, must define destination in MDB in order to use destinationLookup; * must use mappedName if destination is defined elsewhere * (GlassFish issue 20715). */ @JMSDestinationDefinition( name = "java:module/jms/newsTopic", interfaceName = "javax.jms.Topic", destinationName = "PhysicalNewsTopic") @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "java:module/jms/newsTopic"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"), @ActivationConfigProperty(propertyName = "messageSelector", propertyValue = "NewsType = 'Sports' OR NewsType = 'Opinion'"), @ActivationConfigProperty(propertyName = "subscriptionDurability", propertyValue = "Durable"), @ActivationConfigProperty(propertyName = "clientId", propertyValue = "MyID"), @ActivationConfigProperty(propertyName = "subscriptionName", propertyValue = "MySub") }) public class MessageBean implements MessageListener { static final Logger logger = Logger.getLogger("MessageBean"); @Resource public MessageDrivenContext mdc; public MessageBean() { } /** * onMessage method, declared as public (but not final or static), with a * return type of void, and with one argument of type javax.jms.Message. * * Casts the incoming Message to a TextMessage and displays the text. * * @param inMessage the incoming message */ @Override public void onMessage(Message inMessage) { try { if (inMessage instanceof TextMessage) { logger.log(Level.INFO, "MESSAGE BEAN: Message received: {0}", inMessage.getBody(String.class)); } else { logger.log(Level.WARNING, "Message of wrong type: {0}", inMessage.getClass().getName()); } } catch (JMSException e) { logger.log(Level.SEVERE, "MessageBean.onMessage: JMSException: {0}", e.toString()); mdc.setRollbackOnly(); } } }
......