Статьи

Интеграция Spring с JMS и преобразователями карт


В этой статье я объяснил, как работают встроенные пружинные преобразователи при преобразовании сообщения объекта в сообщение карты.

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

Потребитель заинтересован в объектах Java или таких типах, как простой текст, пары имя-значение или модель json.
Spring Integration предоставляет конечные точки, такие как сервисные активаторы, канальные адаптеры,

Мосты сообщений, шлюзы, трансформаторы, фильтры и маршрутизаторы. В этом примере, как конечная точка Transformers преобразует сообщение объекта в сообщение карты. 

Ссылки:

Высокий уровень просмотра

весна-mockrunner.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
	xmlns:context="http://www.springframework.org/schema/context"
	default-autowire="default"
	xmlns:jms="http://www.springframework.org/schema/jms"
	xsi:schemaLocation="http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">


	  <bean id="destinationManager" class="com.mockrunner.jms.DestinationManager"/>

      <bean id="inBoundQueue" factory-bean="destinationManager" factory-method="createQueue">
      		<constructor-arg index="0" value="MOCKRUNNER-IN-QUEUE" />
      </bean>
      
      <bean id="outBoundQueue" factory-bean="destinationManager" factory-method="createQueue">
      		<constructor-arg index="0" value="MOCKRUNNER-OUT-QUEUE" />
      </bean>
      
      <bean id="configurationManager" class="com.mockrunner.jms.ConfigurationManager"/>
      
      <bean id="jmsQueueConnectionFactory" class="com.mockrunner.mock.jms.MockQueueConnectionFactory">
      		<constructor-arg index="0" ref="destinationManager" />
      		<constructor-arg index="1" ref="configurationManager" />
      </bean>
      
      <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
      	<property name="connectionFactory" ref="jmsQueueConnectionFactory"/>
      </bean>
     
     <jms:listener-container  connection-factory="jmsQueueConnectionFactory" >
			<jms:listener destination="MOCKRUNNER-OUT-QUEUE" ref="mapMessageListener" method="onMessage" />
	  </jms:listener-container>
	  
	  <bean id="mapMessageListener" class="com.spijb.listener.MapMessageListener" />
      
</beans>

В файле spring-mockrunner.xml я определил MockQueue, MockQueueConnectionFactory для входящей очереди и исходящей очереди для быстрого тестирования.
inBoundQueue — это место, где вы будете публиковать сообщение объекта  

Класс ObjectToMapTransformerTest.java.
outBoundQueue, где эта очередь ожидает объект типа MapMessage, и эта очередь перечисляет класс MapMessageListener.java. Для получения дополнительной информации о mockrunner, пожалуйста, ознакомьтесь с моей предыдущей статьей  
Mockrunner с Spring JMS
 .
pom.xml
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.springframework.samples</groupId>
  <artifactId>spring-int-jms-basic</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>3.2.3.RELEASE</spring-framework.version>
		
		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

	</properties>
	
	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-core</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-jmx</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration </groupId>
			<artifactId>spring-integration-jms</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>
		

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

	
		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.mockrunner</groupId>
			<artifactId>mockrunner-jms</artifactId>
			<version>1.0.3</version>
		</dependency>
		<dependency>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
			<version>1.1</version>
		</dependency>
		<dependency>
		    <groupId>org.codehaus.jackson</groupId>
		    <artifactId>jackson-mapper-asl</artifactId>
		    <version>1.9.3</version>
		    <scope>compile</scope>
		</dependency>

	</dependencies>	
</project>
весна-ИНТ-jms.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
	xmlns:int="http://www.springframework.org/schema/integration"
	xmlns:context="http://www.springframework.org/schema/context"
	default-autowire="default"
	xmlns:jms="http://www.springframework.org/schema/jms"
	xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
	xsi:schemaLocation="http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.2.xsd
		http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms-2.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
		
	  <import resource="spring-mockrunner.xml"/> 
      
      <int:poller default="true" fixed-delay="50"/>

      <int:channel id="inputChannel">
      	<int:queue  capacity="5"/>
      </int:channel>
      
       <int:channel id="outputChannel">
      	<int:queue  capacity="5"/>
      </int:channel>

	<int:object-to-map-transformer id="objectToMapTransformer" input-channel="inputChannel" output-channel="outputChannel">
	</int:object-to-map-transformer>      
       
	 <int-jms:inbound-channel-adapter  id="inBoundJMSAdapter"  connection-factory="jmsQueueConnectionFactory"  destination="inBoundQueue" channel="inputChannel">
	 	<int:poller  fixed-rate="1000" />
	 </int-jms:inbound-channel-adapter>
	 
	 <int-jms:outbound-channel-adapter id="outBoundJMSAdapter" channel="outputChannel" connection-factory="jmsQueueConnectionFactory" destination="outBoundQueue">
	 	<int:poller  fixed-rate="1000" />
	 </int-jms:outbound-channel-adapter>
	 

