Сообщений 6    Оценка 0        Оценить  
Система Orphus

Основы использования JMS в IBM WebSphere Community Edition

Автор: Александр Цимбал
Источник: RSDN Magazine #3-2007
Опубликовано: 14.11.2007
Исправлено: 15.04.2009
Версия текста: 1.0
Основные понятия JMS
JMS и Geronimo/WAS CE
Запуск брокера сообщений в WAS CE
Создание администрируемых объектов в WAS CE
Доступ к администрируемым объектам в WAS CE с помощью дескрипторов
Использование объектов JMS в Java-программе
Заключение

Асинхронное взаимодействие в стиле «messaging» занимает важное место в распределенных системах. Пожалуй, основными достоинствами такого способа взаимодействия являются:

На уровне использования языка Java формализацией интерфейсов такого взаимодействия является технология JMS – Java Messaging Service.

Основные понятия JMS

JMS – технология небольшая, и использовать ее легко (по крайней мере, в большинстве случаев), хотя, надо сказать, создание с ее использованием сложных, нетривиальных и переносимых приложений – задача весьма непростая. Тем не менее, для начинающих пользователей JMS имеет смысл объяснить основные термины и принципы использования JMS.

Основная идея использования JMS заключается в том, что разработчики создают только клиентские приложения, часть из которых является отправителями, а часть – получателями сообщений. Конечно, можно и отправлять, и получать сообщения в одном приложении. Сервер (его часто называют message broker) обычно создается крупными компаниями – IBM, Tibco, Sonic, и прикладные разработчики просто используют его – подобно Web-серверам или серверам БД.

Как правило, перед началом работы программной системы, использующей JMS, на стороне сервера создаются так называемые «администрируемые объекты». Это, во-первых, фабрики соединений (connection factories), а во-вторых – «целевые» объекты двух видов – топики (topics) и очереди (queues). Основное отличие топиков от очередей состоит в том, что топики размножают сообщение для всех, кто желает его получить, а очередь является просто каналом передачи сообщения единственному потребителю – первому, кто успел. Соответственно, очереди реализуют программную модель «отправитель-получатель» (sender-receiver), а топики – «издатель-подписчик» (publisher-subscriber).

Получатель сообщений – при использовании как топиков, так и очередей – может извлекать сообщения из нужного целевого объекта в двух режимах – синхронном и асинхронном. В синхронном режиме программа-получатель явно вызывает для специального объекта-получателя, сопоставленного с требуемым целевым объектом, специальный метод (receive()). Этот метод возвращает сообщение, если оно доступно. Если сообщения нет, то вызов этого метода блокирует поток выполнения команд – программа ждет прихода сообщения.

В асинхронном режиме получатель реализует callback-метод onMessage() специального интерфейса MessageListener. Разработчик создает класс, реализующий данный интерфейс, создает экземпляр этого класса и сопоставляет этот экземпляр с нужным целевым объектом. При приходе сообщения происходит вызов и выполнение кода метода onMessage().

Администрируемые объекты обычно создаются администратором брокера сообщений с использованием поставляемых разработчиками сервера специальных утилит администратора. В большинстве случаев такие объекты являются глобальными, т.е. доступными для различных приложений, а доступ к ним производится с использованием службы имен – JNDI.

Важно понимать, что JMS обеспечивает доставку сообщений только целевым объектам, а не «истинным» потребителям. Задача «правильного получения» событий в программе от топика или из очереди может быть весьма нетривиальной.

Важнейшим понятием JMS является сессия (session). Проще всего трактовать сессию как контекст потока, в котором выполняется передача сообщений. Фабрикой сессий является соединение (connection). В свою очередь, сессия является фабрикой для объектов-отправителей сообщений, объектов-получателей сообщений и самих сообщений. Отправители, получатели событий и сами события являются обычными локальными объектами Java. При передаче события (сообщения) выполняется его сериализация – опять-таки по обычным правилам Java.

В более сложном случае – с использованием распределенных транзакций – сессия выступает как получатель сообщений, и одновременно – как их диспетчер.

