diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java index 7251040682..5ffe4bba4b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java @@ -19,11 +19,15 @@ package org.springframework.boot.autoconfigure.mongo; import java.util.stream.Collectors; import com.mongodb.MongoClientSettings; +import com.mongodb.MongoClientSettings.Builder; import com.mongodb.connection.netty.NettyStreamFactoryFactory; import com.mongodb.reactivestreams.client.MongoClient; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import reactor.core.publisher.Flux; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -60,23 +64,51 @@ public class MongoReactiveAutoConfiguration { } @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(SocketChannel.class) + @ConditionalOnClass({ SocketChannel.class, NioEventLoopGroup.class }) static class NettyDriverConfiguration { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) - public MongoClientSettingsBuilderCustomizer nettyDriverCustomizer( + public NettyDriverMongoClientSettingsBuilderCustomizer nettyDriverCustomizer( ObjectProvider settings) { - return (builder) -> { - if (!isStreamFactoryFactoryDefined(settings.getIfAvailable())) { - builder.streamFactoryFactory( - NettyStreamFactoryFactory.builder().build()); - } - }; + return new NettyDriverMongoClientSettingsBuilderCustomizer(settings); } - private boolean isStreamFactoryFactoryDefined(MongoClientSettings settings) { - return settings != null && settings.getStreamFactoryFactory() != null; + private static final class NettyDriverMongoClientSettingsBuilderCustomizer + implements MongoClientSettingsBuilderCustomizer, DisposableBean { + + private final ObjectProvider settings; + + private volatile EventLoopGroup eventLoopGroup; + + private NettyDriverMongoClientSettingsBuilderCustomizer( + ObjectProvider settings) { + this.settings = settings; + } + + @Override + public void customize(Builder builder) { + if (!isStreamFactoryFactoryDefined(this.settings.getIfAvailable())) { + NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(); + this.eventLoopGroup = eventLoopGroup; + builder.streamFactoryFactory(NettyStreamFactoryFactory.builder() + .eventLoopGroup(eventLoopGroup).build()); + } + } + + @Override + public void destroy() { + EventLoopGroup eventLoopGroup = this.eventLoopGroup; + if (eventLoopGroup != null) { + eventLoopGroup.shutdownGracefully().awaitUninterruptibly(); + this.eventLoopGroup = null; + } + } + + private boolean isStreamFactoryFactoryDefined(MongoClientSettings settings) { + return settings != null && settings.getStreamFactoryFactory() != null; + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java index 479be2e1be..f78e427a92 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.mongo; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import com.mongodb.MongoClientSettings; import com.mongodb.ReadPreference; @@ -25,6 +26,7 @@ import com.mongodb.connection.StreamFactory; import com.mongodb.connection.StreamFactoryFactory; import com.mongodb.connection.netty.NettyStreamFactoryFactory; import com.mongodb.reactivestreams.client.MongoClient; +import io.netty.channel.EventLoopGroup; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -89,11 +91,17 @@ public class MongoReactiveAutoConfigurationTests { @Test public void nettyStreamFactoryFactoryIsConfiguredAutomatically() { + AtomicReference eventLoopGroupReference = new AtomicReference<>(); this.contextRunner.run((context) -> { assertThat(context).hasSingleBean(MongoClient.class); - assertThat(getSettings(context).getStreamFactoryFactory()) - .isInstanceOf(NettyStreamFactoryFactory.class); + StreamFactoryFactory factory = getSettings(context).getStreamFactoryFactory(); + assertThat(factory).isInstanceOf(NettyStreamFactoryFactory.class); + EventLoopGroup eventLoopGroup = (EventLoopGroup) ReflectionTestUtils + .getField(factory, "eventLoopGroup"); + assertThat(eventLoopGroup.isShutdown()).isFalse(); + eventLoopGroupReference.set(eventLoopGroup); }); + assertThat(eventLoopGroupReference.get().isShutdown()).isTrue(); } @Test