Взаимодействие с IBM WebSphere MQ через JMS API

💡 Усі статті, обговорення, новини про Java — в одному місці. Приєднуйтесь до Java спільноти!

Многим известен продукт IBM WebSphere MQ, предназначенный для обмена сообщениями между приложениями и обеспечивающий их гарантированную доставку. В данной статье я хотел бы поделиться личным опытом интеграции прикладного Java-ПО с этим продуктом. Я постараюсь не вдаваться в описание самого продукта (который является одним из лидеров в своей нише), т.к. в интернете масса ресурсов (в т.ч. и русскоязычных) по этой теме, которыми я нередко пользовался сам. Также, не буду глубоко описывать принципы применяемых J2EE-технологий. Вместо этого расскажу, как их использовать на конкретном примере.

Задача: обеспечить интеграцию программного обеспечения на платформе J2EE с IBM WebSphere MQ, для межсистемного взаимодействия в гетерогенной среде. Схема следующая:

  • Есть наш софт — 1 шт.
  • Есть софт, с которым нам надо взаимодействовать (внешние системы) — N шт.
  • Для информационного обмена с каждой из внешних систем, на нашей стороне создаётся одна входящая очередь и одна исходящая очередь (стандартная схема асинхронного взаимодействия).

В J2EE существует API JMS (Java Message Service), описывающий технологию асинхронного обмена сообщений. Как и многие другие технологии J2EE, JMS является просто набором интерфейсов, реализуемым поставщиками конкретных продуктов. Для чего нужен API, не заполненный ни единой строчкой кода его реализации? Для того, чтобы обеспечить независимость программного обеспечения от поставщика услуг (в данном случае услуг доставки сообщений), и возможность его смены без изменения исходного кода ПО. IBM WebSphere MQ в числе прочего, предоставляет также и реализацию JMS API. Важно: при установке под Windows следует указать поддержку Java, по умолчанию она отключена.

Чтобы было с чем работать, сначала создаём объекты менеджера MQ. Кто не знает, что это и зачем нужно может сходить, например, сюда, либо поискать любые другие ресурсы. Советую вначале разобраться с объектами MQ, т.к. без этого моя статья будет трудна для понимания (см. выше — описываю не сам продукт, а его интеграцию с J2EE). В результате создаётся несколько объектов, из которых нас как программистов интересуют: менеджер MQ, входящая(-ие) очередь(-и), исходящая(-ие) очередь(-и). Для того, чтобы использовать стандартный API взаимодействия с WebSphere MQ — MQI, этого достаточно. Но мы используем технологии J2EE, поэтому работа администратора MQ продолжается.

Небольшое отступление. Обычно, технология JMS работает в тесной связке с технологией JNDI (Java Naming and Directory Interface). Грубо говоря, JNDI представляет собой API хранилища объектов, реализуемый различными поставщиками (самый распространённый и доступный поставщик услуг хранения — файловая система вашей операционной системы). Как это работает. JMS API описывает работу с объектами очередей и фабрик подключений к ним (в терминологии WebSphere MQ «фабрикой подключений» является «менеджер очередей»). Вопрос — как нам получить работающие объекты, если JMS содержит только интерфейсы? Ответ — считать их из JNDI-хранилища. Но «прежде, чем купить что-нибудь хорошее, надо сначала продать что-нибудь хорошее», т.е. прежде, чем считать объекты из JNDI-хранилища, следует их туда сначала сохранить. Посему возвращаемся из небольшого отступления обратно.

Итак, продолжаем администрировать MQ. Мой опыт основан на работе с сервером WebSphere MQ установленным на платформе Windows, к тому же, я программист, а не администратор, посему профессиональных админов (а особенно -иксоидов) прошу сильно не ругаться. Нам требуется сохранить данные о ранее созданных объектах MQ в каком-либо JNDI-хранилище, чтобы потом обратиться к ним из нашего софта. Для этого с MQ поставляется специальная утилита, которую требуется перед началом использования сконфигурировать под нашего провайдера JNDI API. Заходим в «Program Files\IBM\WebSphere MQ\Java\bin» (при установке по умолчанию), и открываем на редактирование конфигурационный файл «JMSAdmin.config». Я решил использовать того самого общедоступного поставщика услуг JNDI — файловую систему ОС Windows. Посему, указываем следующие значения свойств в конфигурационном файле:

INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
PROVIDER_URL=file:/C:/JNDI-Directory
SECURITY_AUTHENTICATION=none

Где:

  • INITIAL_CONTEXT_FACTORY — поставщик услуг, лежит в «fscontext.jar», в папке «Program Files\IBM\WebSphere MQ\Java\lib». Если библиотеки там нет, найдите её среди ваших (или не ваших) J2EE библиотек и положите туда.
  • PROVIDER_URL — путь к хранилищу наших объектов. Надо создать пустую папку, в которой впоследствии будет автоматически создан файл с именем «.bindings», содержащий наши объекты в виде пар свойство=значение. Обратите внимание, что файл не имеет имени, только лишь расширение.

