Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Более сложные приложения JMS

В следующих примерах показано, как использовать некоторые из более сложных функций JMS API: долговременные подписки и транзакции.

Здесь рассматриваются следующие темы:

Использование долговременных подписок

Пример durablesubscriptionexample показывает, как работают долговременные подписки, не являющиеся общими. Это демонстрирует, что долговременная подписка продолжает существовать и накапливать сообщения, даже если у неё нет активного потребителя.

Пример состоит из двух модулей: приложения durableconsumer, создающего долговременную подписку и принимающего сообщения, и приложения unsubscriber, позволяющего отказаться от подписки на долговременную подписку после завершения работы приложения durableconsumer.

Для получения информации о долговременных подписках см. Создание долговременных подписок.

Главный клиент, DurableConsumer.java, находится в каталоге tut-install/examples/jms/durablesubscriptionexample/durableconsumer/.

В этом примере используется фабрика соединений jms/DurableConnectionFactory, которая имеет идентификатор клиента.

Клиент DurableConsumer создаёт JMSContext, используя фабрику соединений. Затем он останавливает JMSContext, вызывает createDurableConsumer, чтобы создать долговременную подписку и получателя по теме, указав имя подписки, регистрирует слушатель сообщений и запускает JMSContext ещё раз. Подписка создаётся только в том случае, если она ещё не существует, поэтому пример можно запустить несколько раз:

