Spring (Spring Framework) - open source фреймворк для разработки приложений на Java.
IBM Websphere MQ (MQSeries) - коммерческий продукт всем известной компании IBM для обмена сообщениями.
Данный текст в основном предназначен для разработчиков приложений на базе Spring и интересующимися асинхронными средствами обмена.
Будем рассматривать отправку и прием сообщений из Websphere MQ средствами Spring-Jms.
1. Создадим пустой maven проект spring-mq. Структура проекта будет следующей:
spring-mq\src\
spring-mq\src\main\
spring-mq\src\main\java\
spring-mq\src\main\resources\
spring-mq\src\test\
spring-mq\src\test\java\
spring-mq\src\test\resources\
spring-mq\pom.xml
2. В файле pom.xml вы должны прописать зависимости к проектам spring-context, spring-jms, spring-test версий 2.5.5, jms 1.1, junit 4.4 и к библиотекам ibm mqjms и dhbcore версии, соответствующей версии установленной у вас Websphere MQ (соответствие необязательно, если вы будете использовать клиентские соединения, см. далее).
Так как java библиотеки для MQ не выложены в репозиториях maven, вам это придется сделать самим, выложив их в свой локальный репозиторий. Библиотеки для MQ 5 и 6, сконфигурированные для maven можно здесь (4,3Мб) и распаковать их в каталог $maven_repository/com/ibm вашего репозитория .
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.smogg.samples</groupId>
<artifactId>spring-mq</artifactId>
<version>1.0</version>
<name>Spring with Websphere MQ sample</name>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.5</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>logkit</groupId>
<artifactId>logkit</artifactId>
</exclusion>
<exclusion>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>2.5.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm.mqjms</artifactId>
<version>6.0.2.5</version>
<exclusions>
<exclusion>
<groupId>javax.naming</groupId>
<artifactId>jndi</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.ibm</groupId>
<artifactId>com.ibm.dhbcore</artifactId>
<version>6.0.2.5</version>
</dependency>
</dependencies>
</project>
3. Отправка сообщений. Создадим класс JMSSender в папке main/java пакете com.smogg.samples.springmq, который будет отправлять сообщения посредством JMS. Код практически аналогичен коду, приведенному в документации по Spring-JMS (п 19.3)
package com.smogg.samples.springmq;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class JMSSender {
private JmsTemplate jmsTemplate;
public void sendMesage(final byte[] mess) {
System.out.println("Send message...");
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
BytesMessage byteMess = session.createBytesMessage();
byteMess.writeBytes(mess);
return byteMess;
}
});
}
public JmsTemplate getJmsTemplate() {
return jmsTemplate;
}
@Required
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
}
4. Настроим получателя сообщений посредством xml конфигурации – нужно будет определить ConnectoinFactory и Destination. Создадим файл beans-jms.xml в каталоге test\resources. Определяем ConnectionFactory:
<bean id="jmsFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="queueManager">
<value>TEST</value>
</property>
<property name="transportType" value="0" />
</bean>
TEST – имя локального менеджера очередей. transportType=0, означает что соединение будет в режиме BINDING (подробнее).
Для возможности работы с удаленным менеджером очередей, расположенным на другом компьютере, нужно указать transportType=1 (режим CLIENT) и указать hostName, channel и port:
<bean id="jmsFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName">
<value>127.0.0.1</value>
</property>
<property name="queueManager">
<value>TEST</value>
</property>
<property name="channel">
<value>SVRCONN</value>
</property>
<property name="port">
<value>1415</value>
</property>
<property name="transportType" value="1" />
</bean>
Определяем настройки для Destination:
<bean id="sendDestination" class="com.ibm.mq.jms.MQQueue">
<property name="baseQueueName">
<value>QUEUE_IN</value>
</property>
</bean>
QUEUE_IN – имя очереди на менеджере TEST в которую бедет производиться отправка.
Далее нужно настроить jmsTemplate и jmsSender. jmsTemplate:
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref local="jmsFactory" />
</property>
</bean>
</property>
<property name="defaultDestination">
<ref bean="sendDestination" />
</property>
<property name="receiveTimeout">
<value>30000</value>
</property>
</bean>
jmsSender:
<bean id="jmsSender" class="com.smogg.samples.springmq.JMSSender">
<property name="jmsTemplate">
<ref bean="jmsTemplate" />
</property>
</bean>
Настройка для отправки сообщений завершена
5. Напишем тест для проверки работоспособности. Напишем класс JMSTest в каталоге test/java пакете com.smogg.samples.springmq:
package com.smogg.samples.springmq;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/beans-jms.xml" })
public class JMSTest {
JMSSender jsmSender;
@Autowired
public void setJsmSender(JMSSender jsmSender) {
this.jsmSender = jsmSender;
}
@Test
public void testJMSSend() throws Exception {
String text = "123456";
jsmSender.sendMesage(text.getBytes());
}
}
Если все сконфигурировано правильно, то после запуска теста в указанной очереди появится новое сообщение.
Заметки по тесту:
@RunWith(SpringJUnit4ClassRunner.class) - говорит JUnit, что должен использоваться класс SpringJUnit4ClassRunner для запуска теста.
@ContextConfiguration(locations = { "/beans-jms.xml" }) - устанавливает для SpringJUnit4ClassRunner какие настройки загружать
@Autowired - устанавливает, что Spring должен сам определить объект и установить его значение в класс
6. Прием сообщений. Для приема сообщний в Spring используется подход, аналогичный MDB (message driven bean) из спецификации JEE, Message Driven POJO (MDP).
Это означает то, что вы должны создать класс реализующий методы javax.jms.MessageListener, т.е. в спецификации jms 1.1 это один метод "public void onMessage(Message message);"
Создадим наш класс для чтения сообщений – MessageListener в папке main/java пакете com.smogg.samples.springmq:
package com.smogg.samples.springmq;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
public class MessageListener implements javax.jms.MessageListener {
public void onMessage(Message message) {
if (message instanceof BytesMessage) {
try {
byte[] byteMess = new byte[(int) ((BytesMessage) message).getBodyLength()];
((BytesMessage) message).readBytes(byteMess );
String mess = new String(byteMess );
System.out.println("Received message: "+ mess);
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type ByteMessage");
}
}
7. Настройка MessageListener. Для того, чтобы наш класс начал получать сообщений нужно произвести следующие настройки в beans-jms.xml – создать бин MessageListener, создать jmsContainer. В jmsContainer вы должны укзать сonnectionFactory, destination и ссылку на наш MessageListener. ConnectionFactory мы будем использовать существующую. Настройки:
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="com.smogg.samples.springmq.MessageListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref local="jmsFactory" />
</property>
</bean>
</property>
<property name="destination" ref="sourceDestination" />
<property name="messageListener" ref="messageListener" />
</bean>
<bean id="sourceDestination" class="com.ibm.mq.jms.MQQueue">
<property name="baseQueueName">
<value>QUEUE_IN</value>
</property>
</bean>
Для чтения сообщений мы указали ту же очередь, что и для отправки, чтобы можно было показать как это работает в 1 тесте.
8. Модифицируем наш тест для проверки приема сообщений:
package com.smogg.samples.springmq;
@Test
public void testJMSSend() throws Exception {
String text = "123456";
jsmSender.sendMesage(text.getBytes());
Thread.sleep(3000);
}
Если все настроено правильно, то после запуска теста в логе вы должны увидеть текст:
Send message...
Received message: 123456
9. Модифицируем MessageListener и тест для автоматической проверки работы. В MessageListener добавим 2 поля - счетчик и текст последнего сообщения и будем устанавливать их при приеме:
package com.smogg.samples.springmq;
static String readedMessage;
static int messageCount =0;
public static String getReadedMessage() {
return readedMessage;
}
public void onMessage(Message message) {
if (message instanceof BytesMessage) {
try {
byte[] byteMess = new byte[(int) ((BytesMessage) message).getBodyLength()];
((BytesMessage) message).readBytes(byteMess );
String mess = new String(byteMess );
//only for testing
incMessageCount();
readedMessage = mess;
System.out.println("Received message: "+ mess);
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type BytesMessage");
}
}
private static synchronized void incMessageCount() {
++messageCount;
}
public static int getMessageCount() {
return messageCount;
}
Модифицируем тест:
public void testJMSSend() throws Exception {
String text = "123456";
String sendText = "";
for (int i = 0; i < 10; i++) {
sendText = text + i;
jsmSender.sendMesage(sendText.getBytes());
}
Thread.sleep(3000);
Assert.assertEquals(10, MessageListener.getMessageCount());
Assert.assertEquals(sendText, MessageListener.getReadedMessage());
}
Теперь при запуске теста, если сообщения не будут считываться или отправляться, будет ошибка.
Скачать архив с проектом spring-mq.zip
Успехов всем.
Ссылки:
Дополнительная информация по Spring-jms
Websphere MQ
Читать далее