на основе Java JMS
Цель работы - получение практических навыков создания корпоративной информационной системы (КИС) на основе Java JMS, а именно:
- изучение вариантов взаимодействия удаленных объектов Java с использованием JMS;
- изучение компонентов JMS, необходимых для функционирования распределенных приложений Java;
- разработка распределенного Java-приложения с использованием JMS для функционирования в сетевом режиме;
- анализ достоинств и недостатков JMS по сравнению с другими технологиями распределенного взаимодействия приложений.
1 Теоретическое введение
1.1 Архитектура JMS-базированной КИС
J ava M essaging S ystem(JMS) представляет собой универсальный интерфейс обобщенного Java API для корпоративных приложений (КИС), ориентированных на работу с помощью сообщений (message-oriented application programming) с целью обеспечения их независимости от конкретных реализаций служб обработки сообщений. В настоящее время спецификация JMS - это промышленный стандарт для обработки сообщений в масштабе предприятия.
Корпоративная информационная система типа MOM (М essage O riented M iddleware) (рисунок 1), обеспечивает так называемое «слабое связывание» приложений-поставщиков (Producers) с приложениями-потребителями (Consumers) посредством передачи сообщений (Messages) c помощью фабрик соединений (Connection Factories – CF).
Рисунок 1 – КИС типа МОМ
Архитектура JMS -базированной КИС выглядит следующим образом (рисунок 2):
Рисунок 1
- JMS-клиент (JMS client) - прикладная программа Java, использующая JMS для отправки и приёма сообщений;
- JMS - поставщик (message producer) - JMS -клиент, посылающий сообщение;
- JMS - потребитель (message consumer) - JMS -клиент, принимающий сообщение[1];
- не JMS-клиент - программа, не имеющая отношения к JMS, однако являющиеся неотъемлемой частью JMS -проекта КИС;
|
- JMS-провайдер (JMS provider) - система обработки сообщений, управляющая маршрутизацией и доставкой сообщений;
- JMS-приложение (JMS application) - прикладная программная система, состоящая из нескольких JMS -клиентов, и, как правило, одного JMS -провайдера;
- JMS-сообщение (JMS message) - объект, передаваемый и принимаемый JMS- клиентами;
- управляемые объекты JMS (administered objects) - буферы для хранения JMS- сообщений;
- средства администрирования JMS (administrative tools) - средства управления ресурсами, которые использующимися JMS клиентами.
- JMS-сессия (JMS Session) - однопоточный контекст для отсылки и получения JMS -сообщений. JMS сессия может быть нетранзакционной, с поддержкой локальных транзакций, или участвовать в распределенных транзакциях;
- Расположение (destination) – объект, который используется клиентом либо для определения адреса доставки сообщения, либо для получения сообщений, доставленных по определенному адресу.
- Соединение (Connection) - соединение с JMS -провайдером;
- Фабрика соединения (Connection Factory - CF) - администрируемый объект, используемый для создания соединения.
- Java Naming and Directory Interface (JNDI) - Java API, организованный в виде службы каталогов и позволяющий Java-клиентам открывать и просматривать данные и объекты по их именам (рисунок 2).
Рисунок 2
JMS определяет инфраструктуру компонентов, позволяющую разработчикам создавать автоматизированные, интеллектуальные решения, - для сетевого управления в быстро меняющихся ситуациях. JMS определяет трехуровневую архитектуру: уровень сетевых ресурсов, уровень агентов и уровень управления (рисунок 3).
|
Управляющее приложение |
Агент управления Java |
Уровень управления |
Уровень агентов |
Управляемый ресурс |
Управляемый ресурс |
Управляемый ресурс |
Уровень сетевых устройств |
Рисунок 3
Уровень сетевых устройств делает объект управляемым. Уровень агентов предоставляет управляющие сервисы, делающие ресурсы доступными для управления. Наконец, уровень управления дает возможность управляющему приложению осуществлять доступ к управляемым ресурсам и взаимодействовать с ними через JMS -агентов. Кроме того, JMS обеспечивает поддержку существующих протоколов сетевого управления, например, SNMP, так что JMS позволяет осуществлять интеграцию КИС с существующими системами сетевого управления.
Модель обмена сообщениями JMS удобно использовать в том случае, если распределенное приложение КИС обладает следующими характеристиками:
- взаимодействие между компонентами КИС является асинхронным;
- информация (сообщение) должна передаваться нескольким или даже всем компонентам КИС (семантика передачи «от одного ко многим »);
- передаваемая информация используется внешними системами, часть из которых неизвестна на момент проектирования КИС или интерфейсы которых подвержены частым изменениям[2];
- обменивающиеся информацией (сообщениями) компоненты КИС выполняются в разное время, что требует наличия посредника для промежуточного хранения переданной информации.
Стандартные интерфейсы JMS доступны в пакете javax.jms, который входит в комплект jdk. Реализация JMS от корпорации Oracle находится в пакете oracle.jms. Поскольку JMS является лишь интерфейсной оболочкой, описывающей доступные для приложения методы, то для разработки и тестирования КИС нужна конкретная реализация интерфейсов JMS, называемая JMS-провайдером. Провайдеры JMS создаются независимыми производителями, и в настоящее время существует несколько таких реализаций (далее рассматривается реализация, включенная в состав Java2 Enterprise Edition и Oracle GlassFish Server).
|
1.2 Сообщения
Типы сообщений
Сообщение в JMS - это Java-объект, состоящий из заголовка (header) и тела (body). В заголовке находится служебная информация, а тело сообщения содержит в себе пользовательские данные.
JMS API определяет несколько типов сообщений:
- BytesMessage содержит массив примитивов в теле сообщения. Таким образом, может быть использован для передачи данных между двумя приложениями в их родном формате, который может быть несовместим с другими типами Message. Также, может быть полезным в тех случаях, когда JMS используется как транспорт между двумя системами. Поскольку в нем можно хранить примитивные типы данных, они могут быть неправильно интерпретированы. Например, long может быть сохранен как short. Это может вызывать ошибки. Поэтому рекомендуется читать данные строго в одном порядке и использовать те же типы данных, которые были использованы отправителем.
- StreamMessage содержит поток java-примитивов в теле сообщения. Содержит несколько удобных методов для чтения данных, сохраненных в сообщении. Однако, StreamMessage предотвращает неправильную интерпретацию данных. Например, чтение long как short. Это достигается тем фактом, что StreamMessage записывает также информацию о типах данных, а не только их значения.
- TextMessage содержит в виде сообщения объект java.lang.String. Таким образом, его полезно использовать для обмена текстовыми данными. Также, может быть использован для обмена комплексными текстовыми данными на подобии XML-документов.
- ObjectMessage содержит объекты, реализующие интерфейс Serializable. Тем самым, позволяя приложениям обмениваться друг с другом объектами Java. Получатель сообщения должен привести тип объекта сообщения к реальному типу объекта. Поэтому, перед получением объекта, получатель должен знать действительный тип отосланного объекта. Неправильное приведение типов может породить ClassCastException. Более того, определение класса объекта, отосланного в сообщении, должно быть доступно на обоих машинах. Если определение класса недоступно на получателе, будет порождено исключение ClassNotFoundException. Некоторые типы MOM могут поддерживать динамическую загрузку необходимых классов по сети, но спецификация JMS не определяет это как обязательный пункт.
- MapMessage содержит данные в формате ключ-значение. Таким образом, тело сообщения представляет из себя ни что иное, как объект java.util.Properties. Значениями ключей могут быть Java примитивы или их оболочки.
Отметим также, что BytesMessage хранит данные примитивных типов посредством перевода их в байтовое представление. Таким образом, сообщение становится одни сплошным куском байтов. StreamMessage умеет различать типы данных, которые хранятся в теле сообщения, поскольку хранит информацию о типах данных. BytesMessage позволяет читать данные с использованием любого типа. Это может вызвать ошибки при неправильном считывание различных типов данных. Например, long считывается в short.
Структура сообщения
Сообщение состоит из трех частей: заголовок, раздела свойств и тело.
Заголовок сообщения содержит значения полей сообщения, которые разработчик может использовать в приложении (таблица 1).
Таблица 1
Поле | Описание |
JMSDestination | Содержит имя расположения, в которое посылается сообщение |
JMSDeliveryMode | Определяет, является ли сообщение сохраняемым или нет |
JMSExpiration | Определяет, когда сообщение устареет и будет удалено из системы (по умолчанию сообщение не устаревает никогда) |
JMSPriority | Определяет приоритет сообщения от 0 до 9 (по умолчанию 4) |
JMSMessageID | Уникальный идентификатор сообщения |
JMSTimestamp | Содержит информацию, когда именно MOM приняла сообщение от поставщика |
JMSCorrelationID | Может быть использовано разработчиком для согласования сообщений: например, если нужно переслать ряд сообщений, обьединенных в одну логическую группу (такую как набор товаров в заказе, при этом в каждое сообщение о товаре можно добавить номер заказа) |
JMSReplyTo | Может быть использовано разработчиком для того, чтобы потребитель знал, кому отсылать ответ |
JMSType | Может быть использован разработчиком для того, чтобы дать приложению информацию, как обращаться с данным сообщением; тип здесь понимается как application-specific type |
JMSRedelivered | Устанавливается, если сообщение не было доставлено с первой попытки, например, в случае, когда потребитель не подтвердил получение сообщения |
JMS предоставляет методы get() и set() получения и установки значений для каждого поля заголовка.
Раздел свойств содержит пары «ключ - значение», которые могут быть использованы для пересылки определенных данных между поставщиком и потребителем. В качестве значений могут быть использованы примитивные Java-типы (boolean, byte, float, double, short, int, long), а также строки (java.lang.String). Свойства устанавливаются и читаются с помощью соответствующих методов set() и get(). Например, для установки integer-свойства с ключом " MyProperty " и значением равным 100 нужен код:
textMessage.setIntProperty("MyProperty", 100);
, а для чтения:
int value = textMessage.getIntProperty("MyProperty");
Доставка сообщений
Сообщения могут быть несохраняемыми и сохраняемыми. Если сообщение сохраняемо, то MOM сохраняет его в БД или файле; такое сообщение переживет «гибель» MOM. Какой тип сообщения выбрать – зависит от того, что вы ожидаете от КИС: большей надежности (в случае сохраняемых сообщений), либо большей производительности (в случае несохраняемых сообщений).
Можно установить режим доставки сообщения в момент его посылки (по умолчанию режим будет таким, каким его установил администратор при создании ConnectionFactory). Например:
textMessage.setText("It's my message");
sender.send(textMessage, DeliveryMode.NON_PERSISTENT ,
messagePriority, messageTimeToLive);
Уведомления о доставке сообщений - это механизм, при котором провайдер оповещается о том, было ли сообщение доставлено адресату. Тип уведомления обычно задается при создании объекта сессии. Всего типов уведомления три:
- Session.DUPS_OK_ACKNOWLEDGE - «ленивое» уведомление о доставке. При нем сводится к минимуму работа по нахождению дубликатов сообщения. Должно использоваться только в том случае, если ожидается появление дублирующих сообщений.
- Session.AUTO_ACKNOWLEDGE - автоматическое уведомление об отправке при получении сообщения адресатом.
- Session.CLIENT_ACKNOWLEDGE - уведомление об отправке устанавливается вручную вызовом метода acknowledge() объектов Message.
Какой тип подтверждения сообщения выбрать – опять-таки нужно решать на основе анализа требований к КИС.
Отсеивание сообщений
JMS предоставляет механизм под названием селектор сообщений (message selector), позволяющий приложению фильтровать и классифицировать принимаемые сообщения. Селектор сообщений — это строка, содержащая выражение, основанное на спецификации SQL92. Селектор вызывается каждый раз при получении очередного сообщения. Сообщения, прошедшие отбор по критерию, становятся доступными приложению, остальные отбрасываются. Выборка основана на сравнении полей заголовка и свойств сообщения.
Транзакции
Очень часто возникает необходимость посылать сообщения в контексте транзакции. JMS - транзакции группируют набор отправляемых сообщений в атомарную единицу обработки данных (рисунок 4).
Рисунок 4 - Использование механизма транзакций в КИС
Если в процессе передачи данных в пределах транзакции возникла хоть одна ошибка, всю транзакцию можно откатить (rollback).
Например, получив сообщение из очереди queue1, потребитель посылает это сообщение в другую очередь queue2. Если в этот момент происходит исключительная ситуация, то желательно, чтобы КИС вернулась в первоначальное состояние, т.е. сообщение вновь оказалось в очереди queue1.
Одно из главных преимуществ JMS в том, что посылка сообщения может быть частью транзакции. За создание и контроль транзакций отвечает объект Session. Распределенные транзакции могут поддерживаться посредством использования Java Transaction API (JTA).
Существует два подхода в использовании транзакций совместно с JMS. Один применим только внутри JMS, другой может быть использован для включения не только JMS но и, например, JDBC -запроса к БД, в состав той же транзакции.
Первый подход заключается в использовании transacted sessions. Для этого достаточно при создании session выставить флаг transacted в значение true:
Session session = con nection .createQueueSession( true , Session.AUTO_ACKNOWLEDGE);
Важно отменить, что в случае transacted session второй аргумент – тип подтверждения получения – хоть и присутствует, но игнорируется JMS. И подтверждение получения будет произведено, когда транзакция будет завершена методом commit() обьекта session, который должен быть вызван клиентом, получающим сообщение. Интерфейс javax.jms.Session включает два метода: commit() - для подтверждения транзакции и rollback() - для отката к первоначальному состоянию.
И поставщик, и потребитель могут использовать transacted session.
Когда поставщик использует transacted session, посланные сообщения накапливаются в буфере до тех пор, пока он не вызовет либо метод commit(), либо метод rollback(). В случае вызова commit(), сообщения будут доступны для доставки, в случае вызова rollback(), JMS очистит буфер сообщений.
В случае, когда потребитель использует transacted session, объект session контролирует подтверждение доставки сообщения. В случае вызова метода commit() производится подтверждение получения сообщений в контексте данной транзакции, а в случае вызова метода rollback() JMS вернет все сообщения в соответствующее исходное расположение.
Второй подход заключается в использовании JTA [3] -транзакций совместно с JMS. Этот подход позволяет разработчику включать в единую транзакцию как посылку сообщений, так и запрос к БД.
Достигается это стандартным способом – использованием javax.transaction.UserTransaction. Например:
UserTransaction myTransaction = (UserTransaction)ctx.lookup("MyUserTransaction");
myTransaction.begin();
sender.send(textMessage);
myStatement.executeUpdate("INSERT INTO testTable VALUES (100,'BILL')");
myTransaction.commit();
При этом важно отметить, что механизм JTA не может быть использован совместно с transacted session. Если попытаться совместить несовместимое, то transacted session будет игнорировать вызовы commit() или rollback() интерфейса UserTransaction.
1.3 Модели передачи сообщений
JMS предоставляет две модели передачи сообщений (т.н. зоны сообщений (messaging domains)): «издание - подписка » (publish an subscribe) и «точка - точка » (point to point).
Модель передачи сообщений «издание - подписка»
Модель передачи сообщений «издание - подписка » (рисунки 5…6) определена в пакете javax.jms.Topic и используется в том случае, если сообщение, отправленное одним клиентом, должно быть получено несколькими другими клиентами.
Рисунок 5 - Модель взаимодействия клиентов по некоторой теме
Рисунок 6 – Буферизация сообщений в модели взаимодействия «издание - подписка»
При использовании модели взаимодействия «издание - подписка» один поставщик может посылать сообщения многим потребителям через виртуальный канал, называемый темой (topic).
Потребители могут выбрать подписку (subscribe) на любую тему. Все сообщения, направляемые в тему, передаются всем потребителям данной темы. Каждый потребитель принимает копию каждого сообщения. В JMS эта концепция реализуется с помощью специальных слушателей (listeners), регистрируемых в системе. При возникновении нового события слушатель, закрепленный за данной темой, возбуждается.
Следует отметить, что при использовании модели «издание - подписка» клиенты JMS могут устанавливать долговременные подписки, позволяющие потребителям отсоединяться и позже снова подключяться и получать сообщения, поступившие во время отключения связи.
TopicConnectionFactory
TopicConnectionFactory - управляемый ресурс, получаемый с помощью обращения к JNDI для получения доступа к провайдеру. С помощью его метода createTopicConnection() мы можем получить объект TopicConnection.
TopicConnection
TopicConnection – создает подключение к провайдеру. Методы TopicConnection приведены в таблице 2.
Таблица 2
Название метода | Описание |
createTopicSession (boolean, int) | Метод возвращает объект TopicSession. Первый логический параметр указывает на то, будет ли сессия помечена как «transacted», второй целочисельный параметр — тип уведомления о доставке |
start() | Активирует получение сообщений от провайдера |
stop() | Временно приостанавливает получение сообщений, возобновить которое можно повторным вызовом метода start() |
close() | Закрывает соединение с провайдером и высвобождает все задействованные ранее ресурсы. |
TopicSession
TopicSession – однопоточный контекст для получения сообщений. Методы TopicSession приведены в таблице 3.
Таблица 3
Название метода | Описание |
createPublisher(Topic) | Возвращает объект TopicPublisher для отправки сообщений в определенную тему |
createSubscriber(Topic) | Возвращает TopicSubscriber, объект для получения сообщений из определенной темы. Сообщения будут приходить до тех пор, пока существует сам объект. |
createDurableSubscriber (Topic, String) | Возвращает TopicSubscriber, одновременно именуя подписку с помощью переданного второго параметра. Даже если подписчик неактивен, JMS сохранит их и доставит при первой же активации объекта или создании нового с таким же именем. |
unsubscribe(String) | Отписывает от темы с заданным именем |
commit() | Подтверждает все входящие или исходящие сообщения |
rollback() | Откатывает все входящие или исходящие сообщения |
create<тип сообщения> Message(...) | Набор методов для создания сообщений определенного типа |
Topic
Topic представляет собой хранилище сообщений и так же, как и TopicConnectionFactory, является управляемым объектом.
TopicPublisher
TopicPublisher — объект, использующийся для отправки сообщений. Некоторые из основных методов TopicPublisher приведены в таблице 4.
Таблица 4
Название метода | Описание |
publish(Message) | Публикует указанное сообщение |
setDeliveryMode(int) | Устанавливает тип доставки сообщения: · DeliveryMode.PERSISTENT — сообщение будет храниться до тех пор, пока не будет принято адресатом · DeliveryMode.NON_PERSISTENT — сообщение будет автоматически удалено, если адресат недоступен в момент доставки. |
setPriority(int) | Устанавливает приоритет отправленного сообщения. В качестве параметра можно передать целое число в диапазоне от 0…9. |
setTimeToLive(long) | Устанавливает время жизни отправляемого сообщения. |
TopicSubscriber
TopicSubscriber используется для получения сообщений. Некоторые из основных методов TopicSubscriber приведены в таблице 5.
Таблица 5
Название метода | Описание |
receive() | Получает очередное сообщение. Метод блокируется до тех пор, пока сообщение не будет получено. |
receive(long) | Получает очередное сообщение. При этом указывается интервал времени ожидания. Если сообщение не приходит в течение указанного количества миллисекунд, метод возвращает null. |
receiveNoWait() | Получает очередное сообщение. Если оно сразу же не было доступно, то метод просто завершает работу, ничего не возвращая. |
setMessageListener (MessageListener) | Устанавливает слушателя (MessageListener), который получает сообщения асинхронно. |
Отметим также, что существуют прочная (durable) и непрочная (non-durable) подписки. Прочная подписка (рисунок 7) гарантирует, что подписчик получит все сообщения, посланные в теме, а непрочная подписка не дает никаких гарантий насчет тех сообщений, которые были отосланы в момент, когда клиент был отключен от темы (рисунок 8).
Рисунок 7 - Прочная подписка. Все сообщения будут обработаны
Рисунок 8 - Непрочная подписка (сообщения M3 и M4 не будут обработаны)
Модель передаче сообщений «точка - точка»
Модель передаче сообщений «точка - точка » (point to point) (рисунок 9) определена в пакете javax.jms.Queue и служит для реализации обмена сообщениями между двумя компонентами.
Модель передачи сообщений «точка - точка» предоставляет возможность JMS- клиентам посылать и принимать сообщения (как синхронно, так и асинхронно) через виртуальные каналы, называемые очередями (queues). Модель основывается на методе опроса, при котором сообщения явно запрашиваются (считываются) JMS- клиентом из очереди. Несмотря на то, что чтение из очереди могут осуществлять несколько JMS- клиентов, каждое сообщение будет прочитано только единожды – JMS- провайдер JMS это гарантирует.
Рисунок 9 - Модель взаимодействия «точка - точка»
QueueConnectionFactory
QueueConnectionFactory – управляемый объект, получаемый из JNDI для создания подключения к провайдеру. QueueConnectionFactory содержит метод генерации createQueueConnection(), возвращающий объект QueueConnection.
QueueConnection
QueueConnection представляет активное подключение к провайдеру. Некоторые из методов, присущих QueueConnection, перечислены в таблице 6.
Таблица 6
Название метода | Описание |
createQueueSession (boolean, int) | Метод возвращает объект QueueSession. Первый логический параметр указывает на то, будет ли сессия помечана как «transacted», второй целочисельный параметр — тип уведомления о доставке сообщения |
start() | Активирует получение сообщений от провайдера |
stop() | Временно приостанавливает получение сообщений, возобновить которое можно повторным вызовом метода start() |
close() | Закрывает соединение с провайдером и высвобождает все задействованные ранее ресурсы. |
QueueSession
QueueSession — однопоточный контекст для отсылки и приема сообщений. Методы QueueSession приведены в таблице 7.
Таблица 7
Название метода | Описание |
createSender(Queue) | Возвращает объект QueueSender, служащий для передачи сообщений в определенное хранилище (в данном случае, представленное в виде очереди) |
createReceiver(Queue) | Возвращает объект QueueReceiver, служащий для приема сообщений из определенного хранилища (в данном случае, представленного в виде очереди) |
createBrowser(Queue) | Возвращает объект QueueBrowser, служащий для тех же целей, что и QueueReceiver |
commit() | Подтверждает все входящие или исходящие сообщения |
rollback() | Откатывает все входящие или исходящие сообщения |
create<тип сообщения> Message(...) | Набор методов для создания сообщений определенного типа |
Queue
Queue инкапсулирует хранилище сообщений. Это управляемый объект, доступ к которому можно получить посредством JNDI.
QueueSender
QueueSender – объект, используемый для посылки сообщений. Методы QueueSender приведены в таблице 8.
Таблица 8
Название метода | Описание |
send(Message) | Отсылает указанное сообщение |
setDeliveryMode(int) | Устанавливает тип доставки сообщения: · DeliveryMode.PERSISTENT — сообщение будет храниться до тех пор, пока не будет принято адресатом · DeliveryMode.NON_PERSISTENT — сообщение будет автоматически удалено, если адресат недоступен в момент доставки. |
setPriority(int) | Устанавливает приоритет отправленного сообщения. В качестве параметра можно передать целое число в диапазоне от 0…9 |
setTimeToLive(long) | Устанавливает время жизни отправляемого сообщения. |
QueueReceiver
QueueReceiver служит для получения сообщений из очереди. Методы QueueReceiver приведены в таблице 9.
Таблица 9
Название метода | Описание |
receive() | Получает очередное доставленное сообщение. Метод блокируется до тех пор, пока сообщение не будет получено. |
receive(long) | Получает очередное сообщение. При этом указывается интервал времени ожидания. Если сообщение не приходит в течение указанного количества миллисекунд, метод возвращает null. |
receiveNoWait() | Получает очередное сообщение. Если оно сразу же не было доступно, то метод просто завершает работу, ничего не возвращая. |
setMessageListener (MessageListener) | Устанавливает слушателя (MessageListener), который получает сообщения асинхронно. |
QueueBrowser
При использовании QueueReceiver для получения сообщений, сообщения удаляются по мере своей обработки. В отличие от QueueReceiver, QueueBrowser просматривает сообщения в очереди, однако не удаляет их. Для этого существует метод getEnumeration(), который возвращает перечисление java.util.Enumeration, которое может быть использовано для просмотра сообщений в очереди.
MessageListener
MessageListener — интерфейс с единственным методом – onMessage(Message), - который обеспечивает асинхронный прием и обработку сообщений. Интерфейс должен быть реализован клиентским классом, объекты которого могут передаваться в метод setMessageListener(MessageListener) объектов QueueReceiver. Как только сообщение приходит в очередь, вызывается метод onMessage(Message).
Объекты MessageListener используются в обеих моделях передачи сообщений.
1.4 Схема применения JMS
Схема применения JMS API предполагает выполнение разработчиком последовательности шагов, описанной далее.
Любой компонент, использующий JMS, прежде всего, должен создать соединение с JMS -провайдером - собственно системой, обеспечивающей всю функциональности управления сообщениями. Для этого используется специальная фабрика объектов ConnectionFactory. ConnectionFactory на самом деле является интерфейсом, который наследуют другие интерфейсы: QueueConnectionFactory, TopicConnectionFactory, XAQueueConnectionFactory и XATopicConnectionFactory; разработчик имеет дело именно с ними.
Провайдеры MOM значительно отличаются в своих механизмах и техниках обработки сообщений. Чтобы сохранять переносимость (портируемость) JMS- приложений, объекты, реализующие JMS- интерфейсы, могут быть изолированы от конкретной реализации провайдера с помощью технологии JNDI. Забегая наперед, следует сказать, что для получения доступа к тому самому буферу сообщений необходимы два основных объекта: менеджер подключения к «хранилищу» сообщений и хранилище сообщений. Это управляемые объекты, ссылки на которые помещаются в область имен JNDI.
Основные интерфейсы для работы с JMS:
- ConnectionFactory - менеджер подключения, использующийся для установки соединения с хранилищем сообщений и с системой обмена сообщениями в целом;
- Destination - ссылка на хранилище; используется JMS - клиентом для определения ресурса для отправки и приема сообщений; это либо очередь, либо тема в зависимости от используемой модели передачи сообщений. Как и ConnectionFactory, Destination связывается с деревом JNDI.
JMS определяет набор высокоуровневых интерфейсов, инкапсулирующих различные концепции работы с сообщениями:
- ConnectionFactory - управляемый объект, служащий для получения Connection;
- Connection – соединение с системой управления сообщениями;
- Destination - буфер для отправки и получения сообщений;
- Session - однопоточный контекст для посылки и приема сообщений;
- MessageProducer - объект, используемый для посылки сообщения;
- MessageConsumer - объект, используемый для приема сообщения.
Таблица 10 описывает базовые интерфейсы и их реализации для модели «издание - подписка» и «точка - точка».
Для того чтобы получить доступ к ConnectionFactory, Java-программа должна извлечь соответствующую ссылку из JNDI.
Таблица 10
Базовый интерфейс | Реализация для модели «издание - подписка» | Реализация для модели «точка – точка» |
ConnectionFactory | TopicConnectionFactory | QueueConnectionFactory |
Connection | TopicConnection | QueueConnection |
Destination | Topic | Queue |
MessageProducer | TopicPublisher | QueueSender |
MessageConsumer | TopicSubscriber | QueueReceiver, QueueBrowser |
С использованием механизма аннотаций соответствующий код будет иметь следующий вид:
@>Resource(mappedName="jms/ConnectionFactory") private static ConnectionFactory connectionFactory;
Указанный фрагмент кода извлекает ресурс с JNDI-именем jms/ConnectionFactory и связывает его с переменной connectionFactory.
Естественно, что перед первым применением фабрика объектов ConnectionFactory должна быть создана командой:
asadmin.bat create-jms-resource --user admin --passwordfile admin-password.txt --host localhost --port 4848 --restype javax.jms.ConnectionFactory --enabled=true jms/ConnectionFactory
, которая создает ресурс типа javax.jms.ConnectionFactory с именем jms/ConnectionFactory.
Следующим шагом является создание соединения с JMS -провайдером. Соответствующий код будет выглядеть следующим образом:
Connection connection = connectionFactory.createConnection();
Следует отметить, что соединение должно быть закрыто методом connection.close() после того, как оно перестает использоваться.
После того как соединение создано, могут быть созданы виртуальные каналы, в рамках которых будет осуществляться передача сообщений. Существуют два типа таких каналов: очередь (queue) и тема (topic). Создание виртуального канала требует наличия соответствующего пункта назначения (destination), созданного в JNDI. Таким образом, прежде чем программа сможет задействовать очередь или тему, соответствующий объект должен быть создан в JMS - провайдере. Для Sun Application Server соответствующая команда будет иметь вид:
asadmin.bat create-jms-resource --user admin --passwordfile admin-password.txt --host localhost --port 4848 --restype javax.jms.Queue --enabled=true --property Name=PhysicalQueue jms/Queue
, где restype указывает тип объекта (в данном случае это javax.jms.Queue),а параметр property задает JNDI-имя объекта (jms/Queue).
Для того, чтобы в программе получить ссылку на соответствующий объект, можно использовать код вида:
@>Resource(mappedName="jms/Queue") private static Queue queue;
для очереди, или, если нужно получить ссылку на объект типа тема:
@>Resource(mappedName="jms/Topic") private static Topic topic;
Еще раз обратим внимание на то, что объекты с именами jms/Queue или jms/Topic должны быть предварительно созданы.