В «JMSAdmin.config» есть ещё несколько свойств, их описание находятся в комментариях там же. Нам они не нужны, и должны быть закомментированы символом «#». Следует внимательно проверить, чтобы все свойства, кроме трёх вышеуказанных, были скрыты в комментариях (не относится к опытным админам, знающим, что делать с этими свойствами).

Теперь мы готовы заняться непосредственно созданием объектов JMS в JNDI-хранилище, что и делаем, запустив «JMSAdmin.bat» из папки «Program Files\IBM\WebSphere MQ\Java\bin». Появится командное окно с приглашением вида «InitCtx>», в которое следует вводить специфические команды WebSphere MQ. После определенного количества экспериментов и наступаний на непонятные грабли, я пришёл к нижеописанным форматам команд.

Создание менеджера в JNDI-хранилище:

def qcf(имя) qmgr(имя) host(адрес) port(порт) ccs(кодировка) tran(CLIENT)

где:

qcf — имя фабрики подключений JMS. Это имя будет использоваться в софте.

qmgr — имя установленного менеджера WebSphere MQ,

host — сетевой адрес менеджера WebSphere MQ,

port — порт службы Listener менеджера WebSphere MQ, по умолчанию 1414,

ccs — кодировка, значение этого параметра должно соответствовать установленному для менеджера WebSphere MQ,

tran — тип связи, установить значение CLIENT

Чтобы не путаться, лучше указывать одно и то же значение для qcf и qmgr. Обычно создаётся только один менеджер.

Создание очередей в JNDI-хранилище:

(выполнить команду для каждой очереди, используемой в софте)

def q(имя) qmgr(имя) qu(имя) tc(MQ)

q — имя очереди сообщений в JNDI-хранилище Это имя будет использоваться в софте.

qu — имя очереди сообщений менеджера WebSphere MQ,

qmgr — имя установленного менеджера WebSphere MQ,

tc — тип клиента. Если ваши исходящие сообщения будут приниматься посредством обращения к очереди через JMS, указывайте значение (JMS). При этом в заголовке сообщения будут передаваться специфические для JMS данные. Если софт, принимающий ваши сообщения, работает не через JMS, то в начале всех сообщений будет видеть мусор. При указании значения (MQ) никаких дополнений к заголовку не будет, при этом сообщение будет корректно приниматься любыми платформами, в т.ч. и JMS. Я предпочитаю указывать значение (MQ) дабы в дальнейшем не задумываться о том, как будет приниматься сообщение на другой стороне.

Чтобы не путаться, лучше указывать одно и то же значение для q и qu.

Подсказка новичкам: многие поначалу путаются в типах очередей, поэтому знайте, что обычно софт обращается к двум очередям: входящей (тип Local Queue) и исходящей (в случае межсерверного взаимодействия — тип Remote Queue Definition; в случае обмена сообщениями через один сервер MQ — тип так же Local Queue). Никогда не пытайтесь обращаться к Local Queue с атрибутом Usage установленным как Transmission (используется в межсерверном взаимодействии).

На этом этапе задача администрирования MQ выполнена. Созданное JNDI-хранилище можно перенести в любое удобное место (например, на машину, с которой запускается ваш софт) путем простого копирования папки и её содержимого. Библиотеки MQ, подключенные к вашему проекту, обеспечат соединение с сервером, находящимся по указанному в первой команде (def qcf) сетевому адресу с любой машины, имеющей доступ к этому адресу.

Архитектура такова: софт должен знать только о настройках JNDI-хранилища (его местоположению) и иметь к нему доступ. Данные, которые мы вводили на этапе администрирования (а также автоматически добавляла в хранилище утилита JMSAdmin), в софте не нужны (за исключением имён объектов).

Двигаемся далее: непосредственно разработка ПО. На данном этапе у нас уже имеется установленный и настроенный сервер IBM WebSphere MQ, и JNDI-хранилище с JMS-объектами. Нам требуется:

  1. Объявить у себя в коде поля JMS-объектов, соответствующие интерфейсам JMS;
  2. Создать экземпляры JMS-объектов, считав их из JNDI-хранилища;
  3. При помощи созданных объектов соединиться с менеджером MQ, и начать непосредственно асинхронный информационный обмен.

1. Создаём проект и подключаем к нему библиотеки: jms.jar, fscontext.jar, com.ibm.mq.jar, com.ibm.mqjms.jar (первые две должны быть в наличие у J2EE-разработчика, последние две берём из «Program Files\IBM\WebSphere MQ\Java\lib»). Создаём некий класс и объявляем в нем следующие поля (из расчёта на информационный обмен с одной внешней системой):