try (JMSContext context = durableConnectionFactory.createContext();) {
    context.stop();
    consumer = context.createDurableConsumer(topic, "MakeItLast");
    listener = new TextListener();
    consumer.setMessageListener(listener);
    context.start();
    ...

Для отправки сообщений в тему запустите клиент producer.

Пример unsubscriber содержит очень простой клиент Unsubscriber, который создаёт JMSContext с той же фабрикой соединений и затем вызывает метод unsubscribe, определяющий имя подписки:

try (JMSContext context = durableConnectionFactory.createContext();) {
    System.out.println("Unsubscribing from durable subscription");
    context.unsubscribe("MakeItLast");
} ...

Создание ресурсов для примера долговременной подписки

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. В командном окне перейдите к примеру durableconsumer.

    cd tut-install/jms/durablesubscriptionexample/durableconsumer
  3. Создайте ресурсы командой asadmin add-resources:

    asadmin add-resources src/main/setup/glassfish-resources.xml

    Вывод команды сообщает о создании пула соединений коннекторов и ресурса коннектора.

  4. Проверьте создание ресурсов:

    asadmin list-jms-resources

    В дополнение к ресурсам, которые были созданы для простых примеров, команда выводит новую фабрику соединений:

    jms/MyQueue
    jms/MyTopic
    jms/__defaultConnectionFactory
    jms/DurableConnectionFactory
    Command list-jms-resources executed successfully.

Запуск примера долговременной подписки

  1. В окне терминала перейдите в следующий каталог:

    tut-install/examples/jms/durablesubscriptionexample/
  2. Создайте примеры durableconsumer и unsubscriber:

    mvn install
  3. Перейдите в каталог durableconsumer:

    cd durableconsumer
  4. Чтобы запустить клиент, введите следующую команду:

    appclient -client target/durableconsumer.jar

    Клиент создаёт долговременного потребителя и затем ждёт сообщений:

    Creating consumer for topic
    Starting consumer
    To end program, enter Q or q, then <return>
  5. В другом окне терминала запустите клиент Producer, отправив несколько сообщений в тему:

    cd tut-install/examples/jms/simple/producer
    appclient -client target/producer.jar topic 3
  6. После того как клиент DurableConsumer получит сообщения, введите q или Q для выхода из программы. На этом этапе клиент вёл себя как любой другой асинхронный потребитель.

  7. Теперь, пока клиент DurableConsumer не запущен, используйте клиент Producer для отправки дополнительных сообщений:

    appclient -client target/producer.jar topic 2

    Если бы долговременная подписка не существовала, эти сообщения были бы потеряны, потому что ни один потребитель в теме в настоящее время не работает. Однако долговременная подписка всё ещё активна и сохраняет сообщения.

  8. Запустите клиент DurableConsumer ещё раз. Он сразу получает сообщения, которые были отправлены, когда он был неактивен:

    Creating consumer for topic
    Starting consumer
    To end program, enter Q or q, then <return>
    Reading message: This is message 1 from producer
    Reading message: This is message 2 from producer
    Message is not a TextMessage
  9. Введите q или Q, чтобы выйти из программы.

Запуск unsubscriber

После завершения работы клиента DurableConsumer запустите пример unsubscriber, чтобы отписаться от долговременной подписки.

  1. В окне терминала перейдите в следующий каталог:

    tut-install/examples/jms/durablesubscriptionexample/unsubscriber
  2. Чтобы запустить клиент Unsubscriber, введите следующую команду:

    appclient -client target/unsubscriber.jar

    Клиент сообщает, что отписался от долговременной подписки.

Использование локальных транзакций

Пример transactedexample демонстрирует использование локальных транзакций в клиентском приложении JMS. Он также демонстрирует использование шаблона обмена сообщениями запрос/ответ, описанного в Создание вре́менных пунктов назначения, хотя он использует постоянные, а не вре́менные пункты назначения. Пример состоит из трех модулей: genericsupplier, retailerи vendor, которые можно найти в каталоге tut-install/examples/jms/transactedexample/. Исходный код можно найти в каталогах src/main/java/javaeetutorial каждого модуля. Каждый из модулей genericsupplier и retailer содержит по одному классу, genericsupplier/GenericSupplier.java и retailer/Retailer.javaсоответственно. Модуль vendor более сложен и содержит четыре класса: vendor/Vendor.java, vendor/VendorMessageListener.java, vendor/Order.javaи vendor/SampleUtilities.java.

В этом примере показано, как использовать очередь и тему в одной транзакции, а также как передать JMSContext в конструктор слушателя сообщений. Пример представляет собой чрезвычайно упрощённое приложение электронной коммерции, в котором выполняются следующие действия.

  1. Розничный продавец (retailer/src/main/java/javaeetutorial/retailer/Retailer.java) отправляет MapMessage в очередь заказов оптового продавца, заказывая количество компьютеров, и ожидает его ответа:

    outMessage = context.createMapMessage();
    outMessage.setString("Item", "Computer(s)");
    outMessage.setInt("Quantity", quantity);
    outMessage.setJMSReplyTo(retailerConfirmQueue);
    context.createProducer().send(vendorOrderQueue, outMessage);
    System.out.println("Retailer: ordered " + quantity + " computer(s)");
    orderConfirmReceiver = context.createConsumer(retailerConfirmQueue);
  2. Оптовый продавец (vendor/src/main/java/javaeetutorial/retailer/Vendor.java) получает сообщение заказа розничного продавца и отправляет сообщение заказа в тему заказа производителя в одной транзакции. Эта JMS-транзакция использует одну сессию, можно объединить получение из очереди с отправкой в ​​тему. Вот код, который использует ту же сессию для создания потребителя для очереди:

    vendorOrderReceiver = session.createConsumer(vendorOrderQueue);

    Следующий код получает входящее сообщение, отправляет исходящее сообщение и фиксирует JMSContext. Обработка сообщений была удалена, чтобы упростить последовательность:

    inMessage = vendorOrderReceiver.receive();
    // Обрабатываем входящее сообщение и форматируем исходящее
    // сообщение
    ...
    context.createProducer(). send (supplierOrderTopic, orderMessage);
    ...
    context.commit();

    Для простоты есть только два производителя, один производит процессоры, другой — жёсткие диски.

  3. Каждый производитель (genericsupplier/src/main/java/javaeetutorial/retailer/GenericSupplier.java) получает заказ из темы заказов, проверяет наличие на складе и затем отправляет заказанные товары в очередь, указанную в поле JMSReplyTo сообщения заказа. Если товара недостаточно на складе, производитель отправляет имеющееся количество. Синхронное получение из темы и отправка в очередь происходят в одной транзакции JMS:

    receiver = context.createConsumer(SupplierOrderTopic);
    ...
    inMessage = receiver.receive();
    if (inMessage instanceof MapMessage) {
        orderMessage = (MapMessage) inMessage;
    } ...
    // Обработка сообщения
    outMessage = context.createMapMessage();
    // Добавление содержимого в сообщение
    context.createProducer().send(
             (Queue) orderMessage.getJMSReplyTo(),
             outMessage);
    // Отображение содержимого сообщения
    context.commit();
  4. Оптовый продавец получает ответы производителя из своей очереди подтверждения и обновляет состояние заказа. Сообщения обрабатываются асинхронным слушателем сообщений VendorMessageListener. Этот шаг показывает использование транзакций JMS слушателем сообщения:

    MapMessage component = (MapMessage) message;
    ...
    int orderNumber = component.getInt("VendorOrderNumber");
    Order order = Order.getOrder(orderNumber).processSubOrder(component);
    context.commit();
  5. Когда все ожидающие ответы обрабатываются для данного заказа, слушатель сообщений оптового продавца отправляет сообщение, уведомляющее розничного продавца, может ли он выполнить заказ:

    Queue replyQueue = (Queue) order.order.getJMSReplyTo();
    MapMessage retailerConfirmMessage = context.createMapMessage();
    // Форматировать сообщение
    context.createProducer(). send (replyQueue, retailerConfirmMessage);
    context.commit();
  6. Розничный продавец получает сообщение от оптового продавца:

    inMessage = (MapMessage) orderConfirmReceiver.receive();

    Затем розничный продавец размещает второй заказ на вдвое большее количество компьютеров, чем в первом случае, поэтому эти шаги выполняются дважды.

Рисунок 49-1 иллюстрирует эти шаги.

Рис. 49-1. Транзакции: пример клиента JMS

Диаграмма шагов в примере транзакции

Все сообщения имеют тип MapMessage. Синхронное получение используется для всех сообщений, кроме тех случаев, когда оптовый продавец обрабатывает ответы производителей. Эти ответы обрабатываются асинхронно и демонстрируют, как использовать транзакции в слушателе сообщений.

Через случайные интервалы клиент Vendor генерирует исключение, чтобы симулировать проблему с базой данных и вызвать откат.

Все клиенты, кроме Retailer, используют транзакционные контексты.

В этом примере используются три очереди jms/AQueue, jms/BQueue и jms/CQueue и одна тема с именем jms/OTopic.

Создание ресурсов для transactedexample

  1. Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).

  2. В командном окне перейдите к примеру genericsupplier:

    cd tut-install/jms/transactedexample/genericsupplier
  3. Создайте ресурсы командой asadmin add-resources:

    asadmin add-resources src/main/setup/glassfish-resources.xml
  4. Проверьте создание ресурсов:

    asadmin list-jms-resources

    В дополнение к ресурсам, которые вы создали для простых примеров и примера долговременной подписки, команда перечисляет четыре новых пункта назначения:

    jms/MyQueue
    jms/MyTopic
    jms/AQueue
    jms/BQueue
    jms/CQueue
    jms/OTopic
    jms/__defaultConnectionFactory
    jms/DurableConnectionFactory
    Command list-jms-resources executed successfully.

