Blue Flower

Chercher

 

Simple Applications JMS

Cet exemple montre les différentes étapes à suivre pour créer une application cliente JMS:

  1. la création d'un objet JMSContext
  2. la création de Producteur et de consommateur de message
  3. Envoi / Réception de messages
  4. Envoi de Messages

Dans notre exemple on va utiliser Glassfish comme serveur d'application et JMS (Provider JMS) comme fournisseur; pour comprendre le fonctionnement de Glassfish il faut se reporter à un autre cours. Dans le Glassfish on crée les ressources nécessaires pour l' application JMS à savoir les Objets adminisntrés (Connection Factories et Destination).

Envoi de Messages

Ici on explique comment on procède pour permettre à un client d'envoyer un message. L'application cliente Producer.java va envoyer des messages.
Les différentes étapes à suivre sont les suivantes:

  1. Injecter des ressources pour les objets administrés de l'exemple.

  2. Accepter et vérifier les arguments saisis en ligne de commande; on peut utiliser cet exemple pour envoyer autant de messages que ce soit en utilisant les files d'attente de messges que les Topic (sujets); on doit spécifier tout ce qu'il faut en ligne de commande quand on exécute le programme.

  3. Créer l'objet JMSContext, puis envoyer le nombre de message texte sous forme de caractères.

  4. Envoyer un message final pour s'assurer que l'application cliente réceptrice de messages ne peut plus recevoir de messages.

  5. Capturer et traiter toutes les exceptions.

Le client producteur de messages Producer.java

package aomara.producer;

import javax.annotation.Resource;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSContext;
import javax.jms.JMSRuntimeException;
import javax.jms.Queue;
import javax.jms.Topic;

/**
 * The Producer class consists only of a main method, which sends several
 * messages to a queue or topic.
 *
 * Run this program in conjunction with SynchConsumer or AsynchConsumer. Specify
 * "queue" or "topic" on the command line when you run the program. By default,
 * the program sends one message. Specify a number after the destination name to
 * send that number of messages.
 */
public class Producer {

    @Resource(lookup = "java:comp/DefaultJMSConnectionFactory")
    private static ConnectionFactory connectionFactory;
    @Resource(lookup = "jms/MyQueue")
    private static Queue queue;
    @Resource(lookup = "jms/MyTopic")
    private static Topic topic;

    /**
     * Main method.
     *
     * @param args the destination used by the example and, optionally, the
     * number of messages to send
     */
    public static void main(String[] args) {
        final int NUM_MSGS;

        if ((args.length < 1) || (args.length > 2)) {
            System.err.println(
                    "Program takes one or two arguments: "
                    + " []");
            System.exit(1);
        }

        String destType = args[0];
        System.out.println("Destination type is " + destType);

        if (!(destType.equals("queue") || destType.equals("topic"))) {
            System.err.println("Argument must be \"queue\" or " + "\"topic\"");
            System.exit(1);
        }

        if (args.length == 2) {
            NUM_MSGS = (new Integer(args[1])).intValue();
        } else {
            NUM_MSGS = 1;
        }

        Destination dest = null;

        try {
            if (destType.equals("queue")) {
                dest = (Destination) queue;
            } else {
                dest = (Destination) topic;
            }
        } catch (JMSRuntimeException e) {
            System.err.println("Error setting destination: " + e.toString());
            System.exit(1);
        }

        /*
         * Within a try-with-resources block, create context.
         * Create producer and message.
         * Send messages, varying text slightly.
         * Send end-of-messages message.
         */
        try (JMSContext context = connectionFactory.createContext();) {
            int count = 0;

            for (int i = 0; i < NUM_MSGS; i++) {
                String message = "This is message " + (i + 1) 
                        + " from producer";
                // Comment out the following line to send many messages
                System.out.println("Sending message: " + message);
                context.createProducer().send(dest, message);
                count += 1;
            }
            System.out.println("Text messages sent: " + count);
            
            /*
             * Send a non-text control message indicating end of
             * messages.
             */
            context.createProducer().send(dest, context.createMessage());
            // Uncomment the following line if you are sending many messages
            // to two synchronous consumers
            // context.createProducer().send(dest, context.createMessage());
        } catch (JMSRuntimeException e) {
            System.err.println("Exception occurred: " + e.toString());
            System.exit(1);
        }
        System.exit(0);
    }
}