javax.naming.InitialContext ctx;
javax.jms.QueueConnectionFactory QCF;
javax.jms.QueueConnection conn;
javax.jms.QueueSession session;
javax.jms.Queue queue_out;
javax.jms.QueueSender sender;
javax.jms.Queue queue_in;
javax.jms.QueueReceiver receiver;

2. Считывание JMS-объектов:

// свойства соединения с JNDI-хранилищем, настраиваются также, как и для JMSAdmin
java.util.Hashtable env = new java.util.Hashtable();

// INITIAL_CONTEXT_FACTORY указать тот же, что и соответствующее 
// свойство в «JMSAdmin.config»
env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");

// provider_url указать тот же, что и соответствующее свойство в «JMSAdmin.config» (в 
// нашем примере – «file:/C:/JNDI-Directory»).
// Если JNDI-хранилище переносилось с сервера с MQ на машину с вашим ПО, указать 
// путь к папке, в которой был сохранён файл с именем «.bindings».
env.put(javax.naming.Context.PROVIDER_URL, provider_url);

// соединяемся с JNDI-хранилищем
ctx = new InitialContext(env);

// ищем в JNDI-хранилище требуемые нам объекты

// ищем менеджер подключений
// connection_factory_name указать то же, что и имя в команде «def qcf(имя)» (см. выше)
QCF = (QueueConnectionFactory) ctx.lookup(connection_factory_name);

// ищем очереди
// queue_name_out и queue_name_in указать те же, 
// что и имя в командах «def q(имя)» (см. выше)
queue_out = (Queue) ctx.lookup(queue_name_out);
queue_in = (Queue) ctx.lookup(queue_name_in);

// больше нам JNDI-хранилище не нужно, отключаемся
ctx.close();

// отправка текстовых сообщений (есть и другие типы сообщений, их изучаем самостоятельно)
// в гетерогенной среде наиболее универсальным форматом является текстовое сообщение,
// с содержимым с XML-разметкой.
// Совет: если в сообщении используются символы, отличающиеся от латиницы,
// надёжнее передавать их в виде кодов
// (например «У» представляет символ кириллицы «У»), во избежание
// возможных проблем с перекодировкой при передаче через различные ОС, или 
// даже через одинаковые ОС, установленные с различной кодировкой.
// IBM WebSphere MQ имеет встроенные средства перекодирования, но в них учтены не
// все возможные варианты.

// соединяемся
conn = QCF.createQueueConnection();
conn.start();
session = conn.createQueueSession(true, QueueSession.AUTO_ACKNOWLEDGE);
sender = session.createSender(queue_out);

// отправляем сообщение
// text - строка с вашим сообщением. Размер ограничен возможностями MQ и 
// настройками очереди
javax.jms.TextMessage message = session.createTextMessage( text);
sender.send(message);
session.commit();

// получение текстовых сообщений, простой вариант
receiver = session.createReceiver(queue_in);
// wait – период ожидания поступления сообщения в очередь, 
// пока нет сообщений, поток будет заблокирован в этой точке,
// на указанный период
javax.jms.Message message = receiver.receive( wait);
System.out.println(((TextMessage)message).getText());

// Более интересный и «продвинутый» вариант:
// создаётся так называемый «слушатель сообщений» и регистрируется
// на входящей очереди. После регистрации слушателя, приложение
// уже не должно беспокоиться о проверке наличия сообщений во входящей
// очереди – при их поступлении MQ автоматически вызовет метод
// onMessage() зарегистрированного слушателя, и передаст в качестве
// параметра поступившее сообщение.
// Во время тестирования не забудьте – если программа завершилась,
// передавать сообщение уже некому, поэтому на данном этапе, после
// регистрации слушателя можно поставить либо бесконечный цикл,
// либо что-то вроде Thread.sleep(100000).
javax.jms.MessageListener listener = new MessageListener() {
    public void onMessage(javax.jms.Message message) {
       if (message instanceof javax.jms.TextMessage) {
           System.out.println(((TextMessage)message).getText());
       }
    }
};
receiver.setMessageListener(listener);
// Thread.sleep(100000)

session.close();
conn.close();

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

На этом, собственно, и всё, что хотелось рассказать об основах взаимодействия с IBM WebSphere MQ через JMS API.

Список литературы и полезных ссылок:


Владимир Смирнов

АО «New Age Technologies»

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn



13 коментарів

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

Статья полезна начинающему разработчику по MQ. Похожая статья есть тут http://www.jpage.ru/blog/? p=18

Отличная статья, особенно для новичков, осваивающих WebSphere Aplication Server и/или WebSphere MQ

В предложенной мной схеме каждая пара входящая/исходящая очередь сопоставлена с одним участником. Получатель знает, что на запрос из очереди «вход1», ответ следует ложить в очередь «исход1»

