Fix tests for executorService.shutdown()

Even if `Executors.newSingleThreadExecutor()` returns a `FinalizableDelegatedExecutorService`,
an instance is kept in the memory until JVM exists.
That may lead to memory leak since we have a lot of threads in memory.

(cherry picked from commit fdac8f1634)
This commit is contained in:
Artem Bilan
2024-08-13 15:57:25 -04:00
committed by Spring Builds
parent 6a7581e6cc
commit ec2cfeb9b8
13 changed files with 132 additions and 78 deletions

View File

@@ -19,13 +19,13 @@ package org.springframework.integration.aggregator;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
@@ -156,8 +156,6 @@ class FluxAggregatorMessageHandlerTests {
}
@Test
@DisabledIfEnvironmentVariable(named = "bamboo_buildKey", matches = ".*?",
disabledReason = "Timing is too short for CI")
void testWindowTimespan() {
QueueChannel resultChannel = new QueueChannel();
FluxAggregatorMessageHandler fluxAggregatorMessageHandler = new FluxAggregatorMessageHandler();
@@ -165,18 +163,18 @@ class FluxAggregatorMessageHandlerTests {
fluxAggregatorMessageHandler.setWindowTimespan(Duration.ofMillis(100));
fluxAggregatorMessageHandler.start();
Executors.newSingleThreadExecutor()
.submit(() -> {
for (int i = 0; i < 10; i++) {
Message<?> messageToAggregate =
MessageBuilder.withPayload(i)
.setCorrelationId("1")
.build();
fluxAggregatorMessageHandler.handleMessage(messageToAggregate);
Thread.sleep(20);
}
return null;
});
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> {
for (int i = 0; i < 10; i++) {
Message<?> messageToAggregate =
MessageBuilder.withPayload(i)
.setCorrelationId("1")
.build();
fluxAggregatorMessageHandler.handleMessage(messageToAggregate);
Thread.sleep(20);
}
return null;
});
Message<?> result = resultChannel.receive(10_000);
assertThat(result).isNotNull();
@@ -211,6 +209,8 @@ class FluxAggregatorMessageHandlerTests {
.doesNotContain(0, 1);
fluxAggregatorMessageHandler.stop();
executorService.shutdown();
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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,11 @@ package org.springframework.integration.channel.config;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -32,8 +31,7 @@ import org.springframework.integration.config.TestChannelInterceptor;
import org.springframework.messaging.PollableChannel;
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.junit.jupiter.SpringJUnitConfig;
import static org.assertj.core.api.Assertions.assertThat;
@@ -42,8 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
* @author Artem Bilan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SpringJUnitConfig
@DirtiesContext
public class ThreadLocalChannelParserTests {
@@ -58,28 +55,29 @@ public class ThreadLocalChannelParserTests {
@Test
public void testSendInAnotherThread() throws Exception {
simpleChannel.send(new GenericMessage<String>("test"));
Executor otherThreadExecutor = Executors.newSingleThreadExecutor();
simpleChannel.send(new GenericMessage<>("test"));
ExecutorService otherThreadExecutor = Executors.newSingleThreadExecutor();
final CountDownLatch latch = new CountDownLatch(1);
otherThreadExecutor.execute(() -> {
simpleChannel.send(new GenericMessage<String>("crap"));
simpleChannel.send(new GenericMessage<>("crap"));
latch.countDown();
});
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(simpleChannel.receive(10).getPayload()).isEqualTo("test");
// Message sent on another thread is not collected here
assertThat(simpleChannel.receive(10)).isEqualTo(null);
assertThat(simpleChannel.receive(1)).isEqualTo(null);
otherThreadExecutor.shutdown();
}
@Test
public void testReceiveInAnotherThread() throws Exception {
simpleChannel.send(new GenericMessage<String>("test-1.1"));
simpleChannel.send(new GenericMessage<String>("test-1.2"));
simpleChannel.send(new GenericMessage<String>("test-1.3"));
channelWithInterceptor.send(new GenericMessage<String>("test-2.1"));
channelWithInterceptor.send(new GenericMessage<String>("test-2.2"));
Executor otherThreadExecutor = Executors.newSingleThreadExecutor();
final List<Object> otherThreadResults = new ArrayList<Object>();
simpleChannel.send(new GenericMessage<>("test-1.1"));
simpleChannel.send(new GenericMessage<>("test-1.2"));
simpleChannel.send(new GenericMessage<>("test-1.3"));
channelWithInterceptor.send(new GenericMessage<>("test-2.1"));
channelWithInterceptor.send(new GenericMessage<>("test-2.2"));
ExecutorService otherThreadExecutor = Executors.newSingleThreadExecutor();
final List<Object> otherThreadResults = new ArrayList<>();
final CountDownLatch latch = new CountDownLatch(2);
otherThreadExecutor.execute(() -> {
otherThreadResults.add(simpleChannel.receive(0));
@@ -100,12 +98,14 @@ public class ThreadLocalChannelParserTests {
assertThat(channelWithInterceptor.receive(0).getPayload()).isEqualTo("test-2.1");
assertThat(channelWithInterceptor.receive(0).getPayload()).isEqualTo("test-2.2");
assertThat(channelWithInterceptor.receive(0)).isNull();
otherThreadExecutor.shutdown();
}
@Test
public void testInterceptor() {
int before = interceptor.getSendCount();
channelWithInterceptor.send(new GenericMessage<String>("test"));
channelWithInterceptor.send(new GenericMessage<>("test"));
assertThat(interceptor.getSendCount()).isEqualTo(before + 1);
}

View File

@@ -20,6 +20,7 @@ import java.time.Duration;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -395,7 +396,8 @@ public class GatewayParserTests {
}
private void startResponder(final PollableChannel requestChannel, final MessageChannel replyChannel) {
Executors.newSingleThreadExecutor().execute(() -> {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
Message<?> request = requestChannel.receive(60000);
assertThat(request).as("Request not received").isNotNull();
Message<?> reply = MessageBuilder.fromMessage(request)
@@ -405,7 +407,7 @@ public class GatewayParserTests {
payload = CompletableFuture.completedFuture(reply);
}
else if (request.getPayload().equals("flowCompletable")) {
payload = CompletableFuture.<String>completedFuture("SYNC_COMPLETABLE");
payload = CompletableFuture.completedFuture("SYNC_COMPLETABLE");
}
else if (request.getPayload().equals("flowCustomCompletable")) {
MyCompletableFuture myCompletableFuture1 = new MyCompletableFuture();
@@ -427,6 +429,7 @@ public class GatewayParserTests {
}
replyChannel.send(reply);
});
executorService.shutdown();
}
@SuppressWarnings("unused")

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.core;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -432,8 +433,8 @@ public class AsyncMessagingTemplateTests {
}
private static void sendMessageAfterDelay(MessageChannel channel, GenericMessage<String> message, int delay) {
Executors.newSingleThreadExecutor()
.execute(() -> {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
try {
Thread.sleep(delay);
}
@@ -443,6 +444,7 @@ public class AsyncMessagingTemplateTests {
}
channel.send(message);
});
executorService.shutdown();
}
private static class EchoHandler extends AbstractReplyProducingMessageHandler {