INT-2822: 'requires-reply' for Outbound Gateways

* Add `requires-reply` attribute for all adapters outbound gateways as `true` by default
* WS-outbound-gateway is still without it, because it has its own specific attribute `ignore-empty-responses`
* Make `requires-reply` as `false` by default for `jdbc:stored-proc-outbound-gateway` inasmuch as `jdbc:stored-proc-outbound-adapter`
doesn't have ability to configure `returning-resultset`
* add parser tests for `requires-reply`

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

INT-2822 'requires-reply' for ws:outbound-gateway

Default false.

INT-2822: Polishing after rebase

INT-2822: deprecate 'ignore-empty-responses'

* Add 'requires-reply' section into What's New

INT-2822: remove 'ignore-empty-responses' from RM

INT-2822: Polishing after rebase

INT-2822: Rebased and polished

INT-2822: Rebased and polished

Add WARN within deprecated `AbstractWebServiceOutboundGateway#setIgnoreEmptyResponses`

Revert 'ignore-empty-responses'; Doc Polishing
This commit is contained in:
Artem Bilan
2013-03-18 13:50:16 +02:00
committed by Gary Russell
parent b8980d4064
commit 70886b2543
46 changed files with 310 additions and 64 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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. You may obtain a copy of the License at
@@ -29,6 +29,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.1
*/
@@ -53,6 +54,7 @@ public class AmqpOutboundGatewayParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key-expression");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "return-channel");

View File

