Do not block by default (#8580)

Currently, many timeouts in the project are like `-1` or other negative value
with a meaning to wait indefinitely.

According to distributed systems design and bad demo developing experience
it is not OK to block forever.

* Rework most of the timeouts in the framework to be `30` seconds.
Only one remained as `1` seconds is a `PollingConsumer` where it is
better to not block even for those 30 seconds when no messages in the queue,
but let the polling task be rescheduled.
* Remove the `MessagingGatewaySupport.replyTimeout` propagation down to the
`PollingConsumer` correlator where it was a `-1` before and blocked
the polling thread on the `Queue.poll()`.
This fixed the problem with a single thread in a pool for auto-configured `TaskScheduler`.
Now with 1 seconds wait time we are able to switch to other scheduled tasks
even with only 1 thread in the pool
This commit is contained in:
Artem Bilan
2023-03-21 17:43:00 -04:00
committed by GitHub
parent fcb06bac61
commit 1bec420fd1
58 changed files with 284 additions and 340 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -61,7 +61,7 @@ public @interface Aggregator {
/**
* Specify the maximum amount of time in milliseconds to wait when sending a reply
* {@link org.springframework.messaging.Message} to the {@link #outputChannel()}.
* Defaults to {@code -1} - blocking indefinitely.
* Defaults to {@code 30} seconds.
* It is applied only if the output channel has some 'sending' limitations, e.g.
* {@link org.springframework.integration.channel.QueueChannel} with
* a fixed 'capacity' and is currently full.
@@ -78,7 +78,7 @@ public @interface Aggregator {
* or {@code replyChannel} from message headers. Messages are expired when their containing
* {@link org.springframework.integration.store.MessageGroup} expires. One of the ways of expiring MessageGroups
* is by configuring a {@link org.springframework.integration.store.MessageGroupStoreReaper}.
* However MessageGroups can alternatively be expired by simply calling
* However, MessageGroups can alternatively be expired by simply calling
* {@code MessageGroupStore.expireMessageGroup(groupId)}. That could be accomplished via a ControlBus operation
* or by simply invoking that method if you have a reference to the
* {@link org.springframework.integration.store.MessageGroupStore} instance.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -100,7 +100,7 @@ public @interface Filter {
/**
* Specify the maximum amount of time in milliseconds to wait when sending a reply
* {@link org.springframework.messaging.Message} to the {@link #outputChannel()}.
* Defaults to {@code -1} - blocking indefinitely.
* Defaults to {@code 30} seconds.
* It is applied only if the output channel has some 'sending' limitations, e.g.
* {@link org.springframework.integration.channel.QueueChannel} with
* fixed a 'capacity'. In this case a {@link org.springframework.messaging.MessageDeliveryException} is thrown.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -22,6 +22,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.integration.context.IntegrationContextUtils;
/**
* Indicates that an interface method is capable of mapping its parameters
* to a message or message payload. These method-level annotations are detected
@@ -82,7 +84,7 @@ public @interface Gateway {
* @return the timeout.
* @see #requestTimeoutExpression()
*/
long requestTimeout() default Long.MIN_VALUE;
long requestTimeout() default IntegrationContextUtils.DEFAULT_TIMEOUT;
/**
* Specify a SpEL Expression to determine the timeout (ms) when sending to the request
@@ -101,10 +103,10 @@ public @interface Gateway {
* @return the timeout.
* @see #replyTimeoutExpression()
*/
long replyTimeout() default Long.MIN_VALUE;
long replyTimeout() default IntegrationContextUtils.DEFAULT_TIMEOUT;
/**
* Specify a SpEL Expression to determine the the time (ms) that the thread sending
* Specify a SpEL Expression to determine the time (ms) that the thread sending
* the request will wait for a reply. The timer starts when the thread returns to the
* gateway, not when the request message is sent. Overrides the encompassing gateway's
* default reply timeout. Overrides {@link #replyTimeout()}.

View File

@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.integration.context.IntegrationContextUtils;
/**
* A stereotype annotation to provide an Integration Messaging Gateway Proxy
@@ -103,17 +104,17 @@ public @interface MessagingGateway {
* See {@link Gateway#requestTimeout()} for per-method configuration.
* @return the suggested timeout in milliseconds, if any
*/
String defaultRequestTimeout() default "-9223372036854775808";
String defaultRequestTimeout() default IntegrationContextUtils.DEFAULT_TIMEOUT_STRING;
/**
* Allows to specify how long this gateway will wait for the reply {@code Message}
* before returning. By default, it will wait indefinitely. {@code null} is returned if
* the gateway times out. Value is specified in milliseconds; it can be a simple long
* before returning. The {@code null} is returned if the gateway times out.
* Value is specified in milliseconds; it can be a simple long
* value or a SpEL expression; array variable #args is available.
* See {@link Gateway#replyTimeout()} for per-method configuration.
* @return the suggested timeout in milliseconds, if any
*/
String defaultReplyTimeout() default "-9223372036854775808";
String defaultReplyTimeout() default IntegrationContextUtils.DEFAULT_TIMEOUT_STRING;
/**
* Provide a reference to an implementation of {@link java.util.concurrent.Executor}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -123,7 +123,7 @@ public @interface Router {
/**
* Specify the maximum amount of time in milliseconds to wait when sending a reply
* {@link org.springframework.messaging.Message} to the {@code outputChannel}.
* Defaults to {@code -1} - blocking indefinitely.
* Defaults to {@code 30} seconds.
* It is applied only if the output channel has some 'sending' limitations, e.g.
* {@link org.springframework.integration.channel.QueueChannel} with
* fixed a 'capacity'. In this case a {@link org.springframework.messaging.MessageDeliveryException} is thrown.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -86,7 +86,7 @@ public @interface ServiceActivator {
/**
* Specify the maximum amount of time in milliseconds to wait when sending a reply
* {@link org.springframework.messaging.Message} to the {@code outputChannel}.
* Defaults to {@code -1} - blocking indefinitely.
* Defaults to {@code 30} seconds.
* It is applied only if the output channel has some 'sending' limitations, e.g.
* {@link org.springframework.integration.channel.QueueChannel} with
* fixed a 'capacity'. In this case a {@link org.springframework.messaging.MessageDeliveryException} is thrown.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -88,7 +88,7 @@ public @interface Splitter {
/**
* Specify the maximum amount of time in milliseconds to wait when sending a reply
* {@link org.springframework.messaging.Message} to the {@code outputChannel}.
* Defaults to {@code -1} - blocking indefinitely.
* Defaults to {@code 30} seconds.
* It is applied only if the output channel has some 'sending' limitations, e.g.
* {@link org.springframework.integration.channel.QueueChannel} with
* fixed a 'capacity'. In this case a {@link org.springframework.messaging.MessageDeliveryException} is thrown.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -64,7 +64,7 @@ public @interface Transformer {
/**
* Specify the maximum amount of time in milliseconds to wait when sending a reply
* {@link org.springframework.messaging.Message} to the {@code outputChannel}.
* Defaults to {@code -1} - blocking indefinitely.
* Defaults to {@code 30} seconds.
* It is applied only if the output channel has some 'sending' limitations, e.g.
* {@link org.springframework.integration.channel.QueueChannel} with
* fixed a 'capacity'. In this case a {@link org.springframework.messaging.MessageDeliveryException} is thrown.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 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.
@@ -50,14 +50,14 @@ public class DefaultRouterParser extends AbstractDelegatingConsumerEndpointParse
protected void postProcess(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
List<Element> mappingElements = DomUtils.getChildElementsByTagName(element, "mapping");
if (!CollectionUtils.isEmpty(mappingElements)) {
ManagedMap<String, String> channelMappings = new ManagedMap<String, String>();
ManagedMap<String, String> channelMappings = new ManagedMap<>();
for (Element mappingElement : mappingElements) {
channelMappings.put(mappingElement.getAttribute("value"), mappingElement.getAttribute("channel"));
}
builder.addPropertyValue("channelMappings", channelMappings);
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "default-output-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "resolution-required");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "apply-sequence");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-send-failures");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -101,6 +101,18 @@ public abstract class IntegrationContextUtils {
public static final String LIST_MESSAGE_HANDLER_FACTORY_BEAN_NAME = "integrationListMessageHandlerMethodFactory";
/**
* The default timeout for blocking operations like send and receive messages.
* @since 6.1
*/
public static final long DEFAULT_TIMEOUT = 30000L;
/**
* A string representation for {@link #DEFAULT_TIMEOUT}, e.g. for annotation attributes.
* @since 6.1
*/
public static final String DEFAULT_TIMEOUT_STRING = "" + DEFAULT_TIMEOUT;
/**
* @param beanFactory BeanFactory for lookup, must not be null.
* @return The {@link MetadataStore} bean whose name is "metadataStore".

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 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.
@@ -145,7 +145,7 @@ public class GatewayProxySpec {
/**
* Allows to specify how long this gateway will wait for the reply {@code Message}
* before returning. By default, it will wait indefinitely. {@code null} is returned if
* before returning. By default, it will wait 30 seconds. {@code null} is returned if
* the gateway times out. Value is specified in milliseconds.
* @param replyTimeout the timeout for replies in milliseconds.
* @return current {@link GatewayProxySpec}.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 the original author or authors.
* Copyright 2016-2023 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.
@@ -50,7 +50,7 @@ public class ScatterGatherSpec extends ConsumerEndpointSpec<ScatterGatherSpec, S
* Specify a timeout (in milliseconds) for the
* {@link org.springframework.messaging.PollableChannel#receive(long)} operation
* to wait for gathering results to output.
* Defaults to {@code -1} - to wait indefinitely.
* Defaults to {@code 30} seconds.
* @param gatherTimeout the {@link org.springframework.messaging.PollableChannel} receive timeout.
* @return the current {@link ScatterGatherSpec} instance.
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -782,14 +782,9 @@ public class GatewayProxyFactoryBean<T> extends AbstractEndpoint
Expression requestTimeout = this.defaultRequestTimeout;
if (gatewayAnnotation != null) {
/*
* INT-2636 Unspecified annotation attributes should not
* override the default values supplied by explicit configuration.
* There is a small risk that someone has used Long.MIN_VALUE explicitly
* to indicate an indefinite timeout on a gateway method and that will
* no longer work as expected; they will need to use, say, -1 instead.
*/
if (requestTimeout == null || gatewayAnnotation.requestTimeout() != Long.MIN_VALUE) {
if (requestTimeout == null ||
gatewayAnnotation.requestTimeout() != IntegrationContextUtils.DEFAULT_TIMEOUT) {
requestTimeout = new ValueExpression<>(gatewayAnnotation.requestTimeout());
}
if (StringUtils.hasText(gatewayAnnotation.requestTimeoutExpression())) {
@@ -813,14 +808,7 @@ public class GatewayProxyFactoryBean<T> extends AbstractEndpoint
Expression replyTimeout = this.defaultReplyTimeout;
if (gatewayAnnotation != null) {
/*
* INT-2636 Unspecified annotation attributes should not
* override the default values supplied by explicit configuration.
* There is a small risk that someone has used Long.MIN_VALUE explicitly
* to indicate an indefinite timeout on a gateway method and that will
* no longer work as expected; they will need to use, say, -1 instead.
*/
if (replyTimeout == null || gatewayAnnotation.replyTimeout() != Long.MIN_VALUE) {
if (replyTimeout == null || gatewayAnnotation.replyTimeout() != IntegrationContextUtils.DEFAULT_TIMEOUT) {
replyTimeout = new ValueExpression<>(gatewayAnnotation.replyTimeout());
}
if (StringUtils.hasText(gatewayAnnotation.replyTimeoutExpression())) {
@@ -968,31 +956,27 @@ public class GatewayProxyFactoryBean<T> extends AbstractEndpoint
private void timeouts(@Nullable Expression requestTimeout, @Nullable Expression replyTimeout,
GatewayMethodInboundMessageMapper messageMapper, MethodInvocationGateway gateway) {
if (requestTimeout == null) {
gateway.setRequestTimeout(-1);
}
else if (requestTimeout instanceof ValueExpression) {
Long timeout = requestTimeout.getValue(Long.class);
if (timeout != null) {
gateway.setRequestTimeout(timeout);
if (requestTimeout != null) {
if (requestTimeout instanceof ValueExpression) {
Long timeout = requestTimeout.getValue(Long.class);
if (timeout != null) {
gateway.setRequestTimeout(timeout);
}
}
}
else {
messageMapper.setSendTimeoutExpression(requestTimeout);
}
if (replyTimeout == null) {
gateway.setReplyTimeout(-1);
}
else if (replyTimeout instanceof ValueExpression) {
Long timeout = replyTimeout.getValue(Long.class);
if (timeout != null) {
gateway.setReplyTimeout(timeout);
else {
messageMapper.setSendTimeoutExpression(requestTimeout);
}
}
else {
messageMapper.setReplyTimeoutExpression(replyTimeout);
}
if (replyTimeout != null) {
if (replyTimeout instanceof ValueExpression) {
Long timeout = replyTimeout.getValue(Long.class);
if (timeout != null) {
gateway.setReplyTimeout(timeout);
}
}
else {
messageMapper.setReplyTimeoutExpression(replyTimeout);
}
gateway.setReceiveTimeoutExpression(replyTimeout);
}
}

View File

@@ -33,6 +33,7 @@ import org.springframework.integration.IntegrationPattern;
import org.springframework.integration.IntegrationPatternType;
import org.springframework.integration.MessageTimeoutException;
import org.springframework.integration.channel.ReactiveStreamsSubscribableChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.endpoint.EventDrivenConsumer;
@@ -91,8 +92,6 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
implements org.springframework.integration.support.management.TrackableComponent,
IntegrationInboundManagement, IntegrationPattern {
private static final long DEFAULT_TIMEOUT = 1000L;
protected final ConvertingMessagingTemplate messagingTemplate; // NOSONAR
private final SimpleMessageConverter messageConverter = new SimpleMessageConverter();
@@ -122,7 +121,7 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
private String errorChannelName;
private long replyTimeout = DEFAULT_TIMEOUT;
private long replyTimeout = IntegrationContextUtils.DEFAULT_TIMEOUT;
private InboundMessageMapper<Object> requestMapper = new DefaultRequestMapper();
@@ -165,7 +164,7 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
public MessagingGatewaySupport(boolean errorOnTimeout) {
ConvertingMessagingTemplate template = new ConvertingMessagingTemplate();
template.setMessageConverter(this.messageConverter);
template.setSendTimeout(DEFAULT_TIMEOUT);
template.setSendTimeout(IntegrationContextUtils.DEFAULT_TIMEOUT);
template.setReceiveTimeout(this.replyTimeout);
this.messagingTemplate = template;
this.errorOnTimeout = errorOnTimeout;
@@ -908,9 +907,7 @@ public abstract class MessagingGatewaySupport extends AbstractEndpoint
correlator = new EventDrivenConsumer((SubscribableChannel) replyChan, handler);
}
else if (replyChan instanceof PollableChannel) {
PollingConsumer endpoint = new PollingConsumer((PollableChannel) replyChan, handler);
endpoint.setReceiveTimeout(this.replyTimeout);
correlator = endpoint;
correlator = new PollingConsumer((PollableChannel) replyChan, handler);
}
else if (replyChan instanceof ReactiveStreamsSubscribableChannel) {
correlator = new ReactiveStreamsConsumer(replyChan, (Subscriber<Message<?>>) handler);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2022 the original author or authors.
* Copyright 2014-2023 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.
@@ -87,6 +87,10 @@ public abstract class AbstractMessageProducingHandler extends AbstractMessageHan
private boolean noHeadersPropagation;
{
this.messagingTemplate.setSendTimeout(IntegrationContextUtils.DEFAULT_TIMEOUT);
}
/**
* Set the timeout for sending reply Messages.
* @param sendTimeout The send timeout.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.integration.IntegrationPatternType;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.support.management.IntegrationManagedResource;
@@ -47,6 +48,8 @@ import org.springframework.util.Assert;
@IntegrationManagedResource
public abstract class AbstractMessageRouter extends AbstractMessageHandler implements MessageRouter {
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private volatile MessageChannel defaultOutputChannel;
private volatile String defaultOutputChannelName;
@@ -55,8 +58,9 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler imple
private volatile boolean applySequence;
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
{
this.messagingTemplate.setSendTimeout(IntegrationContextUtils.DEFAULT_TIMEOUT);
}
/**
* Set the default channel where Messages should be sent if channel resolution
@@ -96,7 +100,7 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler imple
/**
* Set the timeout for sending a message to the resolved channel.
* By default, there is no timeout, meaning the send will block indefinitely.
* By default, 30 seconds timeout.
* @param timeout The timeout.
* @since 4.3
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2020 the original author or authors.
* Copyright 2014-2023 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.
@@ -67,7 +67,7 @@ public class ScatterGatherHandler extends AbstractReplyProducingMessageHandler i
private String errorChannelName = IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME;
private long gatherTimeout = -1;
private long gatherTimeout = IntegrationContextUtils.DEFAULT_TIMEOUT;
private AbstractEndpoint gatherEndpoint;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -187,7 +187,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
/**
* Set the timeout value for sending request messages. If not explicitly configured,
* the default is one second.
* the default is 30 seconds.
* @param requestTimeout the timeout value in milliseconds. Must not be null.
*/
public void setRequestTimeout(Long requestTimeout) {
@@ -197,7 +197,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
/**
* Set the timeout value for receiving reply messages. If not explicitly configured,
* the default is one second.
* the default is 30 seconds.
* @param replyTimeout the timeout value in milliseconds. Must not be null.
*/
public void setReplyTimeout(Long replyTimeout) {

View File

@@ -687,24 +687,24 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-timeout" type="xsd:string">
<xsd:attribute name="request-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Provides the amount of time dispatcher would wait to send a message.
This timeout would only apply if there is a potential to block in the send call.
For example if this gateway is hooked up to a Queue channel. 
For example if this gateway is hooked up to a Queue channel.
Value is specified in milliseconds.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:attribute name="reply-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Allows you to specify how long this gateway will wait for the reply message
before returning. By default it will wait indefinitely. 'null' is returned
before returning. 'null' is returned
if the gateway times out.
Value is specified in milliseconds.
]]>
@@ -779,26 +779,25 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-request-timeout" type="xsd:string">
<xsd:attribute name="default-request-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Provides the amount of time dispatcher would wait to send a message.
This timeout would only apply if there is a potential to block in the send call.
For example if this gateway is hooked up to a Queue channel. 
For example if this gateway is hooked up to a Queue channel.
Value is specified in milliseconds; it can be a simple long value or a SpEL
expression; array variable #args is available.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-reply-timeout" type="xsd:string">
<xsd:attribute name="default-reply-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Allows you to specify how long this gateway will wait for the reply message
before returning. By default it will wait indefinitely. 'null' is returned
if the gateway times out.
before returning. The 'null' is returned if the gateway times out.
Value is specified in milliseconds; it can be a simple long value or a SpEL
expression; array variable #args is available.
Also used for receive-only operations as the receive timeout.
@@ -910,13 +909,13 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="request-timeout" type="xsd:string">
<xsd:attribute name="request-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Provides the amount of time dispatcher would wait to send a message.
This timeout would only apply if there is a potential to block in the send call.
For example if this gateway is hooked up to a Queue channel. 
For example if this gateway is hooked up to a Queue channel.
Value is specified in milliseconds; it can be a simple long value or a SpEL
expression; array variable '#args' is available.
]]>
@@ -932,13 +931,12 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:attribute name="reply-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Specifies how long this gateway will wait for the reply message
before returning. By default it will wait indefinitely. 'null' is returned
if the gateway times out.
before returning. The 'null' is returned if the gateway times out.
Value is specified in milliseconds; it can be a simple long value or a SpEL
expression; array variable '#args' is available.
Also used for receive-only operations as the receive timeout.
@@ -993,14 +991,13 @@
</xsd:sequence>
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes"/>
<xsd:attributeGroup ref="channelAdapterAttributes"/>
<xsd:attribute name="send-timeout" type="xsd:string">
<xsd:attribute name="send-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
Maximum amount of time in milliseconds to wait when sending a message to the channel
if such channel may block.
For example, a Queue Channel can block until space is available if its maximum capacity
has been reached.
Defaults to '-1' - blocking indefinitely.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -3640,8 +3637,7 @@
Specify the maximum amount of time in milliseconds to wait
when sending Messages to the target MessageChannels if blocking
is possible (e.g. a bounded queue channel that is currently full).
By default the send will block indefinitely.
Synonym for 'timeout' - only one can be supplied.
Defaults to 30 seconds to not block the process indefinitely.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -4775,11 +4771,11 @@ The list of component name patterns you want to track (e.g., tracked-components
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="gather-timeout" type="xsd:string">
<xsd:attribute name="gather-timeout" type="xsd:string" default="30000">
<xsd:annotation>
<xsd:documentation>
Allows to specify how long the Scatter-Gather will wait for reply Messages for gathering.
By default it will wait indefinitely. Value is specified in milliseconds.
Value is specified in milliseconds.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -4789,8 +4785,8 @@ The list of component name patterns you want to track (e.g., tracked-components
Specify whether the Scatter-Gather must return a non-null value. This value is
'true' by default, hence a ReplyRequiredException will be thrown when
the underlying aggregator returns a null value after 'gather-timeout'.
Note, if 'null' is a possibility, the 'gather-timeout' should be specified to avoid an indefinite
wait.
Note, if 'null' is a possibility, the 'gather-timeout' should not be specified as negative
to avoid an indefinite wait.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -4807,7 +4803,7 @@ The list of component name patterns you want to track (e.g., tracked-components
<xsd:attribute name="endpoint" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
Consumer Endpoint name(s) or patterns. To specify more than one name(pattern) use ',' 
Consumer Endpoint name(s) or patterns. To specify more than one name(pattern) use ','
(e.g. endpoint="xxx, xxx*, *xxx, *xxx*, xxx*yyy").
The endpoint 'id' is used to retrieve the target endpoint's
'MessageHandler' bean (using its '.handler' suffix),
@@ -5070,10 +5066,11 @@ The list of component name patterns you want to track (e.g., tracked-components
<xsd:annotation>
<xsd:documentation>
Specify the maximum amount of time in milliseconds to wait when sending a reply
Message to the output channel. Defaults to '-1' - blocking indefinitely.
Message to the output channel.
It is applied only if the output channel has some 'sending' limitations, e.g. QueueChannel with
fixed a 'capacity'. In this case a MessageDeliveryException is thrown. The 'send-timeout'
is ignored in case of AbstractSubscribableChannel implementations.
Defaults to 30 seconds to not block the process indefinitely.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -16,19 +16,14 @@
package org.springframework.integration.aggregator;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.internal.stubbing.answers.ThrowsException;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.MessageGroupStore;
@@ -42,50 +37,53 @@ import org.springframework.messaging.MessageHandlingException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Iwein Fuld
* @author Dave Syer
* @author Artme Bilan
*/
@RunWith(MockitoJUnitRunner.class)
public class CorrelatingMessageHandlerTests {
private AggregatingMessageHandler handler;
@Mock
private CorrelationStrategy correlationStrategy;
private final ReleaseStrategy ReleaseStrategy = new SequenceSizeReleaseStrategy();
@Mock
private MessageGroupProcessor processor;
@Mock
private MessageChannel outputChannel;
private final MessageGroupStore store = new SimpleMessageStore();
@Before
@BeforeEach
public void initializeSubject() {
correlationStrategy = mock(CorrelationStrategy.class);
processor = mock(MessageGroupProcessor.class);
outputChannel = mock(MessageChannel.class);
handler = new AggregatingMessageHandler(processor, store, correlationStrategy, ReleaseStrategy);
handler.setOutputChannel(outputChannel);
}
@Test
public void bufferCompletesNormally() throws Exception {
public void bufferCompletesNormally() {
String correlationKey = "key";
Message<?> message1 = testMessage(correlationKey, 1, 2);
Message<?> message2 = testMessage(correlationKey, 2, 2);
when(correlationStrategy.getCorrelationKey(isA(Message.class))).thenReturn(correlationKey);
when(processor.processMessageGroup(any(MessageGroup.class))).thenReturn(MessageBuilder.withPayload("grouped").build());
when(outputChannel.send(any(Message.class))).thenReturn(true);
when(processor.processMessageGroup(any(MessageGroup.class)))
.thenReturn(MessageBuilder.withPayload("grouped").build());
when(outputChannel.send(any(Message.class), eq(30000L))).thenReturn(true);
handler.handleMessage(message1);
@@ -135,21 +133,19 @@ public class CorrelatingMessageHandlerTests {
String correlationKey = "key";
final Message<?> message1 = testMessage(correlationKey, 1, 2);
final Message<?> message2 = testMessage(correlationKey, 2, 2);
final List<Message<?>> storedMessages = new ArrayList<Message<?>>();
final CountDownLatch bothMessagesHandled = new CountDownLatch(2);
when(correlationStrategy.getCorrelationKey(isA(Message.class))).thenReturn(correlationKey);
when(processor.processMessageGroup(any(MessageGroup.class))).thenReturn(MessageBuilder.withPayload("grouped").build());
when(outputChannel.send(any(Message.class))).thenReturn(true);
when(processor.processMessageGroup(any(MessageGroup.class)))
.thenReturn(MessageBuilder.withPayload("grouped").build());
when(outputChannel.send(any(Message.class), eq(30000L))).thenReturn(true);
handler.handleMessage(message1);
bothMessagesHandled.countDown();
storedMessages.add(message1);
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.submit(() -> {
handler.handleMessage(message2);
storedMessages.add(message2);
bothMessagesHandled.countDown();
});
@@ -160,7 +156,7 @@ public class CorrelatingMessageHandlerTests {
}
@Test
public void testNullCorrelationKey() throws Exception {
public void testNullCorrelationKey() {
final Message<?> message1 = MessageBuilder.withPayload("foo").build();
when(correlationStrategy.getCorrelationKey(isA(Message.class))).thenReturn(null);
try {
@@ -169,7 +165,8 @@ public class CorrelatingMessageHandlerTests {
}
catch (MessageHandlingException e) {
Throwable cause = e.getCause();
boolean pass = cause instanceof IllegalStateException && cause.getMessage().toLowerCase().contains("null correlation");
boolean pass = cause instanceof IllegalStateException
&& cause.getMessage().toLowerCase().contains("null correlation");
if (!pass) {
throw e;
}
@@ -178,8 +175,11 @@ public class CorrelatingMessageHandlerTests {
private Message<?> testMessage(String correlationKey, int sequenceNumber, int sequenceSize) {
return MessageBuilder.withPayload("test" + sequenceNumber).setCorrelationId(correlationKey).setSequenceNumber(
sequenceNumber).setSequenceSize(sequenceSize).build();
return MessageBuilder.withPayload("test" + sequenceNumber)
.setCorrelationId(correlationKey)
.setSequenceNumber(sequenceNumber)
.setSequenceSize(sequenceSize)
.build();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -59,9 +59,7 @@ public class ResequencerParserTests {
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
ResequencingMessageHandler.class);
assertThat(getPropertyValue(resequencer, "outputChannel")).isNull();
assertThat(getPropertyValue(
resequencer, "messagingTemplate.sendTimeout"))
.as("The ResequencerEndpoint is not set with the appropriate timeout value").isEqualTo(-1L);
assertThat(getPropertyValue(resequencer, "messagingTemplate.sendTimeout")).isEqualTo(30000L);
assertThat(getPropertyValue(resequencer, "sendPartialResultOnExpiry"))
.as("The ResequencerEndpoint is not configured with the appropriate 'send partial results on " +
"timeout'" +

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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,7 +18,7 @@ package org.springframework.integration.config.annotation;
import java.lang.reflect.Method;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ApplicationContext;
@@ -51,7 +51,7 @@ public class AggregatorAnnotationTests {
assertThat(getPropertyValue(aggregator, "releaseStrategy") instanceof SimpleSequenceSizeReleaseStrategy)
.isTrue();
assertThat(getPropertyValue(aggregator, "outputChannel")).isNull();
assertThat(getPropertyValue(aggregator, "messagingTemplate.sendTimeout")).isEqualTo(-1L);
assertThat(getPropertyValue(aggregator, "messagingTemplate.sendTimeout")).isEqualTo(30000L);
assertThat(getPropertyValue(aggregator, "sendPartialResultOnExpiry")).isEqualTo(false);
context.close();
}
@@ -98,8 +98,8 @@ public class AggregatorAnnotationTests {
Object correlationStrategy = getPropertyValue(aggregator, "correlationStrategy");
assertThat(correlationStrategy instanceof MethodInvokingCorrelationStrategy).isTrue();
MethodInvokingCorrelationStrategy releaseStrategyAdapter = (MethodInvokingCorrelationStrategy) correlationStrategy;
DirectFieldAccessor processorAccessor = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(releaseStrategyAdapter)
.getPropertyValue("processor")).getPropertyValue("delegate"));
DirectFieldAccessor processorAccessor =
new DirectFieldAccessor(TestUtils.getPropertyValue(releaseStrategyAdapter, "processor.delegate"));
Object targetObject = processorAccessor.getPropertyValue("targetObject");
assertThat(targetObject).isSameAs(context.getBean(endpointName));
assertThat(processorAccessor.getPropertyValue("handlerMethods")).isNull();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -49,7 +49,7 @@ class HeaderEnricherParserTests {
void sendTimeoutDefault() {
Object endpoint = context.getBean("headerEnricherWithDefaults");
long sendTimeout = TestUtils.getPropertyValue(endpoint, "handler.messagingTemplate.sendTimeout", Long.class);
assertThat(sendTimeout).isEqualTo(-1L);
assertThat(sendTimeout).isEqualTo(30000L);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2022 the original author or authors.
* Copyright 2014-2023 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.
@@ -639,7 +639,7 @@ public class EnableIntegrationTests {
assertThat(TestUtils.getPropertyValue(consumer, "handler.outputChannelName")).isEqualTo("annOutput");
assertThat(TestUtils.getPropertyValue(consumer, "handler.discardChannelName")).isEqualTo("annOutput");
assertThat(TestUtils.getPropertyValue(consumer, "trigger.period")).isEqualTo(Duration.ofSeconds(1));
assertThat(TestUtils.getPropertyValue(consumer, "handler.messagingTemplate.sendTimeout")).isEqualTo(-1L);
assertThat(TestUtils.getPropertyValue(consumer, "handler.messagingTemplate.sendTimeout")).isEqualTo(30000L);
assertThat(TestUtils.getPropertyValue(consumer, "handler.sendPartialResultOnExpiry", Boolean.class)).isFalse();
consumer = this.context.getBean("annotationTestService.annAgg2.aggregator", PollingConsumer.class);

View File

@@ -91,9 +91,9 @@ public class MessagingGatewayTests {
@Test
public void sendMessage() {
Mockito.when(requestChannel.send(messageMock, 1000L)).thenReturn(true);
Mockito.when(requestChannel.send(messageMock, 30000L)).thenReturn(true);
this.messagingGateway.send(messageMock);
Mockito.verify(requestChannel).send(messageMock, 1000L);
Mockito.verify(requestChannel).send(messageMock, 30000L);
}
@Test
@@ -109,10 +109,10 @@ public class MessagingGatewayTests {
Mockito.doAnswer(invocation -> {
assertThat(((Message<?>) invocation.getArguments()[0]).getPayload()).isEqualTo("test");
return true;
}).when(requestChannel).send(Mockito.any(Message.class), Mockito.eq(1000L));
}).when(requestChannel).send(Mockito.any(Message.class), Mockito.eq(30000L));
this.messagingGateway.send("test");
Mockito.verify(requestChannel).send(Mockito.any(Message.class), Mockito.eq(1000L));
Mockito.verify(requestChannel).send(Mockito.any(Message.class), Mockito.eq(30000L));
}
@Test
@@ -136,17 +136,17 @@ public class MessagingGatewayTests {
@Test
public void receiveMessage() {
Mockito.when(replyChannel.receive(1000L)).thenReturn(messageMock);
Mockito.when(replyChannel.receive(30000L)).thenReturn(messageMock);
Mockito.when(messageMock.getPayload()).thenReturn("test");
assertThat(this.messagingGateway.receive()).isEqualTo("test");
Mockito.verify(replyChannel).receive(1000L);
Mockito.verify(replyChannel).receive(30000L);
}
@Test
public void receiveMessage_null() {
Mockito.when(replyChannel.receive(1000L)).thenReturn(null);
Mockito.when(replyChannel.receive(30000L)).thenReturn(null);
assertThat(this.messagingGateway.receive()).isNull();
Mockito.verify(replyChannel).receive(1000L);
Mockito.verify(replyChannel).receive(30000L);
}
/* send and receive tests */

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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,11 +18,9 @@ package org.springframework.integration.handler;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
@@ -32,8 +30,10 @@ import org.springframework.messaging.support.GenericMessage;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willReturn;
import static org.mockito.Mockito.mock;
/**
* @author Iwein Fuld
@@ -43,7 +43,6 @@ import static org.mockito.BDDMockito.willReturn;
* @author Artem Bilan
* @author Oleg Zhurakousky
*/
@RunWith(MockitoJUnitRunner.class)
public class AbstractReplyProducingMessageHandlerTests {
private final AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
@@ -57,9 +56,12 @@ public class AbstractReplyProducingMessageHandlerTests {
private final Message<?> message = MessageBuilder.withPayload("test").build();
@Mock
private final MessageChannel channel = null;
private MessageChannel channel;
@BeforeEach
void setup() {
channel = mock(MessageChannel.class);
}
@Test
public void errorMessageShouldContainChannelName() {
@@ -91,7 +93,7 @@ public class AbstractReplyProducingMessageHandlerTests {
handler.setOutputChannel(this.channel);
assertThat(handler.getNotPropagatedHeaders()).contains("f*", "*r");
ArgumentCaptor<Message<?>> captor = ArgumentCaptor.forClass(Message.class);
willReturn(true).given(this.channel).send(captor.capture());
willReturn(true).given(this.channel).send(captor.capture(), eq(30000L));
handler.handleMessage(MessageBuilder.withPayload("hello")
.setHeader("foo", "FOO")
.setHeader("bar", "BAR")
@@ -119,7 +121,7 @@ public class AbstractReplyProducingMessageHandlerTests {
assertThat(handler.getNotPropagatedHeaders()).contains("boom");
handler.setOutputChannel(this.channel);
ArgumentCaptor<Message<?>> captor = ArgumentCaptor.forClass(Message.class);
willReturn(true).given(this.channel).send(captor.capture());
willReturn(true).given(this.channel).send(captor.capture(), eq(30000L));
handler.handleMessage(MessageBuilder.withPayload("hello")
.setHeader("boom", "FOO")
.setHeader("bar", "BAR")
@@ -149,7 +151,7 @@ public class AbstractReplyProducingMessageHandlerTests {
handler.setOutputChannel(this.channel);
assertThat(handler.getNotPropagatedHeaders()).contains("foo", "b*r");
ArgumentCaptor<Message<?>> captor = ArgumentCaptor.forClass(Message.class);
willReturn(true).given(this.channel).send(captor.capture());
willReturn(true).given(this.channel).send(captor.capture(), eq(30000L));
handler.handleMessage(
MessageBuilder.withPayload("hello")
.setHeader("foo", "FOO")

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -19,12 +19,9 @@ package org.springframework.integration.handler;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -35,6 +32,8 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
/**
@@ -43,21 +42,12 @@ import static org.mockito.Mockito.mock;
* @author Gary Russell
* @author Artem Bilan
*/
@RunWith(MockitoJUnitRunner.class)
public class MessageHandlerChainTests {
private final Message<String> message = MessageBuilder.withPayload("foo").build();
@Mock
private MessageChannel outputChannel;
@Mock
private MessageHandler handler1;
@Mock
private MessageHandler handler2;
@Mock
private MessageHandler handler3;
private ProducingHandlerStub producer1;
@@ -66,9 +56,13 @@ public class MessageHandlerChainTests {
private ProducingHandlerStub producer3;
@Before
@BeforeEach
public void setup() {
Mockito.when(outputChannel.send(Mockito.any(Message.class))).thenReturn(true);
outputChannel = mock(MessageChannel.class);
MessageHandler handler1 = mock(MessageHandler.class);
MessageHandler handler2 = mock(MessageHandler.class);
handler3 = mock(MessageHandler.class);
Mockito.when(outputChannel.send(Mockito.any(Message.class), eq(30000L))).thenReturn(true);
producer1 = new ProducingHandlerStub(handler1);
producer2 = new ProducingHandlerStub(handler2);
producer3 = new ProducingHandlerStub(handler3);
@@ -76,7 +70,7 @@ public class MessageHandlerChainTests {
@Test
public void chainWithOutputChannel() {
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
List<MessageHandler> handlers = new ArrayList<>();
handlers.add(producer1);
handlers.add(producer2);
handlers.add(producer3);
@@ -86,12 +80,12 @@ public class MessageHandlerChainTests {
chain.setOutputChannel(outputChannel);
chain.setBeanFactory(mock(BeanFactory.class));
chain.handleMessage(message);
Mockito.verify(outputChannel).send(Mockito.eq(message));
Mockito.verify(outputChannel).send(Mockito.eq(message), eq(30000L));
}
@Test(expected = IllegalArgumentException.class)
@Test
public void chainWithOutputChannelButLastHandlerDoesNotProduceReplies() {
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
List<MessageHandler> handlers = new ArrayList<>();
handlers.add(producer1);
handlers.add(producer2);
handlers.add(handler3);
@@ -100,12 +94,12 @@ public class MessageHandlerChainTests {
chain.setHandlers(handlers);
chain.setOutputChannel(outputChannel);
chain.setBeanFactory(mock(BeanFactory.class));
chain.afterPropertiesSet();
assertThatIllegalArgumentException().isThrownBy(chain::afterPropertiesSet);
}
@Test
public void chainWithoutOutputChannelButLastHandlerDoesNotProduceReplies() {
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
List<MessageHandler> handlers = new ArrayList<>();
handlers.add(producer1);
handlers.add(producer2);
handlers.add(handler3);
@@ -119,7 +113,7 @@ public class MessageHandlerChainTests {
@Test
public void chainForwardsToReplyChannel() {
Message<String> message = MessageBuilder.withPayload("test").setReplyChannel(outputChannel).build();
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
List<MessageHandler> handlers = new ArrayList<>();
handlers.add(producer1);
handlers.add(producer2);
handlers.add(producer3);
@@ -128,7 +122,7 @@ public class MessageHandlerChainTests {
chain.setHandlers(handlers);
chain.setBeanFactory(mock(BeanFactory.class));
chain.handleMessage(message);
Mockito.verify(outputChannel).send(Mockito.any(Message.class));
Mockito.verify(outputChannel).send(Mockito.any(Message.class), eq(30000L));
}
@Test
@@ -136,7 +130,7 @@ public class MessageHandlerChainTests {
Message<String> message = MessageBuilder.withPayload("test").setReplyChannelName("testChannel").build();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("testChannel", outputChannel);
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
List<MessageHandler> handlers = new ArrayList<>();
handlers.add(producer1);
handlers.add(producer2);
handlers.add(producer3);
@@ -145,14 +139,14 @@ public class MessageHandlerChainTests {
chain.setHandlers(handlers);
chain.setBeanFactory(beanFactory);
chain.handleMessage(message);
Mockito.verify(outputChannel).send(Mockito.eq(message));
Mockito.verify(outputChannel).send(Mockito.eq(message), eq(30000L));
}
@Test(expected = IllegalArgumentException.class) // INT-1175
@Test
public void chainRejectsDuplicateHandlers() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("testChannel", outputChannel);
List<MessageHandler> handlers = new ArrayList<MessageHandler>();
List<MessageHandler> handlers = new ArrayList<>();
handlers.add(producer1);
handlers.add(producer2);
handlers.add(producer1);
@@ -160,10 +154,11 @@ public class MessageHandlerChainTests {
chain.setBeanName("testChain");
chain.setHandlers(handlers);
chain.setBeanFactory(beanFactory);
chain.afterPropertiesSet();
assertThatIllegalArgumentException().isThrownBy(chain::afterPropertiesSet);
}
private static class ProducingHandlerStub extends IntegrationObjectSupport implements MessageHandler, MessageProducer {
private static class ProducingHandlerStub extends IntegrationObjectSupport
implements MessageHandler, MessageProducer {
private volatile MessageChannel output;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -16,8 +16,7 @@
package org.springframework.integration.router.config;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,8 +28,7 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,8 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @since 1.0.3
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SpringJUnitConfig
public class RecipientListRouterParserTests {
@Autowired
@@ -76,8 +73,7 @@ public class RecipientListRouterParserTests {
assertThat(handler.getClass()).isEqualTo(RecipientListRouter.class);
RecipientListRouter router = (RecipientListRouter) handler;
DirectFieldAccessor accessor = new DirectFieldAccessor(router);
assertThat(new DirectFieldAccessor(
accessor.getPropertyValue("messagingTemplate")).getPropertyValue("sendTimeout")).isEqualTo(-1L);
assertThat(TestUtils.getPropertyValue(router, "messagingTemplate.sendTimeout")).isEqualTo(30000L);
assertThat(accessor.getPropertyValue("applySequence")).isEqualTo(Boolean.FALSE);
assertThat(accessor.getPropertyValue("ignoreSendFailures")).isEqualTo(Boolean.FALSE);
}