Запуск клиентов для transactedexample

Вам понадобятся четыре окна терминала для запуска клиентов. Убедитесь, что вы запускаете клиентов в правильном порядке.

  1. В окне терминала перейдите в следующий каталог:

    tut-install/examples/jms/transactedexample/
  2. Чтобы собрать и упаковать все модули, введите следующую команду:

    mvn install
  3. Перейдите в каталог genericsupplier:

    cd genericsupplier
  4. Используйте следующую команду для запуска клиента производителя процессоров:

    appclient -client target/genericsupplier.jar CPU

    После некоторого начального вывода клиент сообщает следующее:

    Starting CPU supplier
  5. Во втором окне терминала перейдите в каталог genericsupplier:

    cd tut-install/examples/jms/transactedexample/genericsupplier
  6. Используйте следующую команду для запуска клиента производителя жёстких дисков:

    appclient -client target/genericsupplier.jar HD

    После некоторого начального вывода клиент сообщает следующее:

    Starting Hard Drive supplier
  7. В третьем окне терминала перейдите в каталог vendor:

    cd tut-install/examples/jms/transactedexample/vendor
  8. Используйте следующую команду для запуска клиента Vendor:

    appclient -client target/vendor.jar

    После некоторого начального вывода клиент сообщает следующее:

    Starting vendor
  9. В другом окне терминала перейдите в каталог retailer:

    cd tut-install/examples/jms/transactedexample/retailer
  10. Используйте команду, подобную следующей, чтобы запустить клиент Retailer. Аргумент указывает количество компьютеров для заказа:

    appclient -client target/retailer.jar 4

    После некоторого начального вывода клиент Retailer сообщает что-то вроде следующего. В этом случае первый заказ заполнен, а второй нет:

    Retailer: Quantity to be ordered is 4
    Retailer: Ordered 4 computer(s)
    Retailer: Order filled
    Retailer: Placing another order
    Retailer: Ordered 8 computer(s)
    Retailer: Order not filled

    Клиент Vendor сообщает что-то вроде следующего, заявляя в этом случае, что он может отправлять все компьютеры по первому заказу, но не по второму:

    Vendor: Retailer ordered 4 Computer(s)
    Vendor: Ordered 4 CPU(s) and hard drive(s)
      Vendor: Committed transaction 1
    Vendor: Completed processing for order 1
    Vendor: Sent 4 computer(s)
      Vendor: committed transaction 2
    Vendor: Retailer ordered 8 Computer(s)
    Vendor: Ordered 8 CPU(s) and hard drive(s)
      Vendor: Committed transaction 1
    Vendor: Completed processing for order 2
    Vendor: Unable to send 8 computer(s)
      Vendor: Committed transaction 2

    Производитель процессоров сообщает что-то вроде следующего. В этом случае он может отправить все процессоры для обоих заказов:

    CPU Supplier: Vendor ordered 4 CPU(s)
    CPU Supplier: Sent 4 CPU(s)
      CPU Supplier: Committed transaction
    CPU Supplier: Vendor ordered 8 CPU(s)
    CPU Supplier: Sent 8 CPU(s)
      CPU Supplier: Committed transaction

    Производитель жёстких дисков сообщает что-то вроде следующего. В этом случае ему не хватает жёстких дисков для второго заказа:

    Hard Drive Supplier: Vendor ordered 4 Hard Drive(s)
    Hard Drive Supplier: Sent 4 Hard Drive(s)
      Hard Drive Supplier: Committed transaction
    Hard Drive Supplier: Vendor ordered 8 Hard Drive(s)
    Hard Drive Supplier: Sent 1 Hard Drive(s)
      Hard Drive Supplier: Committed transaction
  11. Повторите шаги с 4 по 10 столько раз, сколько пожелаете. Иногда оптовый продавец сообщает об исключительной ситуации, которая вызывает откат:

    Vendor: JMSException occurred: javax.jms.JMSException: Simulated
    database concurrent access exception
      Vendor: Rolled back transaction 1
  12. После завершения работы с клиентами вы можете удалить ресурсы пунктов назначения с помощью следующих команд:

    asadmin delete-jms-resource jms/AQueue
    asadmin delete-jms-resource jms/BQueue
    asadmin delete-jms-resource jms/CQueue
    asadmin delete-jms-resource jms/OTopic

Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)