The 'extract-payload-for-reply' attribute for a JMS inbound-gateway is now 'extract-reply-payload'. The 'extract-request-payload' attribute has also been added. Both have default values of TRUE (the default for 'extract-reply-payload' had been FALSE). This work is related to INT-368, INT-460, and INT-467.

This commit is contained in:
Mark Fisher
2008-11-13 21:22:40 +00:00
parent b730a0ddc5
commit e2e98d8c2c
6 changed files with 130 additions and 61 deletions

View File

@@ -16,11 +16,9 @@
package org.springframework.integration.jms;
import java.io.Serializable;
import java.util.Map;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import org.apache.commons.logging.Log;
@@ -31,7 +29,7 @@ import org.springframework.integration.core.MessageHeaders;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.util.Assert;
import org.springframework.jms.support.converter.SimpleMessageConverter;
/**
* A {@link MessageConverter} implementation that is capable of delegating to
@@ -57,13 +55,14 @@ public class HeaderMappingMessageConverter implements MessageConverter {
private final JmsHeaderMapper headerMapper;
private volatile boolean extractPayload;
private volatile boolean extractRequestPayload = true;
private volatile boolean extractReplyPayload = true;
/**
* Create a HeaderMappingMessageConverter instance that will <em>not</em>
* delegate to another {@link MessageConverter} and will use the default
* implementation of the {@link JmsHeaderMapper} strategy.
* Create a HeaderMappingMessageConverter instance that will rely on the
* default {@link SimpleMessageConverter} and {@link DefaultJmsHeaderMapper}.
*/
public HeaderMappingMessageConverter() {
this(null, null);
@@ -78,36 +77,54 @@ public class HeaderMappingMessageConverter implements MessageConverter {
this(converter, null);
}
/**
* Create a HeaderMappingMessageConverter instance that will delegate to
* the provided {@link JmsHeaderMapper} instance and will use the default
* {@link SimpleMessageConverter} implementation.
*/
public HeaderMappingMessageConverter(JmsHeaderMapper headerMapper) {
this(null, headerMapper);
}
/**
* Create a HeaderMappingMessageConverter instance that will delegate to
* the provided {@link MessageConverter} and {@link JmsHeaderMapper}.
*/
public HeaderMappingMessageConverter(MessageConverter converter, JmsHeaderMapper headerMapper) {
this.converter = converter;
this.converter = (converter != null ? converter : new SimpleMessageConverter());
this.headerMapper = (headerMapper != null ? headerMapper : new DefaultJmsHeaderMapper());
}
/**
* Specify whether the inbound JMS Message's payload should be extracted
* during the conversion process. Otherwise, the raw JMS Message itself
* will be the payload of the created Spring Integration Message. The
* HeaderMapper will be applied to the Message regardless of this value.
*
* <p>The default value is <code>true</code>.
*/
public void setExtractRequestPayload(boolean extractRequestPayload) {
this.extractRequestPayload = extractRequestPayload;
}
/**
* Specify whether the integration Message's payload should be extracted
* prior to conversion. Otherwise, the integration Message itself will be
* passed to the converter.
*
* <p>If no {@link MessageConverter} is available (the default), the
* integration Message will be sent within a JMS {@link ObjectMessage}.
* Specify whether the outbound integration Message's payload should be
* extracted prior to conversion into a JMS Message. Otherwise, the
* integration Message itself will be passed to the converter.
*
* <p>Typically, this setting should be determined by the expectations of
* the target system. If the target system is not capable of understanding
* a Spring Integration Message, then set this to <code>true</code>.
* a Spring Integration Message, then set this to <code>true</code>.
* On the other hand, if the system is not only capable of understanding a
* Spring Integration Message but actually expected to rely upon header
* values, then this must be set to <code>false</code> so that the actual
* Message along with its headers will be passed.
* Spring Integration Message but actually expected to rely upon Spring
* Integration Message Header values, then this must be set to
* <code>false</code> to ensure that the actual Message will be passed
* along with its Serializable headers.
*
* <p>The default value is <code>false</code>.
* <p>The default value is <code>true</code>.
*/
public void setExtractPayload(boolean extractPayload) {
this.extractPayload = extractPayload;
public void setExtractReplyPayload(boolean extractReplyPayload) {
this.extractReplyPayload = extractReplyPayload;
}
/**
@@ -115,10 +132,7 @@ public class HeaderMappingMessageConverter implements MessageConverter {
*/
public Object fromMessage(javax.jms.Message jmsMessage) throws JMSException, MessageConversionException {
MessageBuilder<?> builder = null;
if (this.converter == null) {
builder = MessageBuilder.withPayload(jmsMessage);
}
else {
if (this.extractRequestPayload) {
Object conversionResult = this.converter.fromMessage(jmsMessage);
if (conversionResult == null) {
return null;
@@ -130,6 +144,9 @@ public class HeaderMappingMessageConverter implements MessageConverter {
builder = MessageBuilder.withPayload(conversionResult);
}
}
else {
builder = MessageBuilder.withPayload(jmsMessage);
}
Map<String, Object> headers = this.headerMapper.toHeaders(jmsMessage);
Message<?> message = builder.copyHeadersIfAbsent(headers).build();
if (logger.isDebugEnabled()) {
@@ -146,17 +163,11 @@ public class HeaderMappingMessageConverter implements MessageConverter {
javax.jms.Message jmsMessage = null;
if (object instanceof Message) {
headers = ((Message<?>) object).getHeaders();
if (this.extractPayload) {
if (this.extractReplyPayload) {
object = ((Message<?>) object).getPayload();
}
}
if (this.converter == null) {
Assert.isInstanceOf(Serializable.class, object, "Object must implement Serializable");
jmsMessage = session.createObjectMessage((Serializable) object);
}
else {
jmsMessage = this.converter.toMessage(object, session);
}
jmsMessage = this.converter.toMessage(object, session);
if (headers != null) {
this.headerMapper.fromHeaders(headers, jmsMessage);
}

View File

@@ -31,7 +31,6 @@ import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.jms.listener.adapter.MessageListenerAdapter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;
@@ -52,6 +51,12 @@ public class JmsInboundGateway extends SimpleMessagingGateway implements Disposa
private volatile MessageConverter messageConverter;
private volatile JmsHeaderMapper headerMapper;
private volatile boolean extractRequestPayload = true;
private volatile boolean extractReplyPayload = true;
private volatile TaskExecutor taskExecutor;
private volatile PlatformTransactionManager transactionManager;
@@ -68,8 +73,6 @@ public class JmsInboundGateway extends SimpleMessagingGateway implements Disposa
private volatile int idleTaskExecutionLimit = 1;
private volatile boolean extractPayloadForReply = false;
public void setContainer(AbstractMessageListenerContainer container) {
this.container = container;
@@ -87,11 +90,40 @@ public class JmsInboundGateway extends SimpleMessagingGateway implements Disposa
this.destinationName = destinationName;
}
/**
* Provide a {@link MessageConverter} implementation to use when
* converting between JMS Messages and Spring Integration Messages.
* If none is provided, a {@link HeaderMappingMessageConverter} will
* be used and the {@link JmsHeaderMapper} instance provided to the
* {@link #setHeaderMapper(JmsHeaderMapper)} method will be included
* in the conversion process.
*/
public void setMessageConverter(MessageConverter messageConverter) {
Assert.notNull(messageConverter, "'messageConverter' must not be null");
this.messageConverter = messageConverter;
}
/**
* Provide a {@link JmsHeaderMapper} implementation to use when
* converting between JMS Messages and Spring Integration Messages.
* If none is provided, a {@link DefaultJmsHeaderMapper} will be used.
*
* <p>This property will be ignored if a {@link MessageConverter} is
* provided to the {@link #setMessageConverter(MessageConverter)} method.
* However, you may provide your own implementation of the delegating
* {@link HeaderMappingMessageConverter} implementation.
*/
public void setHeaderMapper(JmsHeaderMapper headerMapper) {
this.headerMapper = headerMapper;
}
public void setExtractRequestPayload(boolean extractRequestPayload) {
this.extractRequestPayload = extractRequestPayload;
}
public void setExtractReplyPayload(boolean extractReplyPayload) {
this.extractReplyPayload = extractReplyPayload;
}
public void setTaskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
@@ -124,21 +156,15 @@ public class JmsInboundGateway extends SimpleMessagingGateway implements Disposa
this.idleTaskExecutionLimit = idleTaskExecutionLimit;
}
public void setExtractPayloadForReply(boolean extractPayloadForReply) {
this.extractPayloadForReply = extractPayloadForReply;
}
private void initialize() {
if (this.container == null) {
this.container = createDefaultContainer();
}
if (this.messageConverter == null) {
this.messageConverter = new SimpleMessageConverter();
}
if (!(this.messageConverter instanceof HeaderMappingMessageConverter)) {
HeaderMappingMessageConverter hmmc = new HeaderMappingMessageConverter(this.messageConverter);
hmmc.setExtractPayload(this.extractPayloadForReply);
HeaderMappingMessageConverter hmmc = new HeaderMappingMessageConverter(null, this.headerMapper);
hmmc.setExtractRequestPayload(this.extractRequestPayload);
hmmc.setExtractReplyPayload(this.extractReplyPayload);
this.messageConverter = hmmc;
}
MessageListenerAdapter listener = new MessageListenerAdapter();

View File

@@ -87,9 +87,10 @@ public class JmsInboundGatewayParser extends AbstractSingleBeanDefinitionParser
builder.addPropertyValue("sessionAcknowledgeMode", acknowledgeMode);
}
}
if ("true".equals(element.getAttribute("extract-payload-for-reply"))) {
builder.addPropertyValue("extractPayloadForReply", Boolean.TRUE);
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converter");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "header-mapper");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-request-payload");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-reply-payload");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "transaction-manager");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout");

View File

@@ -47,11 +47,13 @@
<xsd:complexContent>
<xsd:extension base="jmsInboundAdapterType">
<xsd:attribute name="message-converter" type="xsd:string"/>
<xsd:attribute name="header-mapper" type="xsd:string"/>
<xsd:attribute name="extract-request-payload" type="xsd:string" default="true"/>
<xsd:attribute name="extract-reply-payload" type="xsd:string" default="true"/>
<xsd:attribute name="request-channel" type="xsd:string" use="required"/>
<xsd:attribute name="reply-channel" type="xsd:string"/>
<xsd:attribute name="request-timeout" type="xsd:string"/>
<xsd:attribute name="reply-timeout" type="xsd:string"/>
<xsd:attribute name="extract-payload-for-reply" type="xsd:string" default="false"/>
<xsd:attribute name="transaction-manager" type="xsd:string"/>
<xsd:attribute name="concurrent-consumers" type="xsd:string"/>
<xsd:attribute name="max-concurrent-consumers" type="xsd:string"/>

View File

@@ -85,25 +85,43 @@ public class JmsInboundGatewayParserTests {
"jmsGatewaysWithExtractPayloadAttributes.xml", this.getClass());
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("defaultGateway");
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.FALSE, accessor.getPropertyValue("extractPayloadForReply"));
assertEquals(Boolean.TRUE, accessor.getPropertyValue("extractReplyPayload"));
}
@Test
public void testGatewayWithExtractPayloadTrue() {
public void testGatewayWithExtractReplyPayloadTrue() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"jmsGatewaysWithExtractPayloadAttributes.xml", this.getClass());
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("gatewayExpectingReply");
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("extractReplyPayloadTrue");
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.TRUE, accessor.getPropertyValue("extractPayloadForReply"));
assertEquals(Boolean.TRUE, accessor.getPropertyValue("extractReplyPayload"));
}
@Test
public void testGatewayWithExtractPayloadFalse() {
public void testGatewayWithExtractReplyPayloadFalse() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"jmsGatewaysWithExtractPayloadAttributes.xml", this.getClass());
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("gatewayNotExpectingReply");
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("extractReplyPayloadFalse");
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.FALSE, accessor.getPropertyValue("extractPayloadForReply"));
assertEquals(Boolean.FALSE, accessor.getPropertyValue("extractReplyPayload"));
}
@Test
public void testGatewayWithExtractRequestPayloadTrue() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"jmsGatewaysWithExtractPayloadAttributes.xml", this.getClass());
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("extractRequestPayloadTrue");
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.TRUE, accessor.getPropertyValue("extractRequestPayload"));
}
@Test
public void testGatewayWithExtractRequestPayloadFalse() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"jmsGatewaysWithExtractPayloadAttributes.xml", this.getClass());
JmsInboundGateway gateway = (JmsInboundGateway) context.getBean("extractRequestPayloadFalse");
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.FALSE, accessor.getPropertyValue("extractRequestPayload"));
}
@Test(expected=BeanDefinitionStoreException.class)

View File

@@ -18,15 +18,26 @@
destination="testDestination"
request-channel="requestChannel"/>
<jms:inbound-gateway id="gatewayNotExpectingReply"
<jms:inbound-gateway id="extractReplyPayloadTrue"
destination="testDestination"
request-channel="requestChannel"
extract-payload-for-reply="false"/>
extract-reply-payload="true"/>
<jms:inbound-gateway id="gatewayExpectingReply"
<jms:inbound-gateway id="extractReplyPayloadFalse"
destination="testDestination"
request-channel="requestChannel"
extract-payload-for-reply="true"/>
extract-reply-payload="false"/>
<jms:inbound-gateway id="extractRequestPayloadTrue"
destination="testDestination"
request-channel="requestChannel"
extract-request-payload="true"/>
<jms:inbound-gateway id="extractRequestPayloadFalse"
destination="testDestination"
request-channel="requestChannel"
extract-request-payload="false"/>
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<constructor-arg>