У вас есть приложение, которое отправляет сообщения в очередь сообщений IBM WebSphere? И приложение-получатель основано на JMS (служба сообщений Java), а приложение-отправитель является традиционным приложением WebSphere MQ, отличным от JMS (т. е. родным).

В этом случае вы можете столкнуться с той же проблемой, что и я. В частности, приложение JMS требует наличия заголовка MQRFH2. Этот заголовок обычно просто содержит лишнюю информацию, но, по-видимому, это требование для приложений JMS. Возможно, можно настроить приложение JMS так, чтобы оно игнорировало этот заголовок, однако у меня нет никакой информации об этом.

Я провел несколько дней, изучая в Интернете и тестируя различные методы, и вообще пробовал все, пока не получил то, что теперь работает. Ниже я приведу вам самые важные факты.

Структура заголовка RFH

Эта статья очень полезна для объяснения того, как структурировано сообщение MQ в случае приложений JMS:

Это также объясняет формат заголовка MQRFH2. Формат состоит из фиксированной части (длиной 36 байт), в которой есть набор байтов для указания общей длины, используемой кодировки, наборов символов и т. д. Полную разбивку этой фиксированной части см. в статье выше.

Пример фиксированной части заголовка

StrucId (MQCHAR4) Идентификатор структуры. Должно быть MQRFH_STRUC_ID (значение: «RFH») (начальное значение).
Версия (MQLONG) Номер версии структуры. Должно быть MQRFH_VERSION_2 (значение: 2) (начальное значение).
StrucLength (MQLONG) Общая длина MQRFH2, включая поля NameValueData.
Encoding (MQLONG) Кодирование данных.
CodedCharSetId (MQLONG) Идентификатор набора закодированных символов.
Формат (MQCHAR8) Имя формата.
Флаги (MQLONG) Флаги. MQRFH_NO_FLAGS = 0. Флаги не установлены.
NameValueCCSID (MQLONG)

Вторая часть заголовка — это переменная часть, которая обычно состоит из нескольких папок XML, которые вы можете заполнить некоторыми пользовательскими данными (раздел usr) или специфическими данными jms (раздел jms).

Пример переменной части заголовка

‹mcd›‹Msd›jms_text‹/Msd›‹/mcd›‹jms›‹Dst›queue:///APPS/BOB/TEST‹/Dst›‹Tms›1297042644307‹/Tms›‹Dlv›2‹/Dlv ›‹/jms›‹usr›‹msg_id›445566‹/msg_id›‹receiver_id›TestReceiver‹/receiver_id›‹/usr›

Пример кода для создания заголовков JMS

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

Ребята, с которыми мы работали, которые разработали JMS-приложение, которое собирало наши сообщения и которому требовался JMS-заголовок, прислали нам пример кода, чтобы показать, как они создают сообщения с заголовками. Обратите внимание, что это код Java, который использует библиотеки JMS. Нет фактического кода для создания заголовка, это делается автоматически, просто с помощью JMS API.

Раньше на сайте IBM было много образцов для MQ, однако, поскольку они настолько старые, что их убрали и больше не выпускают. Однако кто-то любезно все скачал и разместил на своем сайте:

Самый интересный, с нашей точки зрения, это: Jsmqput.cЭто файл с исходным кодом C (очень сложно читать, если вы не привыкли к C или C++…), но он показывает общий механизм объединения сообщения с заголовком RFH. Одна вещь, которую я заметил в этом, заключается в том, что они дополняют каждую папку в переменной части сообщения до числа, кратного четырем. В конце концов, моя версия дополняет только всю переменную часть, а не каждый раздел. Другая интересная вещь, которую я усвоил из этого примера, заключается в том, что каждому разделу в переменной части заголовка предшествует длина, это не было очевидно из другой документации, которую я читал.

Пример кода для генерации заголовка:

Эти примеры были полезны с точки зрения сравнения, однако мне все же пришлось придумать способ сделать то же самое в .NET, в среде, которую я использую.

Вариант А — разумный способ — использовать интерфейс IBM XMS

Этот вариант, вероятно, является правильным, однако вам необходимо выполнить обновление до клиентских библиотек MQ версии 7 (если вы еще этого не сделали), а затем установить клиентскую библиотеку IBM Message Service (XMS). Затем добавляются некоторые новые библиотеки DLL, на которые вы можете ссылаться из своего кода (в частности, IBM.XMS.dll), и это обеспечивает оболочку для JMS API, которую можно использовать в среде .NET.

Вот откуда вы можете скачать эту библиотеку:

Есть также примеры программ, которые поставляются с этим, чтобы проиллюстрировать его использование, например, вот некоторый код:

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

К сожалению, мне было запрещено обновлять нашу систему до версии 7, мы все еще использовали версию 6 клиентских библиотек, поэтому мне пришлось перейти к следующему лучшему варианту…

