INT-4452: Expire immediately when group timeout<0

JIRA: https://jira.spring.io/browse/INT-4452

This is pretty typical in practice to get a `groupTimeout` to be
evaluated to a negative value: some business decisions, sensitive
calculations and so on.

* Treat any non-positive `groupTimeout` as a signal to expire group
immediately without scheduling.
Only `null` is considered as a signal do nothing for the current message
* Polishing for some tests for better performance - saves some execution
time
This commit is contained in:
Artem Bilan
2018-06-22 17:12:50 -04:00
committed by Gary Russell
parent ee501c801f
commit 13510df575
15 changed files with 161 additions and 121 deletions

View File

@@ -100,7 +100,7 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
private final Map<UUID, ScheduledFuture<?>> expireGroupScheduledFutures = new ConcurrentHashMap<>();
private final Set<Object> groupIds = ConcurrentHashMap.newKeySet();
private final Set<Object> groupIds = ConcurrentHashMap.newKeySet();
private MessageGroupProcessor outputProcessor;
@@ -144,15 +144,23 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
public AbstractCorrelatingMessageHandler(MessageGroupProcessor processor, MessageGroupStore store,
CorrelationStrategy correlationStrategy, ReleaseStrategy releaseStrategy) {
Assert.notNull(processor, "'processor' must not be null");
Assert.notNull(store, "'store' must not be null");
setMessageStore(store);
this.outputProcessor = processor;
this.correlationStrategy = (correlationStrategy == null
? new HeaderAttributeCorrelationStrategy(IntegrationMessageHeaderAccessor.CORRELATION_ID)
: correlationStrategy);
this.releaseStrategy = releaseStrategy == null ? new SimpleSequenceSizeReleaseStrategy() : releaseStrategy;
this.correlationStrategy =
correlationStrategy == null
? new HeaderAttributeCorrelationStrategy(IntegrationMessageHeaderAccessor.CORRELATION_ID)
: correlationStrategy;
this.releaseStrategy =
releaseStrategy == null
? new SimpleSequenceSizeReleaseStrategy()
: releaseStrategy;
this.releaseStrategySet = releaseStrategy != null;
this.sequenceAware = this.releaseStrategy instanceof SequenceSizeReleaseStrategy;
}
@@ -195,7 +203,7 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
}
public void setForceReleaseAdviceChain(List<Advice> forceReleaseAdviceChain) {
Assert.notNull(forceReleaseAdviceChain, "forceReleaseAdviceChain must not be null");
Assert.notNull(forceReleaseAdviceChain, "'forceReleaseAdviceChain' must not be null");
this.forceReleaseAdviceChain = forceReleaseAdviceChain;
}
@@ -242,7 +250,7 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
}
if (this.releasePartialSequences) {
Assert.isInstanceOf(SequenceSizeReleaseStrategy.class, this.releaseStrategy,
Assert.isInstanceOf(SequenceSizeReleaseStrategy.class, this.releaseStrategy, () ->
"Release strategy of type [" + this.releaseStrategy.getClass().getSimpleName() +
"] cannot release partial sequences. Use a SequenceSizeReleaseStrategy instead.");
((SequenceSizeReleaseStrategy) this.releaseStrategy)
@@ -519,7 +527,7 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
* When 'groupTimeout' is evaluated to 'null' we do nothing.
* The 'MessageGroupStoreReaper' can be used to 'forceComplete' message groups.
*/
if (groupTimeout != null && groupTimeout >= 0) {
if (groupTimeout != null) {
if (groupTimeout > 0) {
final Object groupId = messageGroup.getGroupId();
final long timestamp = messageGroup.getTimestamp();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -47,6 +47,7 @@ import org.springframework.messaging.MessageHeaders;
* @author Mark Fisher
* @author Marius Bogoevici
* @author Iwein Fuld
* @author Artem Bilan
*/
public class ConcurrentAggregatorTests {
@@ -90,8 +91,7 @@ public class ConcurrentAggregatorTests {
@Test
@Ignore
// dropped backwards compatibility for duplicate ID's
public void testCompleteGroupWithinTimeoutWithSameId()
throws InterruptedException {
public void testCompleteGroupWithinTimeoutWithSameId() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message1 = createMessage(3, "ABC", 3, 1, replyChannel,
"ID#1");
@@ -111,8 +111,8 @@ public class ConcurrentAggregatorTests {
}
@Test
public void testShouldNotSendPartialResultOnTimeoutByDefault()
throws InterruptedException {
public void testShouldNotSendPartialResultOnTimeoutByDefault() throws InterruptedException {
QueueChannel discardChannel = new QueueChannel();
this.aggregator.setDiscardChannel(discardChannel);
QueueChannel replyChannel = new QueueChannel();
@@ -126,17 +126,16 @@ public class ConcurrentAggregatorTests {
assertEquals("Task should have completed within timeout", 0, latch
.getCount());
Message<?> reply = replyChannel.receive(1000);
Message<?> reply = replyChannel.receive(10);
assertNull("No message should have been sent normally", reply);
this.store.expireMessageGroups(-10000);
Message<?> discardedMessage = discardChannel.receive(1000);
Message<?> discardedMessage = discardChannel.receive(10000);
assertNotNull("A message should have been discarded", discardedMessage);
assertEquals(message, discardedMessage);
}
@Test
public void testShouldSendPartialResultOnTimeoutTrue()
throws InterruptedException {
public void testShouldSendPartialResultOnTimeoutTrue() throws InterruptedException {
this.aggregator.setSendPartialResultOnExpiry(true);
QueueChannel replyChannel = new QueueChannel();
Message<?> message1 = createMessage(3, "ABC", 3, 1, replyChannel, null);
@@ -252,16 +251,14 @@ public class ConcurrentAggregatorTests {
}
@Test(expected = MessageHandlingException.class)
public void testExceptionThrownIfNoCorrelationId()
throws InterruptedException {
public void testExceptionThrownIfNoCorrelationId() {
Message<?> message = createMessage(3, null, 2, 1, new QueueChannel(),
null);
this.aggregator.handleMessage(message);
}
@Test
public void testAdditionalMessageAfterCompletion()
throws InterruptedException {
public void testAdditionalMessageAfterCompletion() throws InterruptedException {
QueueChannel replyChannel = new QueueChannel();
Message<?> message1 = createMessage(3, "ABC", 3, 1, replyChannel, null);
Message<?> message2 = createMessage(5, "ABC", 3, 2, replyChannel, null);
@@ -291,6 +288,7 @@ public class ConcurrentAggregatorTests {
private static Message<?> createMessage(Object payload,
Object correlationId, int sequenceSize, int sequenceNumber,
MessageChannel replyChannel, String predefinedId) {
MessageBuilder<Object> builder = MessageBuilder.withPayload(payload)
.setCorrelationId(correlationId).setSequenceSize(sequenceSize)
.setSequenceNumber(sequenceNumber)

View File

@@ -42,7 +42,7 @@
<aggregator input-channel="groupTimeoutExpressionAggregatorInput" output-channel="output" discard-channel="discard"
send-partial-result-on-expiry="true"
group-timeout-expression="size() ge 2 ? 100 : -1"
group-timeout-expression="size() ge 2 ? 100 : null"
release-strategy-expression="messages[0].headers.sequenceNumber == messages[0].headers.sequenceSize"/>
<aggregator input-channel="zeroGroupTimeoutExpressionAggregatorInput" output-channel="output" discard-channel="discard"

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2018 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.
@@ -45,8 +45,7 @@ import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Iwein Fuld
@@ -55,8 +54,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Artem Bilan
* @author Gary Russell
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@RunWith(SpringRunner.class)
@DirtiesContext
public class AggregatorIntegrationTests {
@@ -91,10 +89,10 @@ public class AggregatorIntegrationTests {
private QueueChannel errors;
@Test
public void testVanillaAggregation() throws Exception {
public void testVanillaAggregation() {
for (int i = 0; i < 5; i++) {
Map<String, Object> headers = stubHeaders(i, 5, 1);
input.send(new GenericMessage<Integer>(i, headers));
input.send(new GenericMessage<>(i, headers));
}
Message<?> receive = output.receive(10000);
assertNotNull(receive);
@@ -102,10 +100,10 @@ public class AggregatorIntegrationTests {
}
@Test
public void testNonExpiringAggregator() throws Exception {
public void testNonExpiringAggregator() {
for (int i = 0; i < 5; i++) {
Map<String, Object> headers = stubHeaders(i, 5, 1);
nonExpiringAggregatorInput.send(new GenericMessage<Integer>(i, headers));
nonExpiringAggregatorInput.send(new GenericMessage<>(i, headers));
}
assertNotNull(output.receive(0));
@@ -113,7 +111,7 @@ public class AggregatorIntegrationTests {
for (int i = 5; i < 10; i++) {
Map<String, Object> headers = stubHeaders(i, 5, 1);
nonExpiringAggregatorInput.send(new GenericMessage<Integer>(i, headers));
nonExpiringAggregatorInput.send(new GenericMessage<>(i, headers));
}
assertNull(output.receive(0));
@@ -125,10 +123,10 @@ public class AggregatorIntegrationTests {
}
@Test
public void testExpiringAggregator() throws Exception {
public void testExpiringAggregator() {
for (int i = 0; i < 5; i++) {
Map<String, Object> headers = stubHeaders(i, 5, 1);
expiringAggregatorInput.send(new GenericMessage<Integer>(i, headers));
expiringAggregatorInput.send(new GenericMessage<>(i, headers));
}
assertNotNull(output.receive(0));
@@ -136,7 +134,7 @@ public class AggregatorIntegrationTests {
for (int i = 5; i < 10; i++) {
Map<String, Object> headers = stubHeaders(i, 5, 1);
expiringAggregatorInput.send(new GenericMessage<Integer>(i, headers));
expiringAggregatorInput.send(new GenericMessage<>(i, headers));
}
assertNotNull(output.receive(0));
@@ -148,7 +146,7 @@ public class AggregatorIntegrationTests {
public void testGroupTimeoutScheduling() throws Exception {
for (int i = 0; i < 5; i++) {
Map<String, Object> headers = stubHeaders(i, 5, 1);
this.groupTimeoutAggregatorInput.send(new GenericMessage<Integer>(i, headers));
this.groupTimeoutAggregatorInput.send(new GenericMessage<>(i, headers));
//Wait until 'group-timeout' does its stuff.
MessageGroupStore mgs = TestUtils.getPropertyValue(this.context.getBean("gta.handler"), "messageStore",
@@ -166,11 +164,11 @@ public class AggregatorIntegrationTests {
@Test
public void testGroupTimeoutReschedulingOnMessageDeliveryException() throws Exception {
for (int i = 0; i < 5; i++) {
this.output.send(new GenericMessage<String>("fake message"));
this.output.send(new GenericMessage<>("fake message"));
}
Map<String, Object> headers = stubHeaders(1, 2, 1);
this.groupTimeoutAggregatorInput.send(new GenericMessage<Integer>(1, headers));
this.groupTimeoutAggregatorInput.send(new GenericMessage<>(1, headers));
//Wait until 'group-timeout' does its stuff.
MessageGroupStore mgs = TestUtils.getPropertyValue(this.context.getBean("gta.handler"), "messageStore",
@@ -190,14 +188,14 @@ public class AggregatorIntegrationTests {
}
@Test
public void testGroupTimeoutExpressionScheduling() throws Exception {
// Since group-timeout-expression="size() >= 2 ? 100 : -1". The first message won't be scheduled to 'forceComplete'
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(1, stubHeaders(1, 6, 1)));
public void testGroupTimeoutExpressionScheduling() {
// Since group-timeout-expression="size() >= 2 ? 100 : null". The first message won't be scheduled to 'forceComplete'
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(1, stubHeaders(1, 6, 1)));
assertNull(this.output.receive(0));
assertNull(this.discard.receive(0));
// As far as 'group.size() >= 2' it will be scheduled to 'forceComplete'
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(2, stubHeaders(2, 6, 1)));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(2, stubHeaders(2, 6, 1)));
assertNull(this.output.receive(0));
Message<?> receive = this.output.receive(10000);
assertNotNull(receive);
@@ -205,15 +203,15 @@ public class AggregatorIntegrationTests {
assertNull(this.discard.receive(0));
// The same with these three messages
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(3, stubHeaders(3, 6, 1)));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(3, stubHeaders(3, 6, 1)));
assertNull(this.output.receive(0));
assertNull(this.discard.receive(0));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(4, stubHeaders(4, 6, 1)));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(4, stubHeaders(4, 6, 1)));
assertNull(this.output.receive(0));
assertNull(this.discard.receive(0));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(5, stubHeaders(5, 6, 1)));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(5, stubHeaders(5, 6, 1)));
assertNull(this.output.receive(0));
receive = this.output.receive(10000);
assertNotNull(receive);
@@ -221,7 +219,7 @@ public class AggregatorIntegrationTests {
assertNull(this.discard.receive(0));
// The last message in the sequence - normal release by provided 'ReleaseStrategy'
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(6, stubHeaders(6, 6, 1)));
this.groupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(6, stubHeaders(6, 6, 1)));
receive = this.output.receive(10000);
assertNotNull(receive);
assertEquals(1, ((Collection<?>) receive.getPayload()).size());
@@ -229,17 +227,17 @@ public class AggregatorIntegrationTests {
}
@Test
public void testZeroGroupTimeoutExpressionScheduling() throws Exception {
public void testZeroGroupTimeoutExpressionScheduling() {
try {
this.output.purge(null);
this.errors.purge(null);
GenericMessage<String> message = new GenericMessage<String>("foo");
GenericMessage<String> message = new GenericMessage<>("foo");
this.output.send(message);
this.output.send(message);
this.output.send(message);
this.output.send(message);
this.output.send(message);
this.zeroGroupTimeoutExpressionAggregatorInput.send(new GenericMessage<Integer>(1, stubHeaders(1, 2, 1)));
this.zeroGroupTimeoutExpressionAggregatorInput.send(new GenericMessage<>(1, stubHeaders(1, 2, 1)));
ErrorMessage em = (ErrorMessage) this.errors.receive(10000);
assertNotNull(em);
assertThat(em.getPayload().getMessage().toLowerCase(),
@@ -263,7 +261,7 @@ public class AggregatorIntegrationTests {
}
private Map<String, Object> stubHeaders(int sequenceNumber, int sequenceSize, int correlationId) {
Map<String, Object> headers = new HashMap<String, Object>();
Map<String, Object> headers = new HashMap<>();
headers.put(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, sequenceNumber);
headers.put(IntegrationMessageHeaderAccessor.SEQUENCE_SIZE, sequenceSize);
headers.put(IntegrationMessageHeaderAccessor.CORRELATION_ID, correlationId);

View File

@@ -38,25 +38,26 @@ import org.springframework.messaging.support.GenericMessage;
/**
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*/
public class PriorityChannelTests {
@Test
public void testCapacityEnforced() {
PriorityChannel channel = new PriorityChannel(3);
assertTrue(channel.send(new GenericMessage<String>("test1"), 0));
assertTrue(channel.send(new GenericMessage<String>("test2"), 0));
assertTrue(channel.send(new GenericMessage<String>("test3"), 0));
assertFalse(channel.send(new GenericMessage<String>("test4"), 0));
assertTrue(channel.send(new GenericMessage<>("test1"), 0));
assertTrue(channel.send(new GenericMessage<>("test2"), 0));
assertTrue(channel.send(new GenericMessage<>("test3"), 0));
assertFalse(channel.send(new GenericMessage<>("test4"), 0));
channel.receive(0);
assertTrue(channel.send(new GenericMessage<String>("test5")));
assertTrue(channel.send(new GenericMessage<>("test5")));
}
@Test
public void testDefaultComparatorWithTimestampFallback() throws Exception {
public void testDefaultComparatorWithTimestampFallback() {
PriorityChannel channel = new PriorityChannel();
for (int i = 0; i < 1000; i++) {
channel.send(new GenericMessage<Integer>(i));
channel.send(new GenericMessage<>(i));
}
for (int i = 0; i < 1000; i++) {
assertEquals(i, channel.receive().getPayload());
@@ -86,9 +87,9 @@ public class PriorityChannelTests {
// although this test has no assertions it results in ConcurrentModificationException
// if executed before changes for INT-2508
@Test
public void testPriorityChannelWithConcurrentModification() throws Exception {
public void testPriorityChannelWithConcurrentModification() {
final PriorityChannel channel = new PriorityChannel();
final Message<String> message = new GenericMessage<String>("hello");
final Message<String> message = new GenericMessage<>("hello");
for (int i = 0; i < 1000; i++) {
channel.send(message);
new Thread(() -> channel.receive()).start();
@@ -99,11 +100,11 @@ public class PriorityChannelTests {
@Test
public void testCustomComparator() {
PriorityChannel channel = new PriorityChannel(5, new StringPayloadComparator());
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
Message<?> messageC = new GenericMessage<String>("C");
Message<?> messageD = new GenericMessage<String>("D");
Message<?> messageE = new GenericMessage<String>("E");
Message<?> messageA = new GenericMessage<>("A");
Message<?> messageB = new GenericMessage<>("B");
Message<?> messageC = new GenericMessage<>("C");
Message<?> messageD = new GenericMessage<>("D");
Message<?> messageE = new GenericMessage<>("E");
channel.send(messageC);
channel.send(messageA);
channel.send(messageE);
@@ -200,7 +201,7 @@ public class PriorityChannelTests {
PriorityChannel channel = new PriorityChannel(5);
Message<?> highPriority = createPriorityMessage(5);
Message<?> lowPriority = createPriorityMessage(-5);
Message<?> nullPriority = new GenericMessage<String>("test:NULL");
Message<?> nullPriority = new GenericMessage<>("test:NULL");
channel.send(lowPriority);
channel.send(highPriority);
channel.send(nullPriority);
@@ -214,7 +215,7 @@ public class PriorityChannelTests {
PriorityChannel channel = new PriorityChannel();
Message<?> highPriority = createPriorityMessage(5);
Message<?> lowPriority = createPriorityMessage(-5);
Message<?> nullPriority = new GenericMessage<String>("test:NULL");
Message<?> nullPriority = new GenericMessage<>("test:NULL");
channel.send(lowPriority);
channel.send(highPriority);
channel.send(nullPriority);
@@ -228,8 +229,8 @@ public class PriorityChannelTests {
final PriorityChannel channel = new PriorityChannel(1);
final AtomicBoolean sentSecondMessage = new AtomicBoolean(false);
ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
channel.send(new GenericMessage<String>("test-1"));
executor.execute(() -> sentSecondMessage.set(channel.send(new GenericMessage<String>("test-2"), 10)));
channel.send(new GenericMessage<>("test-1"));
executor.execute(() -> sentSecondMessage.set(channel.send(new GenericMessage<>("test-2"), 10)));
assertFalse(sentSecondMessage.get());
executor.shutdown();
@@ -249,15 +250,15 @@ public class PriorityChannelTests {
ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
channel.send(new GenericMessage<String>("test-1"));
executor.execute(() -> {
sentSecondMessage.set(channel.send(new GenericMessage<String>("test-2"), 3000));
sentSecondMessage.set(channel.send(new GenericMessage<>("test-2"), 3000));
latch.countDown();
});
assertFalse(sentSecondMessage.get());
Thread.sleep(500);
Thread.sleep(10);
Message<?> message1 = channel.receive();
assertNotNull(message1);
assertEquals("test-1", message1.getPayload());
latch.await(1000, TimeUnit.MILLISECONDS);
assertTrue(latch.await(10000, TimeUnit.MILLISECONDS));
assertTrue(sentSecondMessage.get());
Message<?> message2 = channel.receive();
assertNotNull(message2);
@@ -271,10 +272,10 @@ public class PriorityChannelTests {
final AtomicBoolean sentSecondMessage = new AtomicBoolean(false);
ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
channel.send(new GenericMessage<String>("test-1"));
executor.execute(() -> sentSecondMessage.set(channel.send(new GenericMessage<String>("test-2"), -1)));
executor.execute(() -> sentSecondMessage.set(channel.send(new GenericMessage<>("test-2"), -1)));
assertFalse(sentSecondMessage.get());
Thread.sleep(500);
Message<?> message1 = channel.receive(1000);
Thread.sleep(10);
Message<?> message1 = channel.receive(10000);
assertNotNull(message1);
assertEquals("test-1", message1.getPayload());
executor.shutdown();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2018 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.
@@ -52,6 +52,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Gunnar Hillert
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
@ContextConfiguration
@@ -92,7 +93,7 @@ public class ControlBusTests {
"ControlBusLifecycleTests-context.xml", this.getClass());
MessageChannel inputChannel = context.getBean("inputChannel", MessageChannel.class);
PollableChannel outputChannel = context.getBean("outputChannel", PollableChannel.class);
assertNull(outputChannel.receive(1000));
assertNull(outputChannel.receive(10));
Message<?> message = MessageBuilder.withPayload("@adapter.start()").build();
inputChannel.send(message);
assertNotNull(outputChannel.receive(1000));
@@ -109,7 +110,7 @@ public class ControlBusTests {
assertEquals(0, result.getPayload());
this.registry.channelToChannelName(new DirectChannel());
// Sleep a bit to be sure that we aren't reaped by registry TTL as 60000
Thread.sleep(100);
Thread.sleep(10);
messagingTemplate.convertAndSend(input, "@integrationHeaderChannelRegistry.size()");
result = this.output.receive(0);
assertNotNull(result);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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,12 +17,15 @@
package org.springframework.integration.config.xml;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.QueueChannel;
@@ -37,6 +40,7 @@ import org.springframework.messaging.support.GenericMessage;
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
* @author Artem Bilan
*
*/
public class PollerWithErrorChannelTests {
@@ -54,10 +58,20 @@ public class PollerWithErrorChannelTests {
SubscribableChannel errorChannel = ac.getBean("errorChannel", SubscribableChannel.class);
MessageHandler handler = mock(MessageHandler.class);
CountDownLatch handleLatch = new CountDownLatch(1);
willAnswer(invocation -> {
handleLatch.countDown();
return null;
})
.given(handler)
.handleMessage(any(Message.class));
errorChannel.subscribe(handler);
adapter.start();
Thread.sleep(1000);
verify(handler, atLeastOnce()).handleMessage(Mockito.any(Message.class));
assertTrue(handleLatch.await(10, TimeUnit.SECONDS));
adapter.stop();
ac.close();
}
@@ -75,7 +89,7 @@ public class PollerWithErrorChannelTests {
}
@Test
public void testWithErrorChannelAndHeader() throws Exception {
public void testWithErrorChannelAndHeader() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("PollerWithErrorChannel-context.xml",
this.getClass());
SourcePollingChannelAdapter adapter = ac.getBean("withErrorChannelAndHeader",
@@ -89,7 +103,7 @@ public class PollerWithErrorChannelTests {
@Test
// config the same as above but the error wil come from the send
public void testWithErrorChannelAndHeaderWithSendFailure() throws Exception {
public void testWithErrorChannelAndHeaderWithSendFailure() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("PollerWithErrorChannel-context.xml",
this.getClass());
SourcePollingChannelAdapter adapter = ac.getBean("withErrorChannelAndHeaderErrorOnSend",
@@ -103,12 +117,12 @@ public class PollerWithErrorChannelTests {
@Test
// INT-1952
public void testWithErrorChannelAndPollingConsumer() throws Exception {
public void testWithErrorChannelAndPollingConsumer() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("PollerWithErrorChannel-context.xml",
this.getClass());
MessageChannel serviceWithPollerChannel = ac.getBean("serviceWithPollerChannel", MessageChannel.class);
QueueChannel errorChannel = ac.getBean("serviceErrorChannel", QueueChannel.class);
serviceWithPollerChannel.send(new GenericMessage<String>(""));
serviceWithPollerChannel.send(new GenericMessage<>(""));
assertNotNull(errorChannel.receive(10000));
ac.close();
}

View File

@@ -152,7 +152,7 @@ public class PollerAdviceTests {
adapter.setAdviceChain(adviceChain);
adapter.afterPropertiesSet();
adapter.start();
assertFalse(latch.await(1, TimeUnit.SECONDS));
assertFalse(latch.await(10, TimeUnit.MILLISECONDS));
adapter.stop();
skipper.reset();
latch = new CountDownLatch(1);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2018 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.
@@ -60,7 +60,7 @@ public class PollingLifecycleTests {
private final TestErrorHandler errorHandler = new TestErrorHandler();
@Before
public void init() throws Exception {
public void init() {
taskScheduler.afterPropertiesSet();
}
@@ -68,13 +68,16 @@ public class PollingLifecycleTests {
public void ensurePollerTaskStops() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
QueueChannel channel = new QueueChannel();
channel.send(new GenericMessage<String>("foo"));
channel.send(new GenericMessage<>("foo"));
//Has to be an explicit implementation - Mockito cannot mock/spy lambdas
MessageHandler handler = Mockito.spy(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
latch.countDown();
}
});
PollingConsumer consumer = new PollingConsumer(channel, handler);
consumer.setTrigger(new PeriodicTrigger(0));
@@ -87,7 +90,7 @@ public class PollingLifecycleTests {
Mockito.verify(handler, times(1)).handleMessage(Mockito.any(Message.class));
consumer.stop();
for (int i = 0; i < 10; i++) {
channel.send(new GenericMessage<String>("foo"));
channel.send(new GenericMessage<>("foo"));
}
Thread.sleep(2000); // give enough time for poller to kick in if it didn't stop properly
// we'll still have a natural race condition between call to stop() and poller polling
@@ -105,12 +108,16 @@ public class PollingLifecycleTests {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(2000));
adapterFactory.setPollerMetadata(pollerMetadata);
//Has to be an explicit implementation - Mockito cannot mock/spy lambdas
MessageSource<String> source = spy(new MessageSource<String>() {
@Override
public Message<String> receive() {
latch.countDown();
return new GenericMessage<String>("hello");
return new GenericMessage<>("hello");
}
});
adapterFactory.setSource(source);
adapterFactory.setOutputChannel(channel);
@@ -122,7 +129,7 @@ public class PollingLifecycleTests {
assertTrue(latch.await(20, TimeUnit.SECONDS));
assertNotNull(channel.receive(100));
adapter.stop();
assertNull(channel.receive(1000));
assertNull(channel.receive(10));
Mockito.verify(source, times(1)).receive();
}
@@ -136,20 +143,20 @@ public class PollingLifecycleTests {
pollerMetadata.setMaxMessagesPerPoll(-1);
pollerMetadata.setTrigger(new PeriodicTrigger(2000));
adapterFactory.setPollerMetadata(pollerMetadata);
final Runnable coughtInterrupted = mock(Runnable.class);
final Runnable caughtInterrupted = mock(Runnable.class);
MessageSource<String> source = () -> {
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
Thread.sleep(10);
latch.countDown();
}
}
catch (InterruptedException e) {
coughtInterrupted.run();
caughtInterrupted.run();
}
return new GenericMessage<String>("hello");
return new GenericMessage<>("hello");
};
adapterFactory.setSource(source);
adapterFactory.setOutputChannel(channel);
@@ -161,8 +168,8 @@ public class PollingLifecycleTests {
assertTrue(latch.await(3000, TimeUnit.SECONDS));
//
adapter.stop();
Thread.sleep(1000);
Mockito.verify(coughtInterrupted, times(1)).run();
Thread.sleep(10);
Mockito.verify(caughtInterrupted, times(1)).run();
}
@Test

View File

@@ -13,7 +13,7 @@
default-request-channel="requestChannel"
default-reply-timeout="3000"
service-interface="org.springframework.integration.gateway.GatewayRequiresReplyTests$TestService" />
<service-activator input-channel="requestChannel"
expression="payload == 'foo' ? 'bar' : null"
@@ -21,16 +21,16 @@
<gateway id="timeoutGateway"
default-request-channel="timeoutChannel"
default-reply-timeout="1000"
default-reply-timeout="10"
service-interface="org.springframework.integration.gateway.GatewayRequiresReplyTests$TestService" />
<channel id="timeoutChannel">
<dispatcher task-executor="executor"/>
</channel>
<service-activator input-channel="timeoutChannel">
<beans:bean class="org.springframework.integration.gateway.GatewayRequiresReplyTests.LongRunningService"/>
</service-activator>
<task:executor id="executor" pool-size="5"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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,8 @@
package org.springframework.integration.gateway;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +31,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @author Gunnar Hillert
* @author Artem Bilan
*
* @since 2.0
*/
@ContextConfiguration
@@ -43,34 +45,38 @@ public class GatewayRequiresReplyTests {
@Test
public void replyReceived() {
TestService gateway = (TestService) applicationContext.getBean("gateway");
TestService gateway = this.applicationContext.getBean("gateway", TestService.class);
String result = gateway.test("foo");
assertEquals("bar", result);
}
@Test(expected = ReplyRequiredException.class)
public void noReplyReceived() {
TestService gateway = (TestService) applicationContext.getBean("gateway");
TestService gateway = this.applicationContext.getBean("gateway", TestService.class);
gateway.test("bad");
}
@Test
public void timedOutGateway() {
TestService gateway = (TestService) applicationContext.getBean("timeoutGateway");
TestService gateway = this.applicationContext.getBean("timeoutGateway", TestService.class);
String result = gateway.test("hello");
assertNull(result);
}
public interface TestService {
String test(String s);
}
public static class LongRunningService {
public String echo(String value) throws Exception {
Thread.sleep(5000);
return value;
}
}
}

View File

@@ -425,17 +425,17 @@ public class DelayHandlerTests {
It's difficult to test it from real ctx, because any async process from 'inbound-channel-adapter'
can't achieve the DelayHandler before the main thread emits 'ContextRefreshedEvent'.
*/
public void testRescheduleAndHandleAtTheSameTime() throws Exception {
public void testRescheduleAndHandleAtTheSameTime() {
QueueChannel results = new QueueChannel();
delayHandler.setOutputChannel(results);
this.delayHandler.setDefaultDelay(100);
this.delayHandler.setDefaultDelay(10);
startDelayerHandler();
this.input.send(new GenericMessage<>("foo"));
this.delayHandler.reschedulePersistedMessages();
Message<?> message = results.receive(10000);
assertNotNull(message);
message = results.receive(500);
message = results.receive(50);
assertNull(message);
}

View File

@@ -42,6 +42,7 @@ import org.springframework.messaging.MessageChannel;
/**
* @author Gary Russell
*
* @since 3.0
*
*/
@@ -61,11 +62,11 @@ public class RouterConcurrencyTest {
protected void setConversionService(ConversionService conversionService) {
try {
if (count.incrementAndGet() > 1) {
Thread.sleep(2000);
Thread.sleep(20);
}
super.setConversionService(conversionService);
semaphore.release();
Thread.sleep(1000);
Thread.sleep(10);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -85,8 +86,7 @@ public class RouterConcurrencyTest {
router.setBeanFactory(beanFactory);
ExecutorService exec = Executors.newFixedThreadPool(2);
final List<ConversionService> returns = Collections.synchronizedList(
new ArrayList<ConversionService>());
final List<ConversionService> returns = Collections.synchronizedList(new ArrayList<>());
Runnable runnable = () -> {
ConversionService requiredConversionService = router.getRequiredConversionService();
returns.add(requiredConversionService);