L'application cliente productrice de message Producer.java passe par les étapes suivantes:

  1. On injecte les ressources pour la "fabrique de connexion" Connection Factories et la file d'attente de messages (queue) et le Topic (sujet):

    @Resource(lookup = "java:comp/DefaultJMSConnectionFactory")
    private static ConnectionFactory connectionFactory;
    @Resource(lookup = "jms/MyQueue")
    private static Queue queue;
    @Resource(lookup = "jms/MyTopic")
    private static Topic topic;
    
  2. On récupère et on vérifie les arguments, saisis sur la ligne de commande, qui spécifient le type de destination et le nombre d'arguments:

    final int NUM_MSGS;
    String destType = args[0];
    System.out.println("Destination type is " + destType);
    if ( ! ( destType.equals("queue") || destType.equals("topic") ) ) { 
        System.err.println("Argument must be \"queue\" or " + "\"topic\"");
        System.exit(1);
    }
    if (args.length == 2){ 
        NUM_MSGS = (new Integer(args[1])).intValue();
    } else { 
        NUM_MSGS = 1;
    }
    
  3. Assigner à la fois la file d'attente de message (queue) et le Topic (sujet) un objet Destination adéquat.

    Destination dest = null;
    try { 
        if (destType.equals("queue")) { 
            dest = (Destination) queue; 
        } else { 
            dest = (Destination) topic; 
        }
    } catch (Exception e) {
        System.err.println("Error setting destination: " + e.toString()); 
        System.exit(1);
    }
    
  4. Avec un try-with-resources block créer un objet JMSContext:

    try (JMSContext context = connectionFactory.createContext();) {
    
  5. Mettre à zéro le nombre de messages puis créer un objet JMSProducer et envoyer un ou plusieurs messages à la Destination indiquée et incrémenter le compteur.

    int count = 0;
        for (int i = 0; i < NUM_MSGS; i++) { 
            String message = "This is message " + (i + 1) 
                    + " from producer";
            // Comment out the following line to send many messages
            System.out.println("Sending message: " + message); 
            context.createProducer().send(dest, message);
            count += 1;
        }
        System.out.println("Text messages sent: " + count);
    
  6. Envoie un un message vide pour le contrôle de la fin des envois des données:

    context.createProducer().send(dest, context.createMessage());
    

    Envoie d' un message vide pour le contrôle (le messsage n'a pas de type spécifique) est un moyen de signifier au consommateur que l'envoi des messages est fini.

  7. Capturer et traiter les différentes exceptions . la fin de "try-with-resources" block permet la clôture automatique de l'objet JMSContext:

    } catch (Exception e) {
        System.err.println("Exception occurred: " + e.toString());
        System.exit(1);
    }
    System.exit(0);
    

Exécuter l'application Producer Client

On peut exécuter l'application cliente en utilisant la commande appclient; l'application Cliente nécessite une ou deux arguments en ligne de commande à savoir le type de Destination et le nombre de messages (qui est optionnel - si ce nombre n'est pas spécifier on a un seul message envoyé -); on se sert de l'application pour envoyer 3 messages dans la file d'attente de message.

  1. S'assurer que le serveur est lancé et que toutes les ressources relatives au fonctionnement du programme sont en place:

  2. Dans un terminal allez dans le répertoire où se trouve le programme producer (on tape la commande cd producer).

  3. Exécuter le programme en envoyant 3 message dans la file d'attente de messages (queue):

    appclient -client target/producer.jar queue 3
    

    La sortie du programme donne:

    type de Destination est queue 
    Sending message: This is message 1 from producer
    Sending message: This is message 2 from producer
    Sending message: This is message 3 from producer
    Text messages sent: 3
    

précédent

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

Client to Session Bean to Message-Driven Bean
Client to Session Bean to Message-Driven Bean

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();
       }
    }
}