JMS поддерживает два вида транзакций – локальные и распределенные (XA-транзакции). Тем, кто знаком с JDBC, может помочь следующая аналогия: аналогом локальной транзакции в JMS является транзакция на уровне конкретной сессии (в JDBC локальная транзакция «завязана» на соединение). В распределенных транзакциях JDBC используется пул соединений, причем с соединениями сопоставлен ресурсный объект, обеспечивающий связь соединения с контекстом глобальной транзакции. Управление распределенными транзакциями в JMS выполняется похожим образом, только создается пул не соединений, а сессий, и соответственно, ресурсный объект привязывает к контексту внешней (глобальной транзакции) сессию. При передаче сообщений в контексте каждой сессии для нее создается отдельный поток, в котором и вызываются нужные методы.

Обычно прикладной программист не имеет дела с распределенными XA-транзакциями JMS. Это не значит, что они не используются – просто управление распределенными транзакциями и их взаимодействие с сессиями JMS в J2EE полностью берет на себя сервер приложений. Обычно от разработчика требуется задать (в соответствующем XML-дескрипторе развертывания) нужный тип транзакций – глобальные или локальные.

Сессии бывают транзакционные и нетранзакционные. Это очень похоже на то, как в JDBC используется механизм явного и неявного управления локальными транзакциями.

В конце статьи будет приведен пример кода, демонстрирующий основные этапы создания клиентских приложений (как отправителя, так и получателя) с использованием JMS.

JMS и Geronimo/WAS CE

Надо сказать, что история взаимоотношений IBM и JMS (создаваемой, как обычно, в рамках Java Community Process), довольно непроста. Дело в том, что IBM был одним из инициаторов создания систем асинхронного взаимодействия и довольно давно создал программный продукт для его реализации. Этот продукт чрезвычайно популярен и даже сейчас больше известен под названием MQSeries, хотя он давно интегрирован с WebSphere и даже получил иное название (WebSphere MQ). Проблемой являлось то, что JMS, как любая универсальная стандартизованная технология, не поддерживает, конечно же, всех возможностей различных реализаций, поэтому долгое время IBM не слишком «жаловала» JMS, хотя, разумеется, доступ к основным возможностям MQSeries несложно было получить с использованием JMS.

Отношения Geronimo/WAS CE с JMS тоже (в настоящий момент) далеки от идеальных. Проблема – в принципиальной нестыковке идеологии использования JMS и некоторых принципов Geronimo. Суть рассогласования проста. JMS основан на использовании «глобальных», то есть общедоступных, администрируемых объектов. Предполагается, что они создаются администратором системы, а программный код просто использует их, причем для поиска применяется типовой для J2EE подход – использование службы имен JNDI.

Наоборот, философия Geronimo построена на отказе от глобальных контекстов службы имен с целью повышения производительности системы.

Это означает, что возникают определенные трудности для получения объектных ссылок на администрируемые объекты вне «единого пространства XML-дескрипторов», другими словами, вне EAR-архивов J2EE.

Проблема осознается разработчиками. Конечно, радикально вопрос решается поддержкой глобального контекста JNDI и автоматической регистрацией объектных ссылок на администрируемые объекты JMS в этом контексте при развертывании этих объектов на сервере. Поддержка глобального контекста обещана в версии 1.2. Пока этого нет, используется паллиативное решение. При запуске сервера устанавливается (для версий 1.0.x сервера) конфигурация с именем geronimo/activemq/1.0/car, в которой создаются специальный экземпляр службы имен с глобальным контекстом и несколько администрируемых объектов, в число которых входят три фабрики соединений. Получить доступ к этим объектам можно, например, с помощью такого кода:

Properties props = new Properties();

props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
  "org.activemq.jndi.ActiveMQInitialContextFactory");
props.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");
// props.setProperty("java.naming.security.principal", "system");
// props.setProperty("java.naming.security.credentials", "manager");
          
Context initContext = new InitialContext(props);

Если после этого для полученного контекста вызвать метод list() – например,

NamingEnumeration enum = initContext.list("");
while (enum.hasMore())
  {
    Object o = enum.next();
    System.out.println(o);
  }

то выводимая информация будет иметь следующий вид:

QueueConnectionFactory: org.activemq.ActiveMQConnectionFactory
dynamicTopics: org.activemq.jndi.ActiveMQInitialContextFactory$2
ConnectionFactory: org.activemq.ActiveMQConnectionFactory
TopicConnectionFactory: org.activemq.ActiveMQConnectionFactory
dynamicQueues: org.activemq.jndi.ActiveMQInitialContextFactory$1 