Вариант Б — Безумный способ — Создайте свой собственный заголовок MQRFH2

Если вы используете клиентские библиотеки MQ версии 6, вы ограничены этой опцией, которая заключается в том, чтобы вручную создать заголовок и вставить его в сообщение при его отправке. Это подход, который я использовал, и было трудно заставить его работать, но это означает, что не нужно ничего обновлять. Вы можете использовать эту опцию для любой версии клиентских библиотек.

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

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

Я добавил еще одну строку перед этой WriteString, где я отправляю MQMessage в новый класс RfhHeaderProvider (внедренный в вышеупомянутый класс с использованием нашей структуры внедрения зависимостей, являющейся StructureMap).

Тогда код выглядит примерно так:

Класс поставщика заголовков — это то место, где находится суть логики. Мы загружаем сведения о заголовке RFH из файла конфигурации, включая переключатель для его включения/выключения и xml для трех папок, которые мы включаем в переменную часть сообщения. Взгляните на код ниже:

открытый класс RfhHeaderProvider : IHeaderProvider
{
Поля #region

закрытый только для чтения IRfhConfigurationProvider _rfhHeaderDetailsConfiguration;

#endregion Поля

#region Конструкторы

/// ‹summary›
/// Инициализирует новый экземпляр класса ‹see cref="RfhHeaderProvider"/›.
/// ‹/summary›
/// ‹param name="rfhHeaderDetailsConfiguration"› Детальная конфигурация заголовка RFH. ‹/param›
public RfhHeaderProvider(IRfhConfigurationProvider rfhHeaderDetailsConfiguration)
{
_rfhHeaderDetailsConfiguration = rfhHeaderDetailsConfiguration;
}

#endregion Конструкторы

/// ‹summary›
/// Вводит необязательный заголовок RFH, настройки для которого загружаются через файл конфигурации.
/// Этот заголовок обычно используется только приложениями JMS, т.е. Java Websphere Приложения MQ, использующие API JMS.
/// поскольку извлекающая часть системы написана HP с использованием JMS, им нужен этот заголовок, чтобы их конец
/// мог обрабатывать сообщение без изменения своего кода
/// ИНФОРМАЦИЯ В шапке RFH
/// http://middleware.its.state.nc.us/middleware/Documentation/en_US/htm/csqzaw09/csqzaw0937.htm
/// Существует альтернатива созданию этого заголовка вручную, а именно использование следующей библиотеки:
/// http://www-01.ibm.com/support/docview.wss?rs= 171&uid=swg24011756&loc=en_US&cs=utf-8&lang=en.
/// Однако для этого требуется установить этот пакет и клиентские библиотеки MQ v7.
/// Структура заголовка также определена здесь:
/// http://publib.boulder.ibm.com/infocenter/wmbhelp/v6r1m0/index.jsp?topic=/ com.ibm.etools.mft.doc/aq06930_.htm
/// Если вы хотите изменить эту структуру и проверить, действительно ли сообщение, я рекомендую RfhUtils.exe
/// Это — это инструмент, который можно запустить на сервере для просмотра сообщений в MQ, чтобы просмотреть их данные и свойства заголовка RFH:
/// http://sites.google.com/site/ibmmiddleware/rfhutils< br /> /// ‹/summary›
/// ‹param name="mqMsg"› Сообщение. ‹/param›
public void WriteHeader(MQMessage mqMsg)
{
// ЗАГОЛОВОК НЕ ВКЛЮЧЕН, ПОЭТОМУ НИЧЕГО НЕ ДЕЛАТЬ
if (_rfhHeaderDetailsConfiguration.HeaderDetails.IsEnabled == false ) возвращение;

кодировка var = новая System.Text.UTF8Encoding();

// СТАРЫЕ ЗАКОДИРОВАННЫЕ СТРОКИ
//var mcdHeader = @”‹mcd›‹Msd›jms_text‹/Msd›‹/mcd›”;
//var jmsHeader = @”‹jms›‹Dst ›queue:///APPS/OOT/REQUEST‹/Dst›‹Tms›1297042644307‹/Tms›‹Dlv›2‹/Dlv›‹/jms›”;
//var usrHeader = @”‹usr ›‹msg_id›1122‹/msg_id›‹receiver_id›TestReceiver‹/receiver_id›‹/usr›”;

// ЗАГРУЖАЕМЫЕ СТРОКИ НОВОЙ ДИНАМИЧЕСКОЙ КОНФИГУРАЦИИ
var mcdHeader = _rfhHeaderDetailsConfiguration.HeaderDetails.McdHeader;
var jmsHeader = _rfhHeaderDetailsConfiguration.HeaderDetails.JmsHeader;
var usrHeader = _rfhHeaderDetailsConfiguration.UaderDetails;

var rfhHeader = mcdHeader + jmsHeader + usrHeader;

while (encoding.GetByteCount(rfhHeader) % 4 != 0)
{ rfhHeader = rfhHeader + " "; // добавляем здесь пробел для отслеживания общей длины (на самом деле ничего не делаем с этим)
usrHeader = usrHeader + " "; // последний элемент в разделе "имя-значение" получает лишние пробелы
}
var rfhHeaderLen = encoding.GetByteCount(rfhHeader);

Int32 iStrucLength = MQC.MQRFH_STRUC_LENGTH_FIXED_2 + rfhHeaderLen + 12; // 3 длины заголовка * 4 байта

// ЗАГОЛОВОК RFH
mqMsg.Format = MQC.MQFMT_RF_HEADER_2; // Формат сообщения
mqMsg.WriteBytes(MQC.MQRFH_STRUC_ID); // Идентификатор структуры
mqMsg.WriteInt4(MQC.MQRFH_VERSION_2); // Номер версии структуры
mqMsg.WriteInt4(iStrucLength); // Общая длина MQRFH2, включая NameValueData
mqMsg.WriteInt4(MQC.MQENC_NATIVE); // Числовое кодирование данных, следующих за NameValueData
mqMsg.WriteInt4(MQC.MQCCSI_DEFAULT); // Идентификатор набора символов данных, следующих за NameValueData
mqMsg.WriteBytes(MQC.MQFMT_STRING); // Формат имени данных, следующих за NameValueData
mqMsg.WriteInt4(MQC.MQRFH_NO_FLAGS); // Флаги
mqMsg.WriteInt4(1208); // Идентификатор набора символов NameValueData, 1208 = UTF-8

// Необязательные папки NameValueData и их содержимое должны встречаться в последовательности (длина, данные)

mqMsg.WriteInt4(encoding.GetByteCount(mcdHeader));
mqMsg.WriteBytes(mcdHeader);

mqMsg.WriteInt4(encoding.GetByteCount(jmsHeader));
mqMsg.WriteBytes(jmsHeader);

mqMsg.WriteInt4(encoding.GetByteCount(usrHeader));
mqMsg.WriteBytes(usrHeader);
}
}