......

précédent suivant

Utilser un EJB Entity avec deux MDBs pour regrouper des messages

L'application utilise les composants suivants:

  • Une application cliente qui à la fois envoie et reçoit des messages
  • Deux MDB (Message-Driven Beans)
  • Une classe entity (EJB entity)

Présentation de l'application exemple clientmdbentity
Cette application va simuler le processus d'embauche dans une entreprise; elle  montre une possibilité d'utilisation des EJB Entity avec les MDB. On est dans le cas où un système de messagerie client  doit attendre l'arrivée  de plusieurs messages avant de les traiter; il assemble tous ces messages et les traite en une seule fois. Cette technique appelée "pattern joining messages"  n'est pas spécifique à JMS; on s'en sert aussi pour des opérations transactionnelles. Ici on a un ensemble de messages qui seront traités simultanément par un MDB.
un MDB traite un seul message à la fois lors d'une transaction; ici l'application se sert de MDB pour envoyer des messages qui sont stockés temporairement en se servant de EJB Entity; le bean Entity  détermina ensuite si toutes les informations requises sont arrivées, les réunit sous forme d'un message unique, le réexpédie à un autre MDB  qui se chargera d'envoyer ce message stocké via l'EJB Entity de  la Destination vers HR Client. Une fois que le message est traité l'EJB Entity est détruit par l'application. L'image ci-dessous illustre le comportement de l'application.

 Client to Message-Driven Beans to Entity
Client de Messag-Driven Bean à EJB Entity
  1. Un composant de l’application génère un ID pour chaque employé embauché et publie dans un Topic un message M1 contenant cet ID, le nom de l'employé et sa position dans la société; ce Message est consommé par deux MDBs, OfficeMDB et EquipmentMDB; entre temps l'application crée une file d'attente de messages temporaire (ReplyQueue) qui va attendre l'arrivée de la réponse de (M1).

  2. Chaque message est traité par deux MDBs, le MDB OfficeMDB et le MDB EquipmentMDB. Le MDB  OfficeMDB assigne à l'employé un bureau et le MDB EquipmentMDB lui assigne tout l'équipement nécessaire. Le premier MDB pour rendre persistante son information va créer un EJB Entity appelé SetupOffice, et le deuxième MDB va faire appel à cet Entity nouvellement créé pour y ajouter l'information qu' il traite.

  3. Quand les deux MDBs ont fini de traiter le message (M1), le "business method" de l'EJB Entity SetupOffice retourne une valeur "true"  au MDB qui l'a appelé (EquipmentMDB); ce MDB  EquipmentMDB envoie un message (M2) à la file de message Temporaire et ensuite détruit l'instance de l'EJB Entity.


Les composants de l'application  clientmdbentity

Les composants de l'application cliente clientmdbentity sont les message-driven beans et la classe EJB Entity .

L' application Cliente HumanResourceClient.java
L'application cliente  HumanResourceClient.java exécute les étapes suivantes:

  1. Elle définit un Topic pour l'application en utilisant le namespace java:app car ce Topic est utilisé par l'application Client et le module EJB
  2. L'application client injecte les ressources  ConnectionFactory et Topicresources
  3. Elle crée une file d'attente de message temporaire  TemporaryQueue pour recevoir la notification des actions en cours
  4. Elle Crée une instance de  JMSConsumer pour  TemporaryQueue, modifie le "message listener" de JMSConsumer et démarre la connexion
  5. Elle crée un MapMessage
  6. Elle crée 5 nouveaux noms d'employés (avec chacun sa position et son ID) de façon aléatoire; elle publie cela sous forme de 5 messages