Как видно, созданы три фабрики соединений (одна – общего назначения, одна – только для топиков и одна – только для очередей). Кроме этих администрируемых объектов, созданы два дочерних контекста – dynamicTopics и dynamicQueues. С точки зрения прав доступа JNDI, эти контексты доступны только для чтения. Тем не менее, поместить в них информацию (объектные ссылки на очереди и топики) все-таки можно.

Об использовании этих фабрик соединений и контекстов (dynamicTopics и dynamicQueues) будет рассказано ниже.

Самое важное, что надо иметь в виду при использовании JMS вместе с WAS CE: для создания администрируемых объектов нужно создать RAR-модули (коннекторы) и соответствующие им конфигурации GBeans, а доступ к ресурсам – администрируемым объектам – задается с помощью XML-дескрипторов. Это означает, что все делается быстро и просто только при использовании JMS между различными компонентами системы в составе одного EAR-архива. В противном случае разработчик должен сам обеспечить доступ к объектам JMS. В каждом конкретном случае это не представляет большой сложности.


Рисунок 1.

Запуск брокера сообщений в WAS CE

WAS CE позволяет создавать и запускать несколько различных брокеров сообщений. В частности, можно использовать WebSphere MQ или другую коммерческую реализацию. По умолчанию в комплект поставки входит брокер ActiveMQ, созданный в рамках open-source проекта. Он находится в конфигурации geronimo/activemq-broker/1.0/car, которая запускается по умолчанию при старте сервера. Файл конфигурации сервера ..\var\config\config.xml содержит следующее описание параметров этой конфигурации:

<configuration name=
    "geronimo/activemq-broker/1.0/car">
  <gbean name="ActiveMQ.tcp.default">
    <attribute name="host">0.0.0.0</attribute>
    <attribute name="port">61616</attribute>
  </gbean>
</configuration>

Брокер сообщений ActiveMQ поддерживает различные протоколы взаимодействия. Консоль администратора позволяет увидеть список протоколов и соответствующие параметры настройки (в колонке навигации выбран элемент JMS Server):

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

Создание администрируемых объектов в WAS CE

Основным и самым удобным средством для создания обоих видов администрируемых объектов – и фабрик соединений, и целевых объектов – является консоль администратора. К сожалению, в настоящий момент реально можно сказать не «является», а «должна являться».

В разных версиях Geronimo и WAS CE содержатся различные эксперты, которые имеют разную функциональность. Наиболее развитыми возможностями обладают эксперты Geronimo. Что касается WAS CE, то в версии 1.0.x эксперты консоли позволяют создавать только фабрики соединений, но не целевые объекты, и возможности настройки фабрик очень ограничены. Это, вне всякого сомнения, будет исправлено в версии WAS CE 1.1. Тем не менее, пока при работе с WAS CE приходится создавать соответствующий XML-дескриптор «вручную». Текст будет приведен ниже.


Рисунок 2.

Еще раз напоминаем – создаваемые таким образом объекты непосредственно не доступны через JNDI. Предполагается, что доступ к ним организован с помощью XML-дескрипторов, входящих в EAR-приложение. Например, для организации упрощенной версии Enterprise Service Bus (ESB).

Если вас не устраивают фабрики соединений, созданные в «стандартной» конфигурации geronimo/activemq/1.0/car – которые, к тому же, доступны через JNDI – вы можете создать новую фабрику соединений с помощью эксперта консоли администратора. Для этого в панели навигации нужно выбрать элемент JMS:

Выбрав ссылку «Add New JMS Connecton Factory», вы попадете в режим задания новой фабрики соединений. К сожалению, эксперт из состава WAS CE 1.0.x не позволяет задать все существенные параметры фабрики (рисунок 3).

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

Второе поле должно содержать URL сервера. URL содержит имя протокола и имя сервера – например, vm://localhost или tcp://127.0.0.1:61616).

Пятое и шестое поля позволяют задать максимальный размер пула соединений (10 по умолчанию) с брокером и интервал «блокирующего» тайм-аута в миллисекундах (5000 по умолчанию). Это период времени, в течение которых приложение, которое хочет установить соединение с брокером сообщений, будет ждать освобождения сообщения из пула, если уже достигнут максимально возможный размер пула.