Первая часть метода WriteHeader дополняет переменную часть заголовка до числа, кратного четырем. Обратите внимание, что я могу сделать этот код немного более эффективным (т.е. не нужно добавлять пробелы к переменной rfhHeader, так как я все равно ее не использую, вместо этого мог бы вести счет там), однако я оставил его как есть, так как теперь я хочу напишите что-нибудь об этом коде, прежде чем я начну его дальнейшую оптимизацию. После заполнения переменной части нам нужно определить длину структуры. Это очень важно для правильной длины, иначе вы получите сообщение об ошибке при попытке извлечь сообщение из очереди.

Длина структуры состоит из длины фиксированной части заголовка, плюс длина дополненной переменной части заголовка, плюс 4 (байта) для каждой длины заголовка, которую мы вставляем перед каждой папкой.

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

Последняя часть метода записывает переменную часть заголовка. Каждая папка xml (я использую три, т.е. ‹mcd›, ‹jms› и ‹usr›) должна начинаться с ее длины.

После того, как вы реализовали код, как указано выше, вы также сможете написать свой собственный заголовок RFH в свое сообщение MQ для использования приложениями JMS!

Инструменты для анализа и отладки заголовка RFH

Существует полезный (хотя и довольно сложный для понимания) инструмент для получения сообщений WebSphere MQ. Это приложение RfhUtils.exe, которое доступно здесь:

Полезная статья о том, как его использовать, находится здесь:

Лучший способ начать это дело — написать несколько строк в cmd-файл в соответствии с инструкциями. (т.е. где вы устанавливаете необходимые пути и передаете IP-адрес и т. д.).

После того, как вы запустите приложение, оно будет выглядеть примерно так:

Варианты, которые я использовал чаще всего, были «Обзор Q», это покажет вам любые сообщения в очереди, и, в частности, есть ли какие-либо ошибки или нет. Если ошибок нет, сообщение будет загружено, и вы сможете увидеть разбивку данных (т.е. размер данных) тела по сравнению с заголовком RFH. Затем вы также можете переключиться на вкладку RFH, чтобы просмотреть сведения о заголовке RFH, а также на вкладки jms и usr, чтобы просмотреть содержимое эти xml-папки.

Существует опция под названием «Очистить Q», которая полезна, если вы хотите очистить сообщения, прежде чем поместить другое, или вы можете нажать «Начать просмотр», который проведет вас через все сообщения, если у вас есть более одного и вы хотите перейти через них всех.

Надеюсь, это поможет, и дайте мне знать, если я что-то пропустил?

Первоначально опубликовано на http://fmair.blogspot.com. (21 февраля 2011 г.)