diff --git a/spring-web/src/main/java/org/springframework/http/client/ReactorResourceFactory.java b/spring-web/src/main/java/org/springframework/http/client/ReactorResourceFactory.java index a27df1b525e..53beed1da2a 100644 --- a/spring-web/src/main/java/org/springframework/http/client/ReactorResourceFactory.java +++ b/spring-web/src/main/java/org/springframework/http/client/ReactorResourceFactory.java @@ -34,20 +34,22 @@ import org.springframework.util.Assert; /** * Factory to manage Reactor Netty resources, i.e. {@link LoopResources} for - * event loop threads, and {@link ConnectionProvider} for the connection pool, + * event loop threads and {@link ConnectionProvider} for the connection pool, * within the lifecycle of a Spring {@code ApplicationContext}. * *
This factory implements {@link SmartLifecycle} and is expected typically * to be declared as a Spring-managed bean. * - *
Notice that after a {@link SmartLifecycle} stop/restart, new instances of + *
Note that after a {@link SmartLifecycle} stop/restart, new instances of * the configured {@link LoopResources} and {@link ConnectionProvider} are - * created, so any references to those should be updated. + * created, so any references to those should be updated. However, this factory + * does not participate in {@linkplain #isPauseable() pause} scenarios. * * @author Rossen Stoyanchev * @author Brian Clozel * @author Sebastien Deleuze * @author Juergen Hoeller + * @author Sam Brannen * @since 6.1 */ public class ReactorResourceFactory @@ -328,6 +330,16 @@ public class ReactorResourceFactory return this.running; } + /** + * Returns {@code false} to indicate that a {@code ReactorResourceFactory} + * should be skipped in a pause scenario. + * @since 7.0 + */ + @Override + public boolean isPauseable() { + return false; + } + @Override public int getPhase() { // Same as plain Lifecycle diff --git a/spring-web/src/test/java/org/springframework/http/client/ReactorResourceFactoryTests.java b/spring-web/src/test/java/org/springframework/http/client/ReactorResourceFactoryTests.java index d34dd9ef14b..0121ec8636f 100644 --- a/spring-web/src/test/java/org/springframework/http/client/ReactorResourceFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/http/client/ReactorResourceFactoryTests.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; * @author Rossen Stoyanchev * @author Sebastien Deleuze * @author Juergen Hoeller + * @author Sam Brannen */ class ReactorResourceFactoryTests { @@ -157,7 +158,7 @@ class ReactorResourceFactoryTests { } @Test - void restartWithGlobalResources() { + void stopAndStartWithGlobalResources() { this.resourceFactory.setUseGlobalResources(true); this.resourceFactory.start(); this.resourceFactory.stop(); @@ -174,7 +175,7 @@ class ReactorResourceFactoryTests { } @Test - void restartWithLocalResources() { + void stopAndStartWithLocalResources() { this.resourceFactory.setUseGlobalResources(false); this.resourceFactory.start(); this.resourceFactory.stop(); @@ -197,7 +198,7 @@ class ReactorResourceFactoryTests { } @Test - void restartWithExternalResources() { + void stopAndStartWithExternalResources() { this.resourceFactory.setUseGlobalResources(false); this.resourceFactory.setConnectionProvider(this.connectionProvider); this.resourceFactory.setLoopResources(this.loopResources); @@ -220,7 +221,7 @@ class ReactorResourceFactoryTests { } @Test - void restartWithinApplicationContext() { + void stopAndStartWithinApplicationContext() { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean(ReactorResourceFactory.class); context.refresh(); @@ -241,9 +242,42 @@ class ReactorResourceFactoryTests { assertThat(resourceFactory.getConnectionProvider()).isSameAs(globalResources); assertThat(resourceFactory.getLoopResources()).isSameAs(globalResources); assertThat(globalResources.isDisposed()).isFalse(); + + context.close(); + assertThat(resourceFactory.isRunning()).isFalse(); + assertThat(globalResources.isDisposed()).isTrue(); + } + + @Test // gh-35585 + void pauseAndRestartWithinApplicationContext() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean(ReactorResourceFactory.class); + context.refresh(); + + ReactorResourceFactory resourceFactory = context.getBean(ReactorResourceFactory.class); + assertThat(resourceFactory.isRunning()).isTrue(); + + HttpResources globalResources = HttpResources.get(); + assertThat(resourceFactory.getConnectionProvider()).isSameAs(globalResources); + assertThat(resourceFactory.getLoopResources()).isSameAs(globalResources); + assertThat(globalResources.isDisposed()).isFalse(); + + context.pause(); + globalResources = HttpResources.get(); + assertThat(resourceFactory.isRunning()).isTrue(); + assertThat(resourceFactory.getConnectionProvider()).isSameAs(globalResources); + assertThat(resourceFactory.getLoopResources()).isSameAs(globalResources); + assertThat(globalResources.isDisposed()).isFalse(); + + context.restart(); + globalResources = HttpResources.get(); + assertThat(resourceFactory.isRunning()).isTrue(); + assertThat(resourceFactory.getConnectionProvider()).isSameAs(globalResources); + assertThat(resourceFactory.getLoopResources()).isSameAs(globalResources); assertThat(globalResources.isDisposed()).isFalse(); context.close(); + assertThat(resourceFactory.isRunning()).isFalse(); assertThat(globalResources.isDisposed()).isTrue(); }