Новую фабрику соединений можно (а при работе с WAS CE 1.0 – и удобнее) создать «вручную» – точнее, сгенерировать основу дескриптора, а затем внести необходимые изменения. После этого созданный XML-дескриптор для коннектора Java нужно развернуть на сервере с помощью утилиты deploy.

Дескриптор для фабрики соединения может выглядеть так:

<?xml version="1.0" encoding="UTF-8"?>
<connector xmlns="http://geronimo.apache.org/xml/ns/j2ee/connector-1.0"
    xmlns:naming="http://geronimo.apache.org/xml/ns/naming-1.0"
    configId="geronimo/myjms/1.0/car"
    parentId="geronimo/activemq-broker/1.0/car">

  <resourceadapter>
    <resourceadapter-instance>
      <resourceadapter-name>DemoConnectionFactory</resourceadapter-name>
      <config-property-setting name="ServerUrl">
        vm://localhost
      </config-property-setting>
      <config-property-setting name="UserName">
        system
      </config-property-setting>
      <config-property-setting name="Password">
        manager
      </config-property-setting>

      <naming:workmanager>
        <naming:gbean-link>
          DefaultWorkManager
        </naming:gbean-link>
      </naming:workmanager>
    </resourceadapter-instance>
    <outbound-resourceadapter>
      <connection-definition>
        <connectionfactory-interface>
          javax.jms.ConnectionFactory
        </connectionfactory-interface>
        <connectiondefinition-instance>
          <name>DemoConnectionFactory</name>
          <connectionmanager>
            <xa-transaction>
              <transaction-caching/>
            </xa-transaction>
            <single-pool>
              <max-size>10</max-size>
              <min-size>0</min-size>
              <blocking-timeout-milliseconds>
                5000
              </blocking-timeout-milliseconds>
              <idle-timeout-minutes>
                30
              </idle-timeout-minutes>
              <match-one/>
            </single-pool>
          </connectionmanager>
        </connectiondefinition-instance>
      </connection-definition>
    </outbound-resourceadapter>
  </resourceadapter>
  ...
</connector>

Как обычно, дескриптор содержит имя базовой конфигурации (parentId="geronimo/activemq-broker/1.0/car") и создаваемой конфигурации (configId="geronimo/myjms/1.0/car"). Параметр <idle-nimeout-minute> позволяет задать время (по умолчанию – 15 минут), по истечению которого неиспользуемое соединения закрывается, удаляется из пула соединений и размер пула уменьшается.

Тип поддерживаемых транзакций зависит от свойств реализации JMS и от выбранного провайдера. Серьезные провайдеры обеспечивают поддержку как локальных, так и распределенных транзакций. В частности, поддержка распределенных транзакций необходимо, если потребителями сообщений JMS являются MDB-компоненты EJB.

Для развертывания конфигурации, содержащей описанный администрируемый объект (или объекты), нужно в командной строке при вызове утилиты deploy задать имя созданного XML-дескриптора и указать RAR-файл, содержащий реализацию базовых возможностей администрируемых объектов JMS. Для брокера ActiveMQ в WAS CE 1.0 соответствующая реализация (в виде RAR-файла) находится в каталоге <install_dir>/ repository\activemq\rars\ и имеет имя activemq-ra-3.2.2.ibm.rar. таким образом, командная строка может выглядеть примерно так (предполагается, что XML-дескриптор имеет имя my_jms_admobj_plan_was.xml и находится в текущем каталоге):

java -jar "D:\AppServerCommunityEdition\bin\deployer.jar" --user system --password manager deploy my_jms_admobj_plan_was.xml "D:\AppServerCommunityEdition\repository\activemq\rars\activemq-ra-3.2.2.ibm.rar"   

После установки коннектора работоспособность созданной фабрики соединений можно проверить, выбрав ссылку «Test_connection» для нужной фабрики из списка.

Перейдем к созданию топиков и очередей. Поскольку эксперт WAS CE в версии 1.0 просто не предоставляет такой возможности, придется соответствующее описание создавать «вручную». Приведенный ниже фрагмент можно просто включить в XML-дескриптор, текст которого мы обсуждали только что – сразу после тега <resourceadapter>:

