- •ЛабораторнаЯ рАбота 4
- •ЛабораторнаЯ рАбота 5 Создание корпоративной информационной системы на основе Java jms
- •1 Теоретическое введение
- •1.1 Архитектура jms-базированной кис
- •1.2 Сообщения
- •1.3 Модели передачи сообщений
- •1.4 Схема применения jms
- •Пример №1. Создания jms-базированной кис,
- •Часть 1.
- •Часть 2.
- •Часть 3.
- •Часть 4 (факультативно).
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 = connection.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 вернет все сообщения в соответствующее исходное расположение.
Второй подход заключается в использовании JTA3-транзакций совместно с 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.