Le "message Lstener" HRListener est en attente (à l'écoute) des messages qui précise pour chaque employé son bureau et ses affaires; quand un message arrive  le "message Lstener" affiche l'information reçue et détermine si tous 5 les messages sont arrivés.. Si c'est le cas le "message Listener" à la méthode main  que les messages son bien  là.

Codage des Message-Driven Beans de clientmdbentity
Cet exemple utilise deux message-driven Beans:

  • EquipmentMDB.java
  • OfficeMDB.java

Les beans passent par les étapes suivantes:

  1. Ils injectent une ressource  MessageDrivenContext, un EntityManager, et un  JMSContext.
  2. La méthode onMessage récupère le message . La méthode onMessage de  EquipmentMDB choisit l'équipement qu'il faut en se référant  de la position du nouveau employé, la méthode onMessage de OfficeMDB génère aléatoirement le numéro de bureau
  3. Après un court instant , la méthode onMessage appelle la méthode compose
  4. La méthode compose passe par les étapes suivantes:
    1. Ellle crée et rend persistants l'information via l'Entity SetupOffice  ou la trouve en se servant de la clé primaire
    2. Elle utilise l' Entity pour sauvegarder dans la base de données l'information concernant l' équipement le bureau, puis appelle les deux "business methods"  doEquipmentList et  doOfficeNumber.
    3. Si le "business method" retourne "true" cela veut dir que l'information est sauvegardée, et q'une réponse fut retournée.
    4. Elle détruit ensuite l'instance de l'Entity SetupOffice.

Codage de la classe EJB Entity  de clientmdbentity
La classe EJB entity est déclarée comme suit:

@Entity
public class SetupOffice implements Serializable {

La classe contient un constructeur sans argument et un constructeur qui a deux arguments, l'ID de l'employé, le nom de l'employé, le numéro du bureau et la liste de ses équipements de bureau. La méthode getter de l' ID possède l'annotation @Id pour indiquer qu'on est en présence de la clé primaire.

@Id
public String getEmployeeId() {
    return id;
}

La classe implémente aussi deux "business method"  doEquipmentList  et doOfficeNumber et une méthode utilitaire checkIfSetupComplete.

Le message-Driven Bean appelle les "business methods" et méthodes getter

Le fichier persistence.xml de l'EJB Entity :

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
               http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="clientmdbentity-ejbPU" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>java:comp/DefaultDataSource</jta-data-source>
    <properties>
      <property name="eclipselink.ddl-generation" 
                value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

précédent suivant

Recevoir des Messages de façon synchrone

Ici on décrit comment une application cliente JMS reçoit des messages de façon synchrone; ce client utilise la méthode receive pour recevoir de façon synchrone les messages; le fournisseur JMS utilisé est le serveur d'application Glassfish.

Le Client SynchConsumer.java

/**
 */
package aomara.synchconsumer;

import javax.annotation.Resource;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSRuntimeException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.TextMessage;
import javax.jms.Topic;

/**
 * The SynchConsumer class consists only of a main method, which fetches one or
 * more messages from a queue or topic using synchronous message delivery. Run
 * this program in conjunction with Producer. Specify "queue" or "topic" on the
 * command line when you run the program.
 */
public class SynchConsumer {

    @Resource(lookup = "java:comp/DefaultJMSConnectionFactory")
    private static ConnectionFactory connectionFactory;
    @Resource(lookup = "jms/MyQueue")
    private static Queue queue;
    @Resource(lookup = "jms/MyTopic")
    private static Topic topic;

    /**
     * Main method.
     *
     * @param args the destination name and type used by the example
     */
    public static void main(String[] args) {
        String destType;
        Destination dest = null;
        JMSConsumer consumer;

        if (args.length != 1) {
            System.err.println("Program takes one argument: ");
            System.exit(1);
        }

        destType = args[0];
        System.out.println("Destination type is " + destType);

        if (!(destType.equals("queue") || destType.equals("topic"))) {
            System.err.println("Argument must be \"queue\" or \"topic\"");
            System.exit(1);
        }

        try {
            if (destType.equals("queue")) {
                dest = (Destination) queue;
            } else {
                dest = (Destination) topic;
            }
        } catch (JMSRuntimeException e) {
            System.err.println("Error setting destination: " + e.toString());
            System.exit(1);
        }

        /*
         * In a try-with-resources block, create context.
         * Create consumer.
         * Receive all text messages from destination until
         * a non-text message is received indicating end of
         * message stream.
         */
        try (JMSContext context = connectionFactory.createContext();) {
            consumer = context.createConsumer(dest);
            int count = 0;
            
            while (true) {
                Message m = consumer.receive(1000);

                if (m != null) {
                    if (m instanceof TextMessage) {
                        // Comment out the following two lines to receive
                        // a large volume of messages
                        System.out.println(
                                "Reading message: " + m.getBody(String.class));
                        count += 1;
                    } else {
                        break;
                    }
                }
            }
            System.out.println("Messages received: " + count);
        } catch (JMSException e) {
            System.err.println("Exception occurred: " + e.toString());
            System.exit(1);
        }
        System.exit(0);
    }
}


Le client SynchConsumer.java exécute les étapes suivantes:

Injection des ressources pour la fabrique de connexion (Connection factory), la file d'attente de message (queue) et le sujet (Topic); pour cela donc il:

  • Assigne la file d'attente de message (queue) et le sujet (Topic) à l'objet Destination spécifié,

  • Créé un objet JMSContext avec un "try-with-resources block",

  • Crée un objet JMSConsumer, qui initie l'envoi des messages comme suit,

    consumer = context.createConsumer(dest);
    
  • Il reçoit les messages provenant de la Destination jusqu'à recevoir le "end-of-message-stream control" qui précise la fin du message:

    int count = 0;
    while (true) {
        Message m = consumer.receive(1000); 
        if (m != null) { 
            if (m instanceof TextMessage) { 
                System.out.println(
                        "Reading message: " + m.getBody(String.class));
                count += 1; 
            } else { 
                break; 
            } 
        }
    }
    System.out.println("Messages received: " + count);
    

    Comme le contrôle des messages n'est pas une boucle TextMessagewhile, il s'arrête et cesse de recevoir des messages une fois que le contrôle de fin de réception est arrivé.

  • Capture et traite toutes les exceptions; la fin automatique du block "try-with-resources" provoque la fermeture de l'objet JMSContext.

L'application cliente SynchConsumer utilise une boucle infinie "While" pour la réception des messages et fait appel à la réception des messages avec un argument de Timout.

Exécuter l'application cliente SynchConsumer et l'application cliente Producer

On peut exécuter l'application cliente en exécutant la commande appclient. L'application cliente SynchConsumer nécessite un seul argument à savoir le type de Destination.

Les étapes suivantes montrent comment recevoir/envoyer des messages de façon synchrone en utilisant une file d'attente de message (queue) ou un Topic. Ses étapes considèrent que l'application cliente Producer est déjà lancée et qu'elle a trois messages en attente dans la file d'attente de messages (queue).

  1. Dans le même terminal que celui où on exécute Producer, aller dans le répertoire de l'application synchconsumer:

    cd ../synchconsumer
    
  2. Exécuter le client SynchConsumer en spécifiant la file de message (queue):

    appclient -client target/synchconsumer.jar queue
    

    La sortie après exécution de l'application Cliente est:

    Destination type is queue
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer
    Messages received: 3
    
  3. Après essayer l’exécution de des applications clientes dans l'ordre opposé . Exécuter le client SynchConsumer:

    appclient -client target/synchconsumer.jar queue
    

    L'application affiche te type de Destination et attend les messages.

  4. Ouvrir un autre terminal et exécuter l'application cliente Producer (on se positionne dans son répertoire pour l'exécuter):

    appclient -client target/producer.jar queue 3
    

    Quand les messages sont envoyés, l'application cliente SynchConsumer les reçoit et sort (se termine).

  5. Maintenant exécuter l'application cliente Producer en mode "Topic" plutôt que la file d'attente de message (queue):

    appclient -client target/producer.jar topic 3
    

    La sortie du programme donne:

    Destination type is topic
    Sending message: This is message 1 from producer
    Sending message: This is message 2 from producer
    Sending message: This is message 3 from producer
    Text messages sent: 3
    
  6. Maintenant, dans un terminal, exécuter l'application cliente SynchConsumer en mode "Topic":

    appclient -client target/synchconsumer.jar topic
    

    Le résultat est quand même différent; en effet on a utilisé le système de communication "Topi" au lieu de la file d'attente de message.

  7. Quitter l'exécution de l'application cliente SynchConsumer et exécuter l'application cliente Producer encore:

    appclient -client target/producer.jar topic 3
    

    Maintenant le client SynchConsumer reçoit les messages:

    Destination type is topic
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Reading message: This is message 3 from producer
    Messages received: 3
    

    En effet ces messages ont été envoyés après que le consommateur ait démarré, le client les reçoit.

précédent

Naviguer entre les messages d'une file d'attente de messages

Dans cet exemple on étudie comment naviguer dans une file d'attente de messages (queue) et traiter le message qu'on a besoin; pour cela on se sert d'une instance de l'interface QueueBrowser pour pouvoir examiner un message dans une Queue. Dans Cet exemple on décrit un exemple qui crée un objet QueueBrowser pour examiner un message au sein d'une file d'attente de message.

Le client MessageBrowser.java

Pour créer un objet QueueBrowser, on appelle la méthode JMSContext.createBrowser avec comme paramètre un variable de type Queue; on obtient les messages présents dans la Queue sous forme d'un objet de type Enumeration. On peut afficher les messages en parcourant l'objet de type Enumeration. Pour arriver au résultat final l'application client MessageBrowser.java passe par les étapes suivantes:

  1. Injecter les ressources
  2. Dans le bloc "try-with-resources", créer un objet JMSContext.
  3. Créer un objet QueueBrowser:
    QueueBrowser browser = context.createBrowser(queue);
    
  4. Extraire l'Enumeration qui contient les messages:
    Enumeration msgs = browser.getEnumeration();
    
  5. Vérifier l'Enumeration des messages puis afficher le contenu des messages:
    if ( !msgs.hasMoreElements() ) { 
        System.out.println("No messages in queue");
    } else { 
        while (msgs.hasMoreElements()) { 
            Message tempMsg = (Message)msgs.nextElement(); 
            System.out.println("Message: " + tempMsg); 
        }
    }
    
  6. Capturer et exécuter les exceptions.

Dans le Server d'application Glassfish la sortie d'un message est de la forme suivante:

Text:   This is message 3 from producer
Class:                  com.sun.messaging.jmq.jmsclient.TextMessageImpl
getJMSMessageID():      ID:8-10.152.23.26(bf:27:4:e:e7:ec)-55645-1363100335526
getJMSTimestamp():      1129061034355
getJMSCorrelationID():  null
JMSReplyTo:             null
JMSDestination:         PhysicalQueue
getJMSDeliveryMode():   PERSISTENT
getJMSRedelivered():    false
getJMSType():           null
getJMSExpiration():     0
getJMSPriority():       4
Properties:             {JMSXDeliveryCount=0}

Le code de l'aplication cliente MessageBrowser.java

/** 
* Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. 
* 
* You may not modify, use, reproduce, or distribute this software except in 
* compliance with  the terms of the License at: 
* http://java.net/projects/javaeetutorial/pages/BerkeleyLicense 
*/
package javaeetutorial.messagebrowser;
import java.util.Enumeration;
import javax.annotation.Resource;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueBrowser;

/**
 * The MessageBrowser class inspects a queue and displays the messages it holds.
 */
public class MessageBrowser {
   
 @Resource(lookup = "java:comp/DefaultJMSConnectionFactory")    
private static ConnectionFactory connectionFactory;    
@Resource(lookup = "jms/MyQueue")    
private static Queue queue;
  
 /**   
* Main method.    
*    
* @param args not used     
*/   
public static void main(String[] args) {       
QueueBrowser browser;    
 /*         
* In a try-with-resources block, create context.        
* Create QueueBrowser.         
* Check for messages on queue.        
*/      
try (JMSContext context = connectionFactory.createContext();) {          
browser = context.createBrowser(queue);          
Enumeration msgs = browser.getEnumeration();         
if (!msgs.hasMoreElements()) {               
System.out.println("No messages in queue");         
} else {             
while (msgs.hasMoreElements()) {                   
Message tempMsg = (Message) msgs.nextElement();                   
System.out.println("\nMessage: " + tempMsg);
                }
            }
        } catch (JMSException e) {         
System.err.println("Exception occurred: " + e.toString());          
System.exit(1);     
}    
System.exit(0);   
}
}

Exécuter l'application cliente QueueBrowser

Pour exécuter l'exemple MessageBrowser utiliser la commande appclient et suivez les différentes étapes précisées ci-dessous; on a besoin aussi de l'exemple Producer pour envoyer des messages dans la file de message et un des clients pour les consommer

Pour exécuter les clients on a besoin de deux terminales (fenêtres d'exécution).

  1. Dans un terminal aller dans le répertoire où se trouve Producer:

  2. Exécuter le client Producer, envoyer un message dans la file d'attente de message avec le caractère de contrôle:

    appclient -client target/producer.jar queue
    

    The output of the client looks like this (along with some additional output):

    Destination type is queue
    Sending message: This is message 1 from producer
    Text messages sent: 1
    
  3. Dans un autre terminal aller dans repertoire ou strouve messagebrowser :

  4. Executer l'appli Client MessageBrowser comme suit:

    appclient -client target/messagebrowser.jar
    

    A la sortie de l'appli client on a:

    Message: 
    Text:   This is message 1 from producer
    Class:                  com.sun.messaging.jmq.jmsclient.TextMessageImpl
    getJMSMessageID():      ID:9-10.152.23.26(bf:27:4:e:e7:ec)-55645-1363100335526
    getJMSTimestamp():      1363100335526
    getJMSCorrelationID():  null
    JMSReplyTo:             null
    JMSDestination:         PhysicalQueue
    getJMSDeliveryMode():   PERSISTENT
    getJMSRedelivered():    false
    getJMSType():           null
    getJMSExpiration():     0
    getJMSPriority():       4
    Properties:             {JMSXDeliveryCount=0}
    Message: 
    Class:                  com.sun.messaging.jmq.jmsclient.MessageImpl
    getJMSMessageID():      ID:10-10.152.23.26(bf:27:4:e:e7:ec)-55645-1363100335526
    getJMSTimestamp():      1363100335526
    getJMSCorrelationID():  null
    JMSReplyTo:             null
    JMSDestination:         PhysicalQueue
    getJMSDeliveryMode():   PERSISTENT
    getJMSRedelivered():    false
    getJMSType():           null
    getJMSExpiration():     0
    getJMSPriority():       4
    Properties:             {JMSXDeliveryCount=0}
    

    Le premier message est TextMessage et le second est le "non-text" qui permet de contrôler la fin des messages.

  5. Aller dans le répertoire où se trouve synchconsumer.

  6. Exécuter le client SynchConsumer :

    appclient -client target/synchconsumer.jar queue
    

    La sortie affichée côté client ressemble à ce qui ci-dessous:

    Destination type is queue
    Reading message: This is message 1 from producer
    Messages received: 1
    

précédent