<! Topic -->
<adminobject>
  <adminobject-interface>javax.jms.Topic</adminobject-interface>
  <adminobject-class>
    org.codehaus.activemq.message.ActiveMQTopic
  </adminobject-class>
  <adminobject-instance>
    <message-destination-name>
      MyTopic
    </message-destination-name>
    <config-property-setting name="PhysicalName">
      MyTopicPhysical
    </config-property-setting>
  </adminobject-instance>
</adminobject>
<! Queue -->
<adminobject>
  <adminobject-interface>javax.jms.Queue</adminobject-interface>
  <adminobject-class>
    org.codehaus.activemq.message.ActiveMQQueue
  </adminobject-class>
  <adminobject-instance>
    <message-destination-name>
      MyQueue
    </message-destination-name>
    <config-property-setting name="PhysicalName">
      MyQueuePhysi-cal
    </config-property-setting>
  </adminobject-instance>
</adminobject>

Здесь тег <message-destination-name> задает имя ресурса – в соответствие с классическими правилами задания ресурсов с помощью коннекторов Java. Что касается свойства с атрибутом PhysicalName, то он позволяет задать физического имя целевого объекта, создаваемого на сервере во время развертывания коннектора и создания для него конфигурации.

Созданный таким образом коннектор с нужными администрируемыми объектами доступен для всех приложений, запущенных на данном сервере. Если нужно, чтобы коннектор был доступен только одному приложению, то вместо развертывания с помощью командной строки ссылку на rar-файл и его XML-дескриптор нужно поместить в дескриптор application.xml для EAR-приложения, например, так:

<application
  xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
  http://java.sun.com/xml/ns/j2ee/application_1_4.xsd"
  version="1.4">
  <module>
    ...
  </module>
  ...
  <module>
    <connector>activemq-ra-3.2.2.ibm.rar</connector>
  </module>
</application>

Путь к XML-дескриптору указывается в теге <alt-dd> дополнительного дескриптора (обычно он имеет имя geronimo-application.xml):

<application
  xmlns="http://geronimo.apache.org/xml/ns/j2ee/application-1.0"
  configId="MyApplication">
  <module>
    <connector>activemq-ra-3.2.2.ibm.rar</connector>
    <alt-dd>my_jms_admobj_plan_was.xml</alt-dd>
  </module>
  ...
</application>

Доступ к администрируемым объектам в WAS CE с помощью дескрипторов

Фабрики соединений и целевые объекты доступны с использованием подхода, продемонстрированного в примере создания Web-приложения, выполняющего доступ к БД через JNDI. Просто в случае JNDI именем ресурса было имя фабрики соединений DataSource, а в случае JMS – имя фабрики соединений JMS или имя ресурсного объекта. J2EE предусматривает для этого специальные стандартные теги: универсальный <resource-ref> для фабрик соединений (JDBC и JMS) и специализированный <message-destination-ref> – для топиков и очередей JMS. Следовательно, дескрипторы (стандартные) для web- и/или ejb-компонентов, использующих созданные администрируемые объекты, могли бы содержать такой фрагмент:

<resource-ref>
  <res-ref-name>jms/MyConnectionFactory</res-ref-name>
  <res-type>javax.jms.ConnectionFactory</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<message-destination-ref>
  <message-destination-ref-name>
    jms/MyTopic
  </message-destination-ref-name>
  <message-destination-type>
    javax.jms.Topic
  </message-destination-type>
  <message-destination-usage>
    Consumes
  </message-destination-usage>
  <message-destination-link>
    MyTopic
  </message-destination-link>
</message-destination-ref>
<message-destination-ref>
  <message-destination-ref-name>
    jms/MyQueue
  </message-destination-ref-name>
  <message-destination-type>
    javax.jms.Queue
  </message-destination-type>
  <message-destination-usage>
    Produces
  </message-destination-usage>
  <message-destination-link>
    MyQueue
  </message-destination-link>
</message-destination-ref>
<message-destination>
  <message-destination-name>MyTopic</message-destination-name>
</message-destination>
<message-destination>
  <message-destination-name>MyQueue</message-destination-name>
</message-destination>

