INT-2269 Add ReplyChannelRegistry

Allows reply channel resolution after a message has been serialized
somewhere in a flow. Previously, the reply channel was lost.

With this change, the reply channel can be registered and the header
becomes a string (channel name) that can be serialized.

JIRA: https://jira.springsource.org/browse/INT-2269

INT-2269 Rename Registry to HeaderChannelRegistry

- Extract interface
- DefaultHeaderChannelRgistry

INT-2269 Polishing; PR Comments

- Add errorChannel registration as well.

INT-2269 HeaderChannelRegistry - Fix

The internal BridgeHandler in MessagingGatewaySupport did not have
a channel resolver. When a reply was explicitly routed to the gateway's
reply channel, the String representation of the reply channel could
not be resolved to a channel.

Set the BeanFactory on the bridge handler.

Add tests.

INT-2269 HeaderChannelRegistry - Fix JMS/Enricher

The JMS inbound gateway and ContentEnricher instantiate a
MessagingGatewaySupport internally it does not get a
reference to the BeanFactory. This means that the internal
BridgeHandler cannot resolve the String representation of
the reply channel to a channel.

Make ChannelPublishingJmsMessageListener BeanFactory aware, and
propagate the bean factory to the MGS.

Set the MGS bean factory in the ContentEnricher (which is already
BFA).

