Vous n'êtes pas connecté. Connexion
|
|
Accueil > Intranet Michel Buffa > Cours composants distribués pour l'entreprise / EJB 2012-2013 > TP5 EJB : Utilisation de JMS et de Message Driven Beans
TP5 EJB : Utilisation de JMS et de Message Driven BeansDe $1
Faisons marcher l'exemple du coursRepartez du TP3 ou du TP4. Dans la liste des EJBs, ajoutez un Message Driven Bean. Vous suivrez les étapes ci-dessous : 1 - Ajouter un message driven bean au projet EJB (celui avec le grain de café)2) Configurer le bean. On lui donne un nom puis on indique le nom de la Queue de messages (ici "jms/logginMessages"). On indique aussi le type du gestionnaire de messages (Queue). Le Bean est généré puis ajouté au projet. Le source doit ressembler à cela : package tp2; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.Message; import javax.jms.MessageListener; /** * * @author michel */ @MessageDriven(mappedName = "jms/loggingMessages", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") }) public class MDBduCours implements MessageListener { public MDBduCours() { } public void onMessage(Message message) { } }
/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package tp2; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; /** * * @author michel */ @MessageDriven(mappedName = "jms/loggingMessages", activationConfig = { @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") }) public class MDBduCours implements MessageListener { public MDBduCours() { } public void onMessage(Message message) { if (message instanceof TextMessage) { TextMessage tm = (TextMessage) message; try { String text = tm.getText(); System.out.println("Received new message :" + text); } catch (JMSException e) { } } } } Notez que le nom de la destination est "jms/NewMessage", si vous allez voir dans la console d'administration du serveur d'application après avoir déployé le projet, vous trouverez sous jms/resources sa définition. C'est la même chose que pour les sources de données : on peut déployer de nouveaux Topic ou Queues de messages à la volée. Leur définition est ajoutée (dans le cas de Glassfish) dans le descripteur propriétaire sun-resources.xml (ou glassfish-resources.xml, selon les versions de glassfish ce nom peut changer). Vous pouvez jeter un oeil à ce fichier, vous retrouverez la source de données du TP3 ainsi que la définition de la Queue de messages "jms/loggingMessages". 2 - Ecrire dans le Backing Bean une méthode pour tester le Message Driven Bean
Ouvrez le source d'un JSB Managed Bean, par exemple CompteMBean du TP3/TP4. On va utiliser un wizard pour insérer du code afin que ce bean soit capable d'envoyer un message à la Queue de messages jms/loggingMessages. Rappel : ce bean ne sait pas que le Message Driven Bean existe, elle se contente juste d'envoyer un message indiquant qu'elle souhaite un traitement. Avec netbeans, faites dans le source de customerMBean bouton de droite / insert code / send JMS Message : Cela va faire apparaitre un wizard dans lequel vous allez indiquer le nom de la Queue de message dans laquelle vous allez publier : Choisissez "Message Driven Bean" et le MDB que vous avez créé, à moins que dans Server Destinations, la queue de message soit déjà présente. Ce choix va 1) générer du code d'envoi de message dans le CompteMBean et 2) ajouter des lignes dans le descripteur propriétaire de Glassfish pour qu'il créée cette nouvelle Queue de messages s'il ne l'a pas déjà créée. Voici le code généré. Etudiez-le ! Essayez de comprendre ce qu'il fait. /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package tp2; import java.io.IOException; import java.io.PrintWriter; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Resource; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Named(value = "compteMBean") @SessionScoped public class CompteMBean implements Serializable { @Resource(mappedName = "jms/loggingMessages") private Queue loggingMessages; @Resource(mappedName = "jms/loggingMessagesFactory") private ConnectionFactory loggingMessagesFactory; ....... // code existant dans CompteMBean private Message createJMSMessageForjmsLoggingMessages(Session session, Object messageData) throws JMSException { // TODO create and populate message to send TextMessage tm = session.createTextMessage(); tm.setText(messageData.toString()); return tm; } private void sendJMSMessageToLoggingMessages(Object messageData) throws JMSException { Connection connection = null; Session session = null; try { connection = loggingMessagesFactory.createConnection(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(loggingMessages); messageProducer.send(createJMSMessageForjmsLoggingMessages(session, messageData)); } finally { if (session != null) { try { session.close(); } catch (JMSException e) { Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot close session", e); } } if (connection != null) { connection.close(); } } } } En particulier, notez que les lignes : @Resource(name = "jms/loggingMessages") private Queue loggingMessages; @Resource(name = "jms/loggingMessagesFactory") private ConnectionFactory loggingMessagesFactory; Correspondent aux 4 premières étapes indiquées en cours, que l'on pourrait écrire (en utilisant explicitement JMS) comme ceci : // 1 Récupération de la factory JMS InitialContext ctx = new InitialContext(); opicConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("jms/loggingMessagesFactory"); // 2 Use connection factory to create a JMS connection QueueConnection connection = factory.createQueueConnection(); //3 Use connection to create a session QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); // 4 Lookup destination Queue queue = (Queue) ctx.lookup("jms/loggingMessages"); Evidemment, avec les annotations de code c'est plus simple ! On ne codera pas les lignes ci-dessus, et tant mieux ! Des méthodes utilitaires sont également générées, en particulier la méthode sendJMSMessageToNewMessage qui prend un objet en paramètre (messageData). Regardez ce qu'elle fait... Elle nous mâche le travail. Sauf que par défaut elle suppose que le message est un textMessage. Pour la question suivante du TP, vous utilisez un ObjectMessage, il faudra donc légèrement modifier la ligne de création du message. private Message createJMSMessageForjmsLoggingMessages(Session session, Object messageData) throws JMSException { // TODO create and populate message to send TextMessage tm = session.createTextMessage(); tm.setText(messageData.toString()); return tm; } private void sendJMSMessageToLoggingMessages(Object messageData) throws JMSException { Connection connection = null; Session session = null; try { connection = loggingMessagesFactory.createConnection(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session.createProducer(loggingMessages); messageProducer.send(createJMSMessageForjmsLoggingMessages(session, messageData)); } finally { if (session != null) { try { session.close(); } catch (JMSException e) { Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot close session", e); } } if (connection != null) { connection.close(); } } } On va maintenant envoyer un message de log lors de l'exécution d'une opération bancaire de type "déposer de l'argent", "retirer" ou "transfert". Voici par exemple une méthode possible de CompteMBean qui envoie un message de log au MDB : public void transfert() { // ENVOI DU MESSAGE try { sendJMSMessageToLoggingMessages("Ceci est un message texte envoyé au MDB"); } catch (JMSException ex) { Logger.getLogger(CompteMBean.class.getName()).log(Level.SEVERE, null, ex); } compteBancaireFacade.transfert(id1, id2, montant); refresh(); } Finalement, pour envoyer un message texte on se contente d'une ligne de code ! Compilez, déployez, testezvotre application, effectuez un transfert et vérifiez dans la console du serveur que le message est bien arrivé : Si vous faites "reload" plusieurs fois dans votre navigateur, vous verrez qu'à chaque fois un message arrive ! Modifiez maintenant le code du Message Driven Bean pour simuler un traitement long. Avec l'exemple ci-dessous nous simulons un délai de 10 secondes entre chaque traitement de messages : public void onMessage(Message message) { if (message instanceof TextMessage) { TextMessage tm = (TextMessage) message; try { String text = tm.getText(); System.out.println("Received new message :" + text); System.out.print("J'attend dix secondes que le traitement soit terminé"); Thread.sleep(10000); // 10 fois 1000 millisecondes } catch (InterruptedException ex) { Logger.getLogger(MDBduCours.class.getName()).log(Level.SEVERE, null, ex); } catch (JMSException e) { } } } Relancez le projet, et regardez la console pendant que vous faites "reload" plusieurs fois d'affilée. Le client (le navigateur) n'est pas bloqué et reprend la main instantanément, alors que les messages reçus apparaissent dans la console toutes les 10 secondes ! En revanche dans la console on a les messages dans un ordre bizarre. Quelle est l'explication ? Cf le cours et réflechissez un peu ! La non-attente du client fait tout l'intérêt de JMS. Imaginez une vidéo à encoder, un ordre de paiement qui nécessite de "parler" à la banque, etc... 3 - Une vraie application : on va envoyer des messages de demande de paiement électronique sous forme d'Object MessagesImaginez un site de e-commerce. Des clients remplissent leur panier virtuel et passent une commande. Le site envoie un message JMS à la banque demandant un transfert bancaire du compte en banque du client vers celui du site de e-commerce. On va réaliser cette opération. Cette fois-ci le message envoyé ne sera pas un message de type TextMessage mais de type ObjectMessage. Voir le cours sur les différents types de messages JMS.
Exemple de code dans le MDB pour gérer des objets if (message instance of ObjectMessage) { ... //get the object inside the message .... // Cast it in AccountOrderObjects .... // decode it (look at the value of the "order" property, get other properties... .... // Call the AccountFacade to do the work... } Première étape : créer une classe OrdreBancaire.java Serializable (obligatoire car on va envoyer ces objets dans des messages) qui contiendra plusieurs propriétés : le type d'opération (transfert, retrait, dépot, creation, suppression), l'id du compte source, l'id du compte de destination, le montant à transférer, une description et la date (avec une annotation temporelle sous forme de TIMESTAMP). Travail à faire :
Essayez l'annotation @AsynchronousModifiez une des méthodes existants dans votre projet, par exemple celle qui effectue le transfert dans le CompteBancaireFacade en ajoutant un Thread.sleep(7000) pour qu'elle rpennen 7 secondes à s'exécuter. Testez un transfert (commentez l'envoi du MDB cette fois-ci). Notez que l'action reste bloquante pendant 7 secondes. Maintenant ajoutez l'annotation @Asynchronous devant la méthode transfert() et retestez ! On est passé en multithread ! C'est juste un moyen léger de faire des appels de méthode non bloquants. C'est différent des MDB car peu adapté à des "long voyages", les messages voyagent plus facilement sur de longue distances (de amazon à la caisse d'épargne par ex)...
Essayez les EJBTimers de EJB 3.1Just add to the "sessions" package of your project a new class of type "EJB timer", call it MyTimer. This will pop up a dialog in which you will be able to configure the schedule for your timer. The syntax for specifying when your timer will be called is similar to the one used by Unix's crontabs. A typical schedule: second="*/10", minute="*", hour="8-23", dayOfWeek="Mon-Fri", dayOfMonth="*", month="*", year="*", info="MyTimer" This is the default value proposed by the Eclipse dialog for creating EJB timers, it means:
The class you should get looks like that: package sessions; Notice that an EJB timer is a stateless session bean (@Stateless), that the method that should be called at given period of times is preceeded by a @Schedule annotation that is parameterized by a schedule definition. Deploy this project and look at the console, you should see some messages in the console, and every 10s, a new message should appear...
Mots clés:
|
Powered by MindTouch Deki Open Source Edition v.8.08 |