Этот фрагмент сопоставляет вместе имена ресурсов, объявленные в дескрипторе RAR’а (MyTopic и MyQueue, теги <message-destination-link> и <message-destination-name> данного дескриптора и тег <message-destination-name> дескриптора для коннектора RAR’а, содержащего данные объекты) и локальные JNDI-имена, которые могут быть использованы для доступа к этим ресурсам в Java-приложении. В данном примере JNDI имя, например, топика задано в виде “jms/MyTopic”. Это означает, что при обращении к методу lookup JNDI нужно указывать (относительно начального контекста) контекст «java:comp/env/jms/MyTopic».

Обратите также внимание на тег <message-destination-usage>, который задает режим работы целевого объекта, и возможные значения для этого тега.

Итак, задана вся необходимая информация для топика и очереди. Но этого еще недостаточно для использования фабрики соединений – нетрудно убедиться, что приведенный выше фрагмент дескриптора задает локальное имя для фабрики, но не привязывает это имя к конкретному экземпляру фабрики. Нужный в нашем примере экземпляр создавался с именем DemoConnectionFactory, заданном в теге <resourceadapter-name> дескртиптора для RAR’а, использованного при создании администрируемых объектов. Это имя (DemoConnectionFactory) нужно связать с выбранным JNDI-именем (jms/MyConnectionFactory). Для этого используется, как обычно, вспомогательный дескриптор Geronimo. Его соответствующий фрагмент мог бы выглядеть так:

...
<naming:resource-ref>
  <naming:ref-name>
    jms/MyConnectionFactory
  </naming:ref-name>
  <naming:resource-link>
    DemoConnectionFactory
  </naming:resource-link>
</naming:resource-ref>
...

Теперь можно приступать к написанию Java-программы.

Использование объектов JMS в Java-программе

Прежде, чем перейти к использованию JMS API, нужно сделать одно замечание.

Приведенные выше дескрипторы решают проблему создания фабрик соединения и целевых объектов, а также доступа к ним – но только в пределах одного EAR-приложения. Это ограничение возникает просто потому, что локальный контекст JNDI нельзя использовать для связи между разными приложениями, не говоря уже о приложениях, работающих на разных серверах.

Между тем одним из наиболее популярных видов использования JMS является передача сообщений между приложениями – в частности, для оповещения сервером своих клиентских приложений о событиях, происходящих на сервере. Ценность JMS и состоит, в том числе, в том, что для получения сообщения серверное приложение создавать не нужно.

Итак, есть различные приложения, которые хотят обмениваться сообщениями. Есть серверное приложение, которое используют ту или иную реализацию брокера сообщений. Проблема в том, как «договориться» при написании приложений-отправителей и приложений-получателей о том, какие именно административные объекты использовать.

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

Напоминаем, что в комплект поставки WAS CE входит конфигурация geronimo/activemq/1.0/car, которая содержит три фабрики соединений и два дочерних контекста – один для топиков (dynamicTopics), второй – для очередей (dynamicQueues). Реализация службы JNDI для ActiveMQ создана таким образом, что она создает новые временные (динамические) топики или очереди в случае, если обращение к имени целевого объекта в контексте dynamicTopic или dynamicQueue привело к ошибке потому, что указанное имя не найдено.

Таким образом, даже запускаемые на разных компьютерах клиентские приложения JMS могут использовать одни и те же администрируемые объекты.

Ниже приведены фрагменты Java-кода для отправки и получения простого сообщения JMS. В данном примере в качестве целевого объекта используется очередь (Queue).

Приложение-отправитель:

import java.util.Properties;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;

public class SenderMainClass 
{