INT-2269: Polishing
This commit is contained in:
Gary Russell
2013-11-15 15:21:44 +02:00
committed by Artem Bilan
parent 1d0c28852f
commit 836c8e2556
21 changed files with 1060 additions and 185 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,9 @@ import javax.jms.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
@@ -47,18 +50,18 @@ import org.springframework.util.Assert;
* Message and sends that Message to a channel. If the 'expectReply' value is
* <code>true</code>, it will also wait for a Spring Integration reply Message
* and convert that into a JMS reply.
*
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Oleg Zhurakousky
*/
public class ChannelPublishingJmsMessageListener
implements SessionAwareMessageListener<javax.jms.Message>, InitializingBean, TrackableComponent {
public class ChannelPublishingJmsMessageListener
implements SessionAwareMessageListener<javax.jms.Message>, InitializingBean, TrackableComponent, BeanFactoryAware {
protected final Log logger = LogFactory.getLog(getClass());
private volatile boolean expectReply;
private volatile MessageConverter messageConverter = new SimpleMessageConverter();
private volatile boolean extractRequestPayload = true;
@@ -80,9 +83,11 @@ public class ChannelPublishingJmsMessageListener
private volatile DestinationResolver destinationResolver = new DynamicDestinationResolver();
private volatile JmsHeaderMapper headerMapper = new DefaultJmsHeaderMapper();
private final GatewayDelegate gatewayDelegate = new GatewayDelegate();
private volatile BeanFactory beanFactory;
/**
* Specify whether a JMS reply Message is expected.
*/
@@ -93,39 +98,42 @@ public class ChannelPublishingJmsMessageListener
public void setComponentName(String componentName){
this.gatewayDelegate.setComponentName(componentName);
}
public void setRequestChannel(MessageChannel requestChannel){
this.gatewayDelegate.setRequestChannel(requestChannel);
}
public void setReplyChannel(MessageChannel replyChannel){
this.gatewayDelegate.setReplyChannel(replyChannel);
}
public void setErrorChannel(MessageChannel errorChannel){
this.gatewayDelegate.setErrorChannel(errorChannel);
}
public void setRequestTimeout(long requestTimeout){
this.gatewayDelegate.setRequestTimeout(requestTimeout);
}
public void setReplyTimeout(long replyTimeout){
this.gatewayDelegate.setReplyTimeout(replyTimeout);
}
@Override
public void setShouldTrack(boolean shouldTrack) {
this.gatewayDelegate.setShouldTrack(shouldTrack);
}
@Override
public String getComponentName() {
return this.gatewayDelegate.getComponentName();
}
@Override
public String getComponentType() {
return this.gatewayDelegate.getComponentType();
}
/**
* Set the default reply destination to send reply messages to. This will
* be applied in case of a request message that does not carry a
@@ -189,7 +197,7 @@ public class ChannelPublishingJmsMessageListener
* JMSMessageID from the request will be copied into the JMSCorrelationID of the reply
* unless there is already a value in the JMSCorrelationID property of the newly created
* reply Message in which case nothing will be copied. If the JMSCorrelationID of the
* request Message should be copied into the JMSCorrelationID of the reply Message
* request Message should be copied into the JMSCorrelationID of the reply Message
* instead, then this value should be set to "JMSCorrelationID".
* Any other value will be treated as a JMS String Property to be copied as-is
* from the request Message into the reply Message with the same property name.
@@ -200,7 +208,7 @@ public class ChannelPublishingJmsMessageListener
/**
* Specify whether explicit QoS should be enabled for replies
* (for timeToLive, priority, and deliveryMode settings).
* (for timeToLive, priority, and deliveryMode settings).
*/
public void setExplicitQosEnabledForReplies(boolean explicitQosEnabledForReplies) {
this.explicitQosEnabledForReplies = explicitQosEnabledForReplies;
@@ -224,7 +232,7 @@ public class ChannelPublishingJmsMessageListener
* converting between JMS Messages and Spring Integration Messages.
* If none is provided, a {@link SimpleMessageConverter} will
* be used.
*
*
* @param messageConverter
*/
public void setMessageConverter(MessageConverter messageConverter) {
@@ -260,6 +268,12 @@ public class ChannelPublishingJmsMessageListener
this.extractReplyPayload = extractReplyPayload;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void onMessage(javax.jms.Message jmsMessage, Session session) throws JMSException {
Object result = jmsMessage;
if (this.extractRequestPayload) {
@@ -268,10 +282,10 @@ public class ChannelPublishingJmsMessageListener
logger.debug("converted JMS Message [" + jmsMessage + "] to integration Message payload [" + result + "]");
}
}
Map<String, Object> headers = headerMapper.toHeaders(jmsMessage);
Message<?> requestMessage = (result instanceof Message<?>) ?
MessageBuilder.fromMessage((Message<?>) result).copyHeaders(headers).build() :
MessageBuilder.fromMessage((Message<?>) result).copyHeaders(headers).build() :
MessageBuilder.withPayload(result).copyHeaders(headers).build();
if (!this.expectReply) {
this.gatewayDelegate.send(requestMessage);
@@ -305,18 +319,22 @@ public class ChannelPublishingJmsMessageListener
}
}
@Override
public void afterPropertiesSet() {
if (this.beanFactory != null) {
this.gatewayDelegate.setBeanFactory(this.beanFactory);
}
this.gatewayDelegate.afterPropertiesSet();
}
protected void start(){
this.gatewayDelegate.start();
}
protected void stop(){
this.gatewayDelegate.stop();
}
private void copyCorrelationIdFromRequestToReply(javax.jms.Message requestMessage, javax.jms.Message replyMessage) throws JMSException {
if (this.correlationKey != null) {
if (this.correlationKey.equals("JMSCorrelationID")) {
@@ -416,17 +434,20 @@ public class ChannelPublishingJmsMessageListener
this.isTopic = isTopic;
}
}
private class GatewayDelegate extends MessagingGatewaySupport {
@Override
protected void send(Object request) {
super.send(request);
}
@Override
protected Message<?> sendAndReceiveMessage(Object request) {
return super.sendAndReceiveMessage(request);
}
@Override
public String getComponentType() {
if (expectReply) {
return "jms:inbound-gateway";

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="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.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost"/>
</bean>
<int:channel id="input" />
<int:channel id="output">
<int:queue />
</int:channel>
<int-jms:outbound-gateway request-channel="input" reply-channel="output"
connection-factory="connectionFactory"
request-destination-name="serialized.reply.channel" />
<int-jms:inbound-gateway request-channel="foo" reply-channel="baz"
request-destination-name="serialized.reply.channel"
connection-factory="connectionFactory"/>
<int:header-enricher input-channel="foo" output-channel="bar">
<int:header-channels-to-string />
</int:header-enricher>
<int:transformer input-channel="bar" output-channel="baz"
expression="'echo:' + payload" />
<int:channel id="baz" />
</beans>

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jms.request_reply;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 3.0
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class GatewaySerializedReplyChannelTests {
@Autowired
MessageChannel input;
@Autowired
PollableChannel output;
@Test
public void test() {
input.send(new GenericMessage<String>("foo"));
Message<?> reply = output.receive(0);
assertNotNull(reply);
assertEquals("echo:foo", reply.getPayload());
}
}