@@ -199,6 +199,15 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -40,6 +40,7 @@
routing-key="si.test.binding"
amqp-template="amqpTemplate"
order="5"
requires-reply="false"
mapped-request-headers="foo*"
mapped-reply-headers="bar*"/>

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.amqp.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -62,7 +63,7 @@ public class AmqpOutboundGatewayParserTests {
Object edc = context.getBean("rabbitGateway");
AmqpOutboundEndpoint gateway = TestUtils.getPropertyValue(edc, "handler", AmqpOutboundEndpoint.class);
assertEquals(5, gateway.getOrder());
assertTrue(context.containsBean("rabbitGateway"));
assertTrue(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
assertEquals(context.getBean("fromRabbit"), TestUtils.getPropertyValue(gateway, "outputChannel"));
assertEquals("amqp:outbound-gateway", gateway.getComponentType());
MessageChannel returnChannel = context.getBean("returnChannel", MessageChannel.class);
@@ -81,6 +82,8 @@ public class AmqpOutboundGatewayParserTests {
AmqpOutboundEndpoint endpoint = TestUtils.getPropertyValue(eventDrivernConsumer, "handler", AmqpOutboundEndpoint.class);
assertFalse(TestUtils.getPropertyValue(endpoint, "requiresReply", Boolean.class));
Field amqpTemplateField = ReflectionUtils.findField(AmqpOutboundEndpoint.class, "amqpTemplate");
amqpTemplateField.setAccessible(true);
RabbitTemplate amqpTemplate = TestUtils.getPropertyValue(endpoint, "amqpTemplate", RabbitTemplate.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -26,6 +26,7 @@ import org.springframework.util.StringUtils;
* Base class for url-based outbound gateway parsers.
*
* @author Mark Fisher
* @author Artem Bilan
*/
public abstract class AbstractOutboundGatewayParser extends AbstractConsumerEndpointParser {
@@ -45,6 +46,7 @@ public abstract class AbstractOutboundGatewayParser extends AbstractConsumerEndp
if (StringUtils.hasText(replyChannel)) {
builder.addPropertyReference("replyChannel", replyChannel);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
this.postProcessGateway(builder, element, parserContext);
return builder;
}

View File

@@ -1075,7 +1075,6 @@
</xsd:complexType>
</xsd:element>
<xsd:complexType name="serviceActivatorType">
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
@@ -1083,10 +1082,8 @@
<xsd:annotation>
<xsd:documentation>
Specify whether the service method must return a non-null value. This value will be
FALSE by
default, but if set to TRUE, a MessageHandlingException will be thrown when
the underlying service method (or
expression) returns a NULL value.
'false' by default, but if set to 'true', a ReplyRequiredException will be thrown when
the underlying service method (or expression) returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -3153,9 +3150,9 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Specify whether the splitter method must return a non-null value. This value will be
FALSE by default, but if set to TRUE, a MessageHandlingException will be thrown when
the underlying service method (or expression) returns a NULL value.
Specify whether the service method must return a non-null value. This value will be
'false' by default, but if set to 'true', a ReplyRequiredException will be thrown when
the underlying service method (or expression) returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>

View File

@@ -27,6 +27,7 @@ import org.springframework.util.StringUtils;
* @author Gary Russell
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.1
*/
@@ -54,8 +55,7 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "local-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-create-local-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "order");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "rename-expression");
return builder;
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "rename-expression"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply"); return builder;
}
protected void configureFilter(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -44,6 +44,7 @@ public class FileOutboundGatewayParser extends AbstractConsumerEndpointParser {
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder handlerBuilder = FileWritingMessageHandlerBeanDefinitionBuilder.configure(element, true, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "requires-reply");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(handlerBuilder, element, "reply-channel", "outputChannel");
return handlerBuilder;
}

View File

@@ -394,6 +394,15 @@ Only files matching this regular expression will be picked up by this adapter.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -19,7 +19,7 @@
<int-file:outbound-gateway id="gatewayWithDirectoryExpression"
request-channel="someChannel" directory-expression="'build/foo'"
auto-startup="false" order="777" filename-generator-expression="'foo.txt'">
auto-startup="false" order="777" filename-generator-expression="'foo.txt'" requires-reply="false">
<int-file:request-handler-advice-chain>
<beans:bean class="org.springframework.integration.file.config.FileOutboundGatewayParserTests$FooAdvice" />
</int-file:request-handler-advice-chain>
@@ -28,7 +28,7 @@
<int-file:outbound-gateway id="gatewayWithReplaceMode"
request-channel="gatewayWithReplaceModeChannel"
filename-generator-expression="'fileToAppend.txt'" mode="REPLACE"
directory="test" />
directory="test" requires-reply="false"/>
<int-file:outbound-gateway id="gatewayWithAppendMode"
request-channel="gatewayWithAppendModeChannel"

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -17,6 +17,7 @@
package org.springframework.integration.file.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -27,10 +28,12 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.expression.Expression;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.file.DefaultFileNameGenerator;
@@ -45,6 +48,7 @@ import org.springframework.util.FileCopyUtils;
/**
* @author Mark Fisher
* @author Gunnar Hillert
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@@ -68,6 +72,10 @@ public class FileOutboundGatewayParserTests {
@Autowired
MessageChannel gatewayWithReplaceModeChannel;
@Autowired
@Qualifier("gatewayWithReplaceMode.handler")
MessageHandler gatewayWithReplaceModeHandler;
@Autowired
MessageChannel gatewayWithFailModeLowercaseChannel;
@@ -82,6 +90,7 @@ public class FileOutboundGatewayParserTests {
assertEquals(Boolean.FALSE, gatewayAccessor.getPropertyValue("autoStartup"));
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(handler);
assertEquals(777, handlerAccessor.getPropertyValue("order"));
assertEquals(Boolean.TRUE, handlerAccessor.getPropertyValue("requiresReply"));
DefaultFileNameGenerator fileNameGenerator = (DefaultFileNameGenerator) handlerAccessor.getPropertyValue("fileNameGenerator");
assertNotNull(fileNameGenerator);
String expression = (String) TestUtils.getPropertyValue(fileNameGenerator, "expression");
@@ -264,6 +273,8 @@ public class FileOutboundGatewayParserTests {
@Test
public void gatewayWithReplaceMode() throws Exception{
assertFalse(TestUtils.getPropertyValue(this.gatewayWithReplaceModeHandler, "requiresReply", Boolean.class));
final MessagingTemplate messagingTemplate = new MessagingTemplate(this.gatewayWithReplaceModeChannel);
String expectedFileContent = "String content:";

View File

@@ -435,6 +435,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -43,6 +43,7 @@
command-options="-P"
expression="payload"
order="2"
requires-reply="false"
>
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.ftp.config.FtpOutboundGatewayParserTests$FooAdvice" />

View File

@@ -84,6 +84,7 @@ public class FtpOutboundGatewayParserTests {
Long sendTimeout = TestUtils.getPropertyValue(gateway, "messagingTemplate.sendTimeout", Long.class);
assertEquals(Long.valueOf(777), sendTimeout);
assertTrue(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
}
@Test
@@ -101,6 +102,7 @@ public class FtpOutboundGatewayParserTests {
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertTrue(options.contains(Option.PRESERVE_TIMESTAMP));
gateway.handleMessage(new GenericMessage<String>("foo"));
assertFalse(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
assertEquals(1, adviceCalled);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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. You may obtain a copy of the License at
@@ -24,6 +24,7 @@ import org.w3c.dom.Element;
/**
* @author Dave Syer
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.0
*
@@ -48,8 +49,7 @@ public class JdbcOutboundGatewayParser extends AbstractConsumerEndpointParser {
String updateQuery = IntegrationNamespaceUtils.getTextFromAttributeOrNestedElement(element, "update",
parserContext);
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(JdbcOutboundGateway.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(JdbcOutboundGateway.class);
if (refToDataSourceSet) {
builder.addConstructorArgReference(dataSourceRef);
}
@@ -68,6 +68,7 @@ public class JdbcOutboundGatewayParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "max-rows-per-poll");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "keys-generated");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
String replyChannel = element.getAttribute("reply-channel");
if (StringUtils.hasText(replyChannel)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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. You may obtain a copy of the License at
@@ -27,6 +27,7 @@ import org.w3c.dom.Element;
/**
* @author Gunnar Hillert
* @author Artem Bilan
* @since 2.1
*
*/
@@ -62,6 +63,7 @@ public class StoredProcOutboundGatewayParser extends AbstractConsumerEndpointPar
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expect-single-result");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
String replyChannel = element.getAttribute("reply-channel");
if (StringUtils.hasText(replyChannel)) {

View File

@@ -502,6 +502,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -967,6 +976,15 @@
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'false' by default, if it set to 'true', a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@@ -14,14 +14,17 @@ package org.springframework.integration.jdbc.config;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ApplicationContext;
@@ -29,6 +32,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.endpoint.PollingConsumer;
@@ -75,6 +79,7 @@ public class JdbcOutboundGatewayParserTests {
assertEquals("bar", payload.get("name"));
JdbcOutboundGateway gateway = context.getBean("jdbcGateway.handler", JdbcOutboundGateway.class);
assertEquals(23, TestUtils.getPropertyValue(gateway, "order"));
Assert.assertTrue(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
Object gw = context.getBean("jdbcGateway");
assertEquals(1, adviceCalled);
}
@@ -204,11 +209,15 @@ public class JdbcOutboundGatewayParserTests {
@Test //INT-1029
public void testOutboundGatewayInsideChain() {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("handlingMapPayloadJdbcOutboundGatewayTest.xml", getClass());
//INT-2755
assertNotNull(context.getBean("org.springframework.integration.handler.MessageHandlerChain#0$child.jdbc-outbound-gateway-within-chain.handler",
JdbcOutboundGateway.class));
JdbcOutboundGateway jdbcMessageHandler =
context.getBean("org.springframework.integration.handler.MessageHandlerChain#0$child.jdbc-outbound-gateway-within-chain.handler",
JdbcOutboundGateway.class);
MessageChannel channel = context.getBean("jdbcOutboundGatewayInsideChain", MessageChannel.class);
assertFalse(TestUtils.getPropertyValue(jdbcMessageHandler, "requiresReply", Boolean.class));
channel.send(MessageBuilder.withPayload(Collections.singletonMap("foo", "bar")).build());
PollableChannel outbound = context.getBean("replyChannel", PollableChannel.class);

View File

@@ -47,6 +47,7 @@ import org.springframework.jdbc.core.SqlParameter;
/**
* @author Gunnar Hillert
* @author Gary Russell
* @author Artem Bilan
* @since 2.1
*
*/
@@ -65,6 +66,7 @@ public class StoredProcOutboundGatewayParserTests {
DirectFieldAccessor accessor = new DirectFieldAccessor(this.outboundGateway);
Object source = accessor.getPropertyValue("handler");
accessor = new DirectFieldAccessor(source);
assertEquals(Boolean.TRUE, accessor.getPropertyValue("requiresReply"));
source = accessor.getPropertyValue("executor");
accessor = new DirectFieldAccessor(source);
Expression storedProcedureName = (Expression) accessor.getPropertyValue("storedProcedureNameExpression");

View File

@@ -26,7 +26,7 @@
<si:chain input-channel="jdbcOutboundGatewayInsideChain" output-channel="replyChannel">
<outbound-gateway id="jdbc-outbound-gateway-within-chain" query="select * from foos where id=:headers[id]"
update="insert into foos (id, status, name) values (:headers[id], 0, :payload[foo])"
data-source="dataSource"/>
data-source="dataSource" requires-reply="false"/>
</si:chain>

View File

@@ -18,7 +18,7 @@
data-source="datasource" auto-startup="true" id="storedProcedureOutboundGateway"
ignore-column-meta-data="false" is-function="false"
skip-undeclared-results="false" order="2" reply-channel="replyChannel"
reply-timeout="555" return-value-required="true">
reply-timeout="555" return-value-required="true" requires-reply="true">
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR" />
<int-jdbc:sql-parameter-definition name="password" direction="OUT" />

View File

@@ -33,6 +33,7 @@ import org.w3c.dom.Element;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*/
public class JmsOutboundGatewayParser extends AbstractConsumerEndpointParser {
@@ -65,6 +66,7 @@ public class JmsOutboundGatewayParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "time-to-live");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "priority");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "explicit-qos-enabled");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
String deliveryPersistent = element.getAttribute("delivery-persistent");
if (StringUtils.hasText(deliveryPersistent)) {
@@ -119,7 +121,7 @@ public class JmsOutboundGatewayParser extends AbstractConsumerEndpointParser {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(JmsOutboundGateway.ReplyContainerProperties.class);
Integer acknowledgeMode = JmsAdapterParserUtils.parseAcknowledgeMode(element, parserContext);
if (acknowledgeMode != null) {
if (acknowledgeMode.intValue() == JmsAdapterParserUtils.SESSION_TRANSACTED) {
if (JmsAdapterParserUtils.SESSION_TRANSACTED == acknowledgeMode) {
builder.addPropertyValue("sessionTransacted", Boolean.TRUE);
}
else {

View File

@@ -1065,6 +1065,15 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
@@ -1469,4 +1478,4 @@
</xsd:attributeGroup>
</xsd:schema>
</xsd:schema>

View File

@@ -128,6 +128,7 @@ public class JmsOutboundGatewayParserTests {
new DirectFieldAccessor(endpoint).getPropertyValue("handler"));
Object order = accessor.getPropertyValue("order");
assertEquals(99, order);
assertEquals(Boolean.TRUE, accessor.getPropertyValue("requiresReply"));
}
@Test

View File

@@ -14,7 +14,7 @@
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<si:channel id="requestChannel"/>
<jms:outbound-gateway id="jmsGateway"
request-destination-name="requestQueue"
request-channel="requestChannel"
@@ -47,7 +47,8 @@
request-destination-name="requestQueue"
request-channel="requestChannel"
delivery-persistent="true"
auto-startup="false">
auto-startup="false"
requires-reply="false">
<jms:request-handler-advice-chain>
<bean class="org.springframework.integration.jms.config.JmsOutboundGatewayParserTests$FooAdvice" />
</jms:request-handler-advice-chain>

View File

@@ -15,7 +15,7 @@
<jms:outbound-gateway id="jmsGateway"
request-destination="requestQueue"
request-channel="requestChannel"
order="99"/>
order="99" requires-reply="true"/>
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<constructor-arg>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 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.
@@ -18,6 +18,7 @@ package org.springframework.integration.jmx.config;
import org.w3c.dom.Element;
import org.springframework.integration.jmx.OperationInvokingMessageHandler;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
@@ -25,10 +26,11 @@ import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
/**
* @author Oleg Zhurakousky
* @author Artem Bilan
* @since 2.0
*/
public class OperationInvokingOutboundGatewayParser extends AbstractConsumerEndpointParser {
@Override
protected String getInputChannelAttributeName() {
return "request-channel";
@@ -36,12 +38,12 @@ public class OperationInvokingOutboundGatewayParser extends AbstractConsumerEndp
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(
"org.springframework.integration.jmx.OperationInvokingMessageHandler");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(OperationInvokingMessageHandler.class);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "server");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "object-name");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "operation-name");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
return builder;
}

View File

@@ -47,6 +47,15 @@
</xsd:all>
<xsd:attribute name="request-channel" type="xsd:string" />
<xsd:attribute name="reply-channel" type="xsd:string" use="optional" />
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -29,18 +29,19 @@
object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanGateway"
operation-name="testWithReturn">
<jmx:request-handler-advice-chain>
<bean class="org.springframework.integration.jmx.config.OperationInvokingOutboundGatewayTests$FooADvice" />
<bean class="org.springframework.integration.jmx.config.OperationInvokingOutboundGatewayTests.FooAdvice" />
</jmx:request-handler-advice-chain>
</jmx:operation-invoking-outbound-gateway>
<si:chain input-channel="jmxOutboundGatewayInsideChain" output-channel="withReplyChannelOutput">
<jmx:operation-invoking-outbound-gateway operation-name="testWithReturn"
<si:chain id="operationInvokingWithinChain" input-channel="jmxOutboundGatewayInsideChain" output-channel="withReplyChannelOutput">
<jmx:operation-invoking-outbound-gateway operation-name="testWithReturn" requires-reply="true"
object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanGateway"/>
</si:chain>
<jmx:operation-invoking-outbound-gateway request-channel="withNoReplyChannel"
object-name="org.springframework.integration.jmx.config:type=TestBean,name=testBeanGateway"
operation-name="test"/>
operation-name="test"
requires-reply="false"/>
<bean id="testBeanGateway" class="org.springframework.integration.jmx.config.TestBean"/>
</beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -17,6 +17,7 @@
package org.springframework.integration.jmx.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
@@ -27,10 +28,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jmx.OperationInvokingMessageHandler;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -61,6 +65,12 @@ public class OperationInvokingOutboundGatewayTests {
@Autowired
private TestBean testBean;
@Autowired
@Qualifier("operationInvokingWithinChain.handler")
private MessageHandler operationInvokingWithinChain;
private static volatile int adviceCalled;
@After
@@ -89,8 +99,14 @@ public class OperationInvokingOutboundGatewayTests {
assertEquals(3, testBean.messages.size());
}
@Test //INT-1029
@Test //INT-1029, INT-2822
public void testOutboundGatewayInsideChain() throws Exception {
List handlers = TestUtils.getPropertyValue(this.operationInvokingWithinChain, "handlers", List.class);
assertEquals(1, handlers.size());
Object handler = handlers.get(0);
assertTrue(handler instanceof OperationInvokingMessageHandler);
assertTrue(TestUtils.getPropertyValue(handler, "requiresReply", Boolean.class));
jmxOutboundGatewayInsideChain.send(MessageBuilder.withPayload("1").build());
assertEquals(1, ((List<?>) withReplyChannelOutput.receive().getPayload()).size());
jmxOutboundGatewayInsideChain.send(MessageBuilder.withPayload("2").build());
@@ -99,7 +115,7 @@ public class OperationInvokingOutboundGatewayTests {
assertEquals(3, ((List<?>) withReplyChannelOutput.receive().getPayload()).size());
}
public static class FooADvice extends AbstractRequestHandlerAdvice {
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -30,6 +30,7 @@ import org.w3c.dom.Element;
* The Abstract Parser for the JPA Outbound Gateways.
*
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.2
*
@@ -46,6 +47,7 @@ public abstract class AbstractJpaOutboundGatewayParser extends AbstractConsumerE
.genericBeanDefinition(JpaOutboundGatewayFactoryBean.class);
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaOutboundGatewayBuilder, gatewayElement, "reply-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(jpaOutboundGatewayBuilder, gatewayElement, "requires-reply");
final String replyChannel = gatewayElement.getAttribute("reply-channel");

View File

@@ -70,6 +70,8 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
private long replyTimeout;
private volatile boolean requiresReply = false;
private volatile String componentName;
/**
@@ -118,6 +120,10 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
this.replyTimeout = replyTimeout;
}
public void setRequiresReply(boolean requiresReply) {
this.requiresReply = requiresReply;
}
/**
* Sets the name of the handler component.
*
@@ -140,7 +146,8 @@ public class JpaOutboundGatewayFactoryBean extends AbstractFactoryBean<MessageHa
jpaOutboundGateway.setProducesReply(this.producesReply);
jpaOutboundGateway.setOutputChannel(this.outputChannel);
jpaOutboundGateway.setOrder(this.order);
jpaOutboundGateway.setSendTimeout(replyTimeout);
jpaOutboundGateway.setSendTimeout(this.replyTimeout);
jpaOutboundGateway.setRequiresReply(this.requiresReply);
jpaOutboundGateway.setComponentName(this.componentName);
if (this.adviceChain != null) {
jpaOutboundGateway.setAdviceChain(this.adviceChain);

View File

@@ -363,6 +363,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:attributeGroup name="commonUpdatingJpaAttributes">

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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. You may obtain a copy of the License at
@@ -13,18 +13,24 @@
package org.springframework.integration.jpa.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.After;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.ReplyRequiredException;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.jpa.core.JpaExecutor;
import org.springframework.integration.jpa.core.JpaOperations;
@@ -42,14 +48,12 @@ import org.springframework.integration.test.util.TestUtils;
* @since 2.2
*
*/
public class JpaOutboundGatewayParserTests {
public class JpaOutboundGatewayParserTests extends AbstractRequestHandlerAdvice {
private ConfigurableApplicationContext context;
private EventDrivenConsumer consumer;
private static volatile int adviceCalled;
@Test
public void testRetrievingJpaOutboundGatewayParser() throws Exception {
setUp("JpaOutboundGatewayParserTests.xml", getClass(), "retrievingJpaOutboundGateway");
@@ -69,6 +73,7 @@ public class JpaOutboundGatewayParserTests {
assertEquals(100, sendTimeout);
assertFalse(TestUtils.getPropertyValue(jpaOutboundGateway, "requiresReply", Boolean.class));
final JpaExecutor jpaExecutor = TestUtils.getPropertyValue(this.consumer, "handler.jpaExecutor", JpaExecutor.class);
@@ -87,7 +92,6 @@ public class JpaOutboundGatewayParserTests {
final Integer maxNumberOfResults = TestUtils.getPropertyValue(jpaExecutor, "maxNumberOfResults", Integer.class);
assertEquals(Integer.valueOf(55), maxNumberOfResults);
}
@Test
@@ -109,6 +113,8 @@ public class JpaOutboundGatewayParserTests {
assertEquals(100, sendTimeout);
assertFalse(TestUtils.getPropertyValue(jpaOutboundGateway, "requiresReply", Boolean.class));
final JpaExecutor jpaExecutor = TestUtils.getPropertyValue(this.consumer, "handler.jpaExecutor", JpaExecutor.class);
assertNotNull(jpaExecutor);
@@ -136,14 +142,22 @@ public class JpaOutboundGatewayParserTests {
}
@Test
public void advised() throws Exception {
public void advised() throws Throwable {
setUp("JpaOutboundGatewayParserTests.xml", getClass(), "advised");
MessageHandler jpaOutboundGateway = context.getBean("advised.handler", MessageHandler.class);
FooAdvice advice = context.getBean("jpaFooAdvice", FooAdvice.class);
assertTrue(AopUtils.isAopProxy(jpaOutboundGateway));
jpaOutboundGateway.handleMessage(new GenericMessage<String>("foo"));
assertEquals(1, adviceCalled);
try {
jpaOutboundGateway.handleMessage(new GenericMessage<String>("foo"));
fail("expected ReplyRequiredException");
}
catch (MessagingException e) {
assertTrue(e instanceof ReplyRequiredException);
}
Mockito.verify(advice).doInvoke(Mockito.any(ExecutionCallback.class), Mockito.any(Object.class), Mockito.any(Message.class));
}
@Test
@@ -168,11 +182,16 @@ public class JpaOutboundGatewayParserTests {
consumer = this.context.getBean(gatewayId, EventDrivenConsumer.class);
}
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
// Workaround for access to protected AbstractRequestHandlerAdvice.ExecutionCallback
return null;
}
public static class FooAdvice extends AbstractRequestHandlerAdvice {
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
adviceCalled++;
return null;
}

View File

@@ -23,7 +23,8 @@
max-number-of-results="55"
request-channel="in"
reply-channel="out"
reply-timeout="100"/>
reply-timeout="100"
requires-reply="false"/>
<int-jpa:updating-outbound-gateway id="updatingJpaOutboundGateway"
entity-manager-factory="entityManagerFactory"
@@ -34,7 +35,8 @@
order="2"
request-channel="in"
reply-channel="out"
reply-timeout="100"/>
reply-timeout="100"
requires-reply="false"/>
<int-jpa:updating-outbound-gateway id="advised"
entity-manager-factory="entityManagerFactory"
@@ -48,8 +50,14 @@
reply-timeout="100">
<int-jpa:transactional/>
<int-jpa:request-handler-advice-chain>
<bean class="org.springframework.integration.jpa.config.xml.JpaOutboundGatewayParserTests$FooAdvice"/>
<ref bean="jpaFooAdvice"/>
</int-jpa:request-handler-advice-chain>
</int-jpa:updating-outbound-gateway>
<bean id="jpaFooAdvice" class="org.mockito.Mockito" factory-method="spy">
<constructor-arg>
<bean class="org.springframework.integration.jpa.config.xml.JpaOutboundGatewayParserTests$FooAdvice"/>
</constructor-arg>
</bean>
</beans>

View File

@@ -101,6 +101,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -17,7 +17,9 @@
package org.springframework.integration.rmi.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
@@ -60,6 +62,7 @@ public class RmiOutboundGatewayParserTests {
"rmiOutboundGatewayParserTests.xml", this.getClass());
RmiOutboundGateway gateway = context.getBean("gateway.handler", RmiOutboundGateway.class);
assertEquals(23, TestUtils.getPropertyValue(gateway, "order"));
assertTrue(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
}
@Test
@@ -67,6 +70,9 @@ public class RmiOutboundGatewayParserTests {
ApplicationContext context = new ClassPathXmlApplicationContext(
"rmiOutboundGatewayParserTests.xml", this.getClass());
MessageChannel localChannel = (MessageChannel) context.getBean("advisedChannel");
RmiOutboundGateway gateway = context.getBean("advised.handler", RmiOutboundGateway.class);
assertFalse(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
localChannel.send(new GenericMessage<String>("test"));
Message<?> result = testChannel.receive(1000);
assertNotNull(result);

View File

@@ -23,6 +23,7 @@
<rmi:outbound-gateway id="advised"
request-channel="advisedChannel"
remote-channel="testChannel"
requires-reply="false"
host="localhost">
<rmi:request-handler-advice-chain>
<beans:bean class="org.springframework.integration.rmi.config.RmiOutboundGatewayParserTests$FooAdvice" />
@@ -30,7 +31,7 @@
</rmi:outbound-gateway>
<chain input-channel="rmiOutboundGatewayInsideChain">
<rmi:outbound-gateway remote-channel="testChannel" host="localhost"/>
<rmi:outbound-gateway remote-channel="testChannel" host="localhost" requires-reply="false"/>
</chain>

View File

@@ -432,6 +432,15 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -43,6 +43,7 @@
command-options="-P"
expression="payload"
order="2"
requires-reply="false"
/>
<int-sftp:outbound-gateway id="gateway3"
@@ -66,6 +67,7 @@
command="get"
command-options="-P"
expression="payload"
requires-reply="false"
order="2">
<int-sftp:request-handler-advice-chain>
<bean class="org.springframework.integration.sftp.config.SftpOutboundGatewayParserTests$FooAdvice" />

View File

@@ -73,6 +73,7 @@ public class SftpOutboundGatewayParserTests {
assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
assertEquals(new File("local-test-dir"), TestUtils.getPropertyValue(gateway, "localDirectory"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertTrue(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
assertNotNull(TestUtils.getPropertyValue(gateway, "filter"));
assertEquals(Command.LS, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
@@ -95,6 +96,7 @@ public class SftpOutboundGatewayParserTests {
assertEquals(new File("local-test-dir"), TestUtils.getPropertyValue(gateway, "localDirectory"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertEquals(Command.GET, TestUtils.getPropertyValue(gateway, "command"));
assertFalse(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);
assertTrue(options.contains(Option.PRESERVE_TIMESTAMP));

View File

@@ -83,6 +83,7 @@ public class WebServiceOutboundGatewayParser extends AbstractOutboundGatewayPars
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-empty-responses");
this.postProcessGateway(builder, element, parserContext);

View File

@@ -87,6 +87,16 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" type="xsd:string" use="optional" default="false">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'false' by default, otherwise a ReplyRequiredException will be thrown when
the underlying service returns a null value, or an empty String (if
'ignore-empty-responses' is 'true').
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="uri" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -139,7 +149,9 @@
<xsd:attribute name="ignore-empty-responses" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Indicates whether empty String response payloads should be ignored. The default is TRUE.
Indicates whether empty String response payloads should be considered as null. The default is TRUE.
See also 'requires-reply'. Note that when 'requires-reply' is 'true' the response is not actually 'ignored',
because it will cause a ReplyRequiredException to be thrown.
Set this to FALSE if you want to send empty String responses in reply Messages.
]]></xsd:documentation>
</xsd:annotation>
@@ -456,4 +468,4 @@ this list can also be simple patterns to be matched against the header names (e.
</xsd:attribute>
</xsd:complexType>
</xsd:schema>
</xsd:schema>

View File

@@ -17,13 +17,15 @@
package org.springframework.integration.ws.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.net.URI;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
@@ -72,6 +74,7 @@ public class WebServiceOutboundGatewayParserTests {
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
Object expected = context.getBean("outputChannel");
assertEquals(expected, accessor.getPropertyValue("outputChannel"));
Assert.assertEquals(Boolean.FALSE, accessor.getPropertyValue("requiresReply"));
@SuppressWarnings("unchecked")
List<String> requestHeaders = TestUtils.getPropertyValue(endpoint, "handler.headerMapper.requestHeaderNames", List.class);
@@ -96,18 +99,20 @@ public class WebServiceOutboundGatewayParserTests {
assertEquals(SimpleWebServiceOutboundGateway.class, gateway.getClass());
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.TRUE, accessor.getPropertyValue("ignoreEmptyResponses"));
Assert.assertEquals(Boolean.FALSE, accessor.getPropertyValue("requiresReply"));
}
@Test
public void simpleGatewayWithIgnoreEmptyResponses() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"simpleWebServiceOutboundGatewayParserTests.xml", this.getClass());
AbstractEndpoint endpoint = (AbstractEndpoint) context.getBean("gatewayWithIgnoreEmptyResponsesFalse");
AbstractEndpoint endpoint = (AbstractEndpoint) context.getBean("gatewayWithIgnoreEmptyResponsesFalseAndRequiresReplyTrue");
assertEquals(EventDrivenConsumer.class, endpoint.getClass());
Object gateway = new DirectFieldAccessor(endpoint).getPropertyValue("handler");
assertEquals(SimpleWebServiceOutboundGateway.class, gateway.getClass());
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
assertEquals(Boolean.FALSE, accessor.getPropertyValue("ignoreEmptyResponses"));
Assert.assertEquals(Boolean.TRUE, accessor.getPropertyValue("requiresReply"));
}
@Test

View File

@@ -31,10 +31,11 @@
mapped-request-headers="testRequest"
mapped-reply-headers="testReply"/>
<ws:outbound-gateway id="gatewayWithIgnoreEmptyResponsesFalse"
<ws:outbound-gateway id="gatewayWithIgnoreEmptyResponsesFalseAndRequiresReplyTrue"
request-channel="inputChannel"
uri="http://example.org"
ignore-empty-responses="false"/>
ignore-empty-responses="false"
requires-reply="true"/>
<ws:outbound-gateway id="gatewayWithDefaultSourceExtractor"
request-channel="inputChannel"

View File

@@ -272,5 +272,38 @@
For more information see <xref linkend="ip-headers"/>.
</para>
</section>
<section id="3.0-outbound-gateway-requires-reply">
<title>'requires-reply' Attribute for Outbound Gateways</title>
<para>
All Outbound Gateways (e.g. <code>&lt;jdbc:outbound-gateway/&gt;</code> or <code>&lt;jms:outbound-gateway/&gt;</code>)
are designed for 'request-reply' scenarios. A response is expected from the external service and
will be published to the <code>reply-channel</code>, or the <code>replyChannel</code> message header.
However, there are some cases where the external system might not always return a
result, e.g. a <code>&lt;jdbc:outbound-gateway/&gt;</code>, when a SELECT ends with an empty <interfacename>ResultSet</interfacename>
or, say, a Web Service is One-Way. An option is therefore needed to configure whether or not a
<emphasis>reply</emphasis> is required.
For this purpose, the <emphasis>requires-reply</emphasis> attribute has been introduced for Outbound Gateway components.
In most cases, the default value for <emphasis>requires-reply</emphasis> is <code>true</code> and, if there is not any result,
a <classname>ReplyRequiredException</classname> will be thrown. Changing the value to <code>false</code>
means that, if an external service doesn't return anything, the message-flow will end at that point,
similar to an Outbound Channel Adapter.
</para>
<note>
The WebService outbound gateway has an additional attribute <code>ignore-empty-responses</code>; this is used to
treat an empty String response as if no response was received. It is true by default but can be set to false to
allow the application to receive an empty String in the reply message payload. When the attribute is true an empty
string is treated as no response for the purposes of the <emphasis>requires-reply</emphasis> attribute.
<emphasis>requires-reply</emphasis> is false by default for the WebService outbound gateway.
</note>
<para>
Note, the <code>requiresReply</code> property was previously present in the <classname>AbstractReplyProducingMessageHandler</classname>
but set to <code>false</code>, and there wasn't any way to configure it on Outbound Gateways using the XML namespace.
</para>
<important>
Previously, a gateway receiving no reply would silently end the flow (with a DEBUG log message); with this change an
exception will now be thrown by default by most gateways. To revert to the previous behavior,
set <code>requires-reply</code> to false.
</important>
</section>
</section>
</chapter>