  public static void main(String[] args) 
  {
    
    ConnectionFactory cf = null;  // фабрика соединений с брокером
    Connection conn = null;  // соединение
    QueueSession qSession = null;  // сессия для работы с очередями
    Queue q = null;      // целевой объект (очередь)
    QueueSender qSender = null;  // отправитель сообщений
    try
    {
      Properties props = new Properties();

      props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
        "org.activemq.jndi.ActiveMQInitialContextFactory");
      props.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");

      // Получение ссылки на начальный контекст реализации службы имен ActiveMQ
      Context initContext = new InitialContext(props);

      // Получение объектной ссылки на ранее созданную (конфигурация
      // Geronimo/activemq/1.0/car) фабрику соединений
      Object obj  = 
        initContext.lookup("QueueConnectionFactory");
      if (obj != null)
      {
        ...
      }
      else
      {
        System.out.println("Connection factory lookup failure...");
        ...
      }
      
      // Поиск динамической очереди, зарегистрированной под указанным именем
      // в службе имен ActiveMQ. Если такая очередь не найдена,
      // она будет создана.

      q = (Queue)initContext.lookup ("dynamicQueues/MyQ");
      
      // Создание соединения с брокером сообщений
 
      conn = cf.createConnection();

      // Создание нетранзакционной (первый аргумент имеет значение false) 
      // сессии для данного соединения в режиме автоматического уведомления о 
      // получении сообщения от брокера

      qSession = (QueueSession)conn.createSession (false,
        Session.AUTO_ACKNOWLEDGE);

      // Создание в контексте этой сессии отправителя сообщений для очереди,
      // сопоставленного с указанной ранее найденной (или созданной) очередью

      qSender = (QueueSender)qSession.createSender(q);

      // Создание текстового сообщения

      Message m = qSession.createTextMessage("This is a text message");

      // Активизация соединения с брокером запросов
      conn.start();
  
      // Посылка нескольких сообщений
      for (int i = 0; i < 10; i++)
        qSender.send(m);
      // Закрытие соединения с брокером (с завершеним сессии)
      conn.close();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

Код приложения-получателя может выглядеть так:

import java.util.Properties;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;

public class ReceiverMainClass 
{
  public static void main(String[] args) 
  {
    ConnectionFactory cf = null;
    Connection conn = null;
    QueueSession qSession = null;
    Queue q = null;
    QueueReceiver qReceiver = null;  // Получатель сообщений из очереди
    try
    {
      Properties props = new Properties();

      props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
        "org.activemq.jndi.ActiveMQInitialContextFactory");
      props.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");
                   
      Context initContext = new InitialContext(props);

      Object obj  = initContext.lookup("QueueConnectionFactory");
      if (obj != null)
      {
        cf = (ConnectionFactory)obj;
      }
      else
        System.out.println("Connection factory lookup failure...");
        ...

      // Поиск (или создание) динамической очереди
      q = (Queue)initContext.lookup ("dynamicQueues/MyQ");
      
      conn = cf.createConnection();
      qSession = (QueueSession)conn.createSession (false,
        Session.AUTO_ACKNOWLEDGE);

      // Создание получателя сообщений, не использующего фильтров
      qReceiver = (QueueReceiver)qSession.createReceiver(q, null);

      conn.start();
    
      // Попытка получить несколько сообщений из очереди в синхронном режиме.
      // Если сообщений нет, вызов метода receive() блокирует поток, вызвавший
      // этот метод. Предполагается, что отправитель помещает в данную очередь
      // только текстовые сообщений

      int count = 5;
      while (count > 0)
      {
        Message m = qReceiver.receive(); 
        TextMessage tm = (TextMessage)m; 
        System.out.println(tm.getText());
        count--;
      }
      
      conn.close();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
}

Чтобы программы могли работать, загрузчику классов необходим доступ к большому количеству стандартных классов JMS и классов, специфических для ActiveMQ. Ниже приведен список необходимых jar-файлов. При работе, например, с Eclipse их нужно просто включить в проект.

<install_dir>\repository\org.apache.geronimo.specs\jars/geronimo-j2ee_1.4_spec-1.0.jar
<install_dir>\repository\org.apache.geronimo.specs\jars\geronimo-jms_1.1_spec-1.0.jar
<install_dir>\repository\commons-logging\jars\commons-logging-1.0.4.jar
<install_dir>\repository\concurrent\jars\concurrent-1.3.4.jar

Заключение

WAS CE предоставляет разработчикам полнофункциональную поддержку JMS в случае использования передачи сообщений в пределах системы, реализованной в виде EAR-архива. Организация взаимодействия различных приложений также возможна – с использованием средств, предоставляемых конкретным провайдером реализации JMS. Для ActiveMQ – реализации брокера сообщений по умолчанию – таким средством являются динамические очереди и особенности поведения службы имен ActiveMQ.


Эта статья опубликована в журнале RSDN Magazine #3-2007. Информацию о журнале можно найти здесь
    Сообщений 6    Оценка 0        Оценить