Дело в том что сервер 1 работает с прикладами и другими ведомствами, сервер2 тоже у него свой документооброт (документооброт в одинаковой платформе) со своими ведомствами, но предположим в сервер2 который адресован на те ведомства которые крутятся на сервере1 MQ определяет по ID участника и ложет в его очередь, но тут проблема, в один конец можно отправить, а там обработанный док должен же обратно прити вот как это сделать чтобы он определил куда его отправлять вот это озадачивает...

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

Довольно сумбурно, похоже Жасу стоит поближе ознакомиться с продуктом и его возможностями... Сам по себе продукт представляет собой сервер управления очередями сообщений. При этом возможности продукта достаточно широки для того, чтобы можно было построить на его основе полноценную систему гарантированной доставки сообщений (это больше, чем управление очередями). Обычно подобная система включает несколько самостоятельных удалённых друг от друга серверов, распределённых в гетерогенной среде (разное железо, разные ОС и т.д.). Если всё же прочитать статью по вышеуказанной ссылке (сюда), то можно найти там информацию для размышлений по поводу настройки взаимодействия двух серверов MQ (

как можно связать две MQ расположенных в разных городах

). Насчёт админить — следует определиться что под этим подразумевается. У продукта есть стандартные средства администрирования. Есть возможность настройки консоли администратора на управление удалённым сервером MQ. Насчёт перво-второстепенности — обычно установленные сервера MQ равноценны, но взаимодействие между ними может быть настроено по разному (опять же читаем вышеуказанную статью и другие главы курса, к которому она относится). Я строил следующую схему взаимодействия: — на сервере 1 есть исходящая очередь и канал-отправитель. Канал-отправитель настроен на связь с удалённым сервером 2 через его канал-получатель. При поступлении сообщения в исходящую очередь, оно автоматически передаётся на сервер 2 каналом-отправителем, и ложится в созданную там входящую очередь — на сервере 1 есть входящая очередь и канал-получатель. При поступлении сообщений в канал-получатель на сервере 1 от канала-отправителя на сервере 2, оно сохраняется в эту входящую очередь и становится доступным вашему ПО для получения и обработки — на сервере 2 ситуация точно такая же. — итого схема передачи сообщения (его путь): ваш софт (здесь) -> исходящая очередь (здесь) -> канал-отправитель (здесь) -> канал-получатель (там) -> входящая очередь (там) -> ваш (либо чужой) софт (там). В обратную сторону такая же схема. Всего на каждом сервере есть входящая и исходящая очередь, канал-отправитель и канал-получатель. Это для двух серверов; если требуется обмениваться сообщениями с большим количеством, для каждого добавляемого сервера добавляется полных комплект вышеуказанных объектов. Совет: прочитайте статьи, поставьте на двух рядом стоящих машинах два сервера и настройте их взаимодействие. Настройка удалённо находящихся серверов выполняется аналогично.

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

Спасибо за статью. Пригодилась перед началом работы с MQ.) Для тех кто интересуется, очень хорошо, по-моему, и обобщенно о JMS написано на www.ibm.com/...​s/ru/edu/j-jms/index.html

ну может зато эта статья, опубликована здесь заставит их задуматься об этом.

Одно из часто применяемых программистом орудий является Google, который и приведёт к этой статье заинтересованных в этой теме. Вообще, насколько мне известно, чаще ищут тему и попадают на сайт, а не наоборот. Надеюсь, данная статья внесёт свой вклад в популяризацию сайта;)

Информация полезная, но очень специфичная.Думаю ей место на www.ibm.com/developerworks/.А то 90% наших J2EE разработчиков про WebSphere MQ никогда и не слышали.

ну может зато эта статья, опубликована здесь заставит их задуматься об этом.

to -=fatboy=-Причем здесь бэкэнд реализация JMS к технологиям Web Services и ESB?

Информация полезная, но очень специфичная. Думаю ей место на www.ibm.com/developerworks/.А то 90% наших J2EE разработчиков про WebSphere MQ никогда и не слышали.

to -=fatboy=-Спасибо за комментарий, согласен с Вами. Как я уже упоминал, JMS — это интерфейс, и наполнить его можно любой реализацией, даже реализацией с помощью web-сервисов, несмотря на то, что они по сути не являются асинхронными. Причём во многих случаях, можно сменить реализацию службы доставки сообщений, не меняя исходный код ПО. Например, у меня получалось перейти с реализации встроенной в сервер приложений JBoss на реализацию Sun ONE Message Queue, путём только изменения настроек ПО (имена объектов, путь к JNDI-хранилищу...)

Статья интересная. Немного добавлю с Вашего позволения.JMS используется не только с IBM WebSphere MQ, с другой стороны есть другое решение WS (web services) или ESB (enterprice service bus).

Підписатись на коментарі