Browse Source

Prevent ReactorResourceFactory from participating in pause scenarios

Prior to this commit, ReactorResourceFactory was not restarted properly
when the ApplicationContext was resumed by the TestContext Framework
after a context pause. The reason is that the managed LoopResources
were disposed when ConfigurableApplicationContext.pause() was invoked
and reacquired when ConfigurableApplicationContext.restart() was
invoked.

To address that, this commit overrides isPauseable() in
ReactorResourceFactory to return false, thereby avoiding participation
in pause scenarios.

Closes gh-35585
pull/35588/head
Sam Brannen 3 months ago
parent
commit
d404fdafa0
  1. 18
      spring-web/src/main/java/org/springframework/http/client/ReactorResourceFactory.java
  2. 42
      spring-web/src/test/java/org/springframework/http/client/ReactorResourceFactoryTests.java

18
spring-web/src/main/java/org/springframework/http/client/ReactorResourceFactory.java

@ -34,20 +34,22 @@ import org.springframework.util.Assert; @@ -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}.
*
* <p>This factory implements {@link SmartLifecycle} and is expected typically
* to be declared as a Spring-managed bean.
*
* <p>Notice that after a {@link SmartLifecycle} stop/restart, new instances of
* <p>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 @@ -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

42
spring-web/src/test/java/org/springframework/http/client/ReactorResourceFactoryTests.java

@ -38,6 +38,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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();
}

Loading…
Cancel
Save