</beans>
Конечная точка настроена для подключения к JMS-серверу, извлечения сообщений и публикации их на локальном канале, т.е. inputChannel. Где как фабрика соединений, так и место назначения, ссылается на компоненты MockQueueConnectionFactory и MockQueue (inBoundQueue) из файла spring-mockrunner.xml.

  • inputChannel и outPutChannel определены как канал очереди
  • objectToMapTransformer: элемент object-to-map-transformer, который получает полезную нагрузку из исходного входного здесь входного канала MOCKRUNNER-IN-QUEUE и отправляет сопряженный объект Map имени-значения в выходной канал, т. е. outputChannel и компонент outBoundJMSAdapter извлекают это сообщение и публикуют его в очередь, т.е. MOCKRUNNER-OUT-QUEUE.
  • inBoundJMSAdapter : bean-компонент inbound-channel-adapter отвечает за получение сообщений от JMS-сервера, здесь он читает из имени ложной очереди MOCKRUNNER-IN-QUEUE, см. класс ObjectToMapTransformerTest.java. 
  • outBoundJMSAdapter : bean-компонент outbound-channel-adapter отвечает за выборку сообщений из канала, т.е. outputChannel, и их публикацию в JMS Queue или Topic. В этом outbounJMSAdapter читает сообщение outputChannel как MapMessage и публикует его в outBoundQueue (MOCKRUNNER-OUT-QUEUE).

MapMessageListener.java

package com.spijb.listener;

import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Session;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.listener.SessionAwareMessageListener;

public class MapMessageListener implements SessionAwareMessageListener<MapMessage> {

   private static final Logger log = LoggerFactory.getLogger(MapMessageListener.class);
   @Override
   public void onMessage(MapMessage message, Session session) throws JMSException {
      
      log.info("Message Received \r\n"+message);
      
   }

}

это простой  класс MapMessageListener для печати полученного сообщения из очереди.

Department.java

package com.spijb.domain;

import java.io.Serializable;

public class Department implements Serializable{

   private static final long serialVersionUID = 1L;
   private final Integer deptno;
   private final String  name;
   private final String  location;
   
   public Department()
   {
      deptno=10;
      name="SALES";
      location="TX";
   }
   
   public Department(Integer dno,String name,String loc)
   {
      this.deptno=dno;
      this.name=name;
      this.location=loc;
   }

   public Integer getDeptno() {
      return deptno;
   }

   public String getName() {
      return name;
   }

   public String getLocation() {
      return location;
   }
   @Override
   public String toString()
   {
      return this.deptno+"-> "+this.name+"->"+this.location;
   }
   
}

Доменный объект для отправки в виде сообщения, по умолчанию конструктору назначают deptno 10, имя как SALES, местоположение как TX также предоставляют конструктор параметров.

Spring JUnit класс ObjectToMapTransformerTest.java 

package com.spijb.invoker;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mockrunner.mock.jms.MockQueue;
import com.spijb.domain.Department;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-mockrunner.xml","classpath:spring-int-jms.xml"})
public class ObjectToMapTransformerTest {
   
   @Autowired
   private JmsTemplate jmsTemplate;
   
   @Autowired
   private MockQueue inBoundQueue;
   
   
   @Test
   public void shouldSendMessage() throws InterruptedException
   {
     final Department defaultDepartment = new Department();
     jmsTemplate.send(inBoundQueue,new MessageCreator() {
      
      @Override
      public Message createMessage(Session session) throws JMSException {
        
         ObjectMessage objectMessage = session.createObjectMessage();
         objectMessage.setObject(defaultDepartment);
         return objectMessage;
      }
   });
     Thread.sleep(5000);
   }
}

Spring with JUNIT class where you can send message to inputChannel i.e inBoundQueue using MockRunner.

output :

INFO: started inBoundJMSAdapter
Oct 06, 2014 1:24:25 PM org.springframework.integration.endpoint.AbstractEndpoint start
INFO: started org.springframework.integration.config.ConsumerEndpointFactoryBean#1
13:24:26.882 [org.springframework.jms.listener.DefaultMessageListenerContainer#0-1] INFO  c.spijb.listener.MapMessageListener - Message Received 
com.mockrunner.mock.jms.MockMapMessage: {location=TX, name=SALES, deptno=10}
Oct 06, 2014 1:24:30 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@5840979b: startup date [Mon Oct 06 13:24:25 CDT 2014]; root of context hierarchy
Oct 06, 2014 1:24:30 PM org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup stop
INFO: Stopping beans in phase 2147483647

In the above highlighted one is output as map.