INT-4100: Improve RequestReplyExchanger
JIRA: https://jira.spring.io/browse/INT-4100 * Add `throws MessagingException` to the `RequestReplyExchanger` to make it fully messaging contract based * Add test to the `ServiceActivatorDefaultFrameworkMethodTests` to demonstrate how many info is in the stack trace after `RequestReplyExchanger` * Document how to be with the `throws` clause in custom gateways * Fix `boolean` attributes in the `spring-integration-5.0.xsd` * Fix `AbstractChannelAdapterParser` do not populate endpoint attributes if component is nested * Fix typo in the `AmqpOutboundChannelAdapterParserTests` * Increase timeouts in the `MixedDispatcherConfigurationScenarioTests` Reinstate `auto-startup` to none by default. Not all components expects `true` by default in their initial state Also https://jira.spring.io/browse/INT-3432 Document the behavior of the `throws` clause on gateway method Remove unrelated `bridge` id from the `<chain>` Add What's new about `RequestReplyExchanger` changes Doc Polishing.
This commit is contained in:
committed by
Gary Russell
parent
10ce68d3e3
commit
8da89c7cce
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -65,20 +65,22 @@ public abstract class AbstractChannelAdapterParser extends AbstractBeanDefinitio
|
||||
}
|
||||
AbstractBeanDefinition beanDefinition = doParse(element, parserContext, channelName);
|
||||
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
|
||||
String autoStartup = element.getAttribute(IntegrationNamespaceUtils.AUTO_STARTUP);
|
||||
if (StringUtils.hasText(autoStartup)) {
|
||||
propertyValues.add("autoStartup", new TypedStringValue(autoStartup));
|
||||
}
|
||||
String phase = element.getAttribute(IntegrationNamespaceUtils.PHASE);
|
||||
if (StringUtils.hasText(phase)) {
|
||||
propertyValues.add("phase", new TypedStringValue(phase));
|
||||
}
|
||||
String role = element.getAttribute(IntegrationNamespaceUtils.ROLE);
|
||||
if (StringUtils.hasText(role)) {
|
||||
if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
|
||||
parserContext.getReaderContext().error("When using 'role', 'id' is required", element);
|
||||
if (!parserContext.isNested()) {
|
||||
String autoStartup = element.getAttribute(IntegrationNamespaceUtils.AUTO_STARTUP);
|
||||
if (StringUtils.hasText(autoStartup)) {
|
||||
propertyValues.add("autoStartup", new TypedStringValue(autoStartup));
|
||||
}
|
||||
String phase = element.getAttribute(IntegrationNamespaceUtils.PHASE);
|
||||
if (StringUtils.hasText(phase)) {
|
||||
propertyValues.add("phase", new TypedStringValue(phase));
|
||||
}
|
||||
String role = element.getAttribute(IntegrationNamespaceUtils.ROLE);
|
||||
if (StringUtils.hasText(role)) {
|
||||
if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
|
||||
parserContext.getReaderContext().error("When using 'role', 'id' is required", element);
|
||||
}
|
||||
IntegrationNamespaceUtils.putLifecycleInRole(role, element.getAttribute(ID_ATTRIBUTE), parserContext);
|
||||
}
|
||||
IntegrationNamespaceUtils.putLifecycleInRole(role, element.getAttribute(ID_ATTRIBUTE), parserContext);
|
||||
}
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2017 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.gateway;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
|
||||
/**
|
||||
* Interface for a request/reply Message exchange. This will be used as a default
|
||||
@@ -24,11 +25,13 @@ import org.springframework.messaging.Message;
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RequestReplyExchanger {
|
||||
|
||||
Message<?> exchange(Message<?> request);
|
||||
Message<?> exchange(Message<?> request) throws MessagingException;
|
||||
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="fixed-subscriber" default="false" type="xsd:boolean">
|
||||
<xsd:attribute name="fixed-subscriber" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
When true, only one subscriber is allowed; the subscriber must be available at context initialization time,
|
||||
@@ -192,6 +192,9 @@
|
||||
'message-converter' is not allowed. Default: false.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
@@ -1859,7 +1862,11 @@
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basePollerType">
|
||||
<xsd:attribute name="id" type="xsd:string" />
|
||||
<xsd:attribute name="default" type="xsd:boolean" default="false" />
|
||||
<xsd:attribute name="default" default="false">
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
@@ -2904,12 +2911,15 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="throw-exception-on-rejection" type="xsd:string">
|
||||
<xsd:attribute name="throw-exception-on-rejection" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Throw an exception if the filter rejects the message (default false).
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
@@ -3632,7 +3642,7 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expire-groups-upon-completion" type="xsd:string">
|
||||
<xsd:attribute name="expire-groups-upon-completion" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Boolean flag specifying if MessageGroup should be removed once completed. Useful for
|
||||
@@ -3640,6 +3650,9 @@
|
||||
is the same as the completed MessageGroup will be discarded. Default is 'false'
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
@@ -3769,7 +3782,7 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expire-groups-upon-timeout" type="xsd:string">
|
||||
<xsd:attribute name="expire-groups-upon-timeout">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Boolean flag specifying, if a group is completed due to timeout (reaper or
|
||||
@@ -3779,6 +3792,9 @@
|
||||
resequencer.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="lock-registry" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
@@ -4977,12 +4993,15 @@ endpoint itself is a Polling Consumer for a channel with a queue.
|
||||
</xsd:attributeGroup>
|
||||
|
||||
<xsd:attributeGroup name="smartLifeCycleAttributeGroup">
|
||||
<xsd:attribute name="auto-startup" type="xsd:string">
|
||||
<xsd:attribute name="auto-startup">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Boolean value indicating whether this endpoint should start automatically.
|
||||
Boolean value indicating whether this endpoint should start automatically.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="phase" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
|
||||
@@ -326,10 +326,10 @@ public class MixedDispatcherConfigurationScenarioTests {
|
||||
executor.execute(messageSenderTask);
|
||||
}
|
||||
start.countDown();
|
||||
assertTrue(allDone.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(allDone.await(10, TimeUnit.SECONDS));
|
||||
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(5, TimeUnit.SECONDS);
|
||||
executor.awaitTermination(10, TimeUnit.SECONDS);
|
||||
|
||||
assertTrue("not all messages were accepted", failed.get());
|
||||
verify(handlerA, times(14)).handleMessage(message);
|
||||
|
||||
@@ -42,9 +42,9 @@
|
||||
|
||||
<gateway id="gateway" default-request-channel="requestChannel" default-reply-channel="replyChannel"/>
|
||||
|
||||
<channel id="requestChannel"/>
|
||||
|
||||
<bridge id="bridge" input-channel="requestChannel" output-channel="replyChannel"/>
|
||||
<chain input-channel="requestChannel" output-channel="replyChannel">
|
||||
<transformer expression="payload == 'foo' ? T(org.springframework.integration.handler.ServiceActivatorDefaultFrameworkMethodTests).throwIllegalStateException('Wrong payload') : payload"/>
|
||||
</chain>
|
||||
|
||||
<channel id="replyChannel">
|
||||
<queue/>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.integration.handler;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -41,10 +42,12 @@ import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.MessageTransformationException;
|
||||
import org.springframework.integration.util.StackTraceUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.ErrorMessage;
|
||||
@@ -59,6 +62,7 @@ import org.springframework.util.concurrent.SettableListenableFuture;
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 2.0.1
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@@ -107,8 +111,22 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.gatewayTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,bridge,replyChannel",
|
||||
assertEquals("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,replyChannel",
|
||||
reply.getHeaders().get("history").toString());
|
||||
|
||||
message = MessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build();
|
||||
try {
|
||||
this.gatewayTestInputChannel.send(message);
|
||||
fail("Exception expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, instanceOf(MessageHandlingException.class));
|
||||
assertThat(e.getCause(), instanceOf(MessageTransformationException.class));
|
||||
assertThat(e.getCause().getCause(), instanceOf(MessageHandlingException.class));
|
||||
assertThat(e.getCause().getCause().getCause(), instanceOf(java.lang.IllegalStateException.class));
|
||||
assertThat(e.getMessage(), containsString("Expression evaluation failed"));
|
||||
assertThat(e.getMessage(), containsString("Wrong payload"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -258,6 +276,10 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
|
||||
assertEquals("test", ((MessagingException) error.getPayload()).getFailedMessage().getPayload());
|
||||
}
|
||||
|
||||
public static void throwIllegalStateException(String message) {
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestReplyingMessageHandler extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user