Skip to content

Commit 017bf45

Browse files
committed
Merge branch '6.1.x'
2 parents 28eb9ae + 524da90 commit 017bf45

File tree

4 files changed

+101
-44
lines changed

4 files changed

+101
-44
lines changed

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -101,7 +101,7 @@ protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body
101101
return result;
102102
}
103103
}
104-
catch (RuntimeException ex) { // Exceptions.ReactiveException is package private
104+
catch (RuntimeException ex) { // Exceptions.ReactiveException is package private
105105
Throwable cause = ex.getCause();
106106

107107
if (cause instanceof UncheckedIOException uioEx) {
@@ -111,7 +111,7 @@ else if (cause instanceof IOException ioEx) {
111111
throw ioEx;
112112
}
113113
else {
114-
throw ex;
114+
throw new IOException(ex.getMessage(), cause);
115115
}
116116
}
117117
}

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

+30-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,7 +26,9 @@
2626

2727
import org.springframework.beans.factory.DisposableBean;
2828
import org.springframework.beans.factory.InitializingBean;
29-
import org.springframework.context.Lifecycle;
29+
import org.springframework.context.ApplicationContext;
30+
import org.springframework.context.ApplicationContextAware;
31+
import org.springframework.context.SmartLifecycle;
3032
import org.springframework.lang.Nullable;
3133
import org.springframework.util.Assert;
3234

@@ -35,20 +37,21 @@
3537
* event loop threads, and {@link ConnectionProvider} for the connection pool,
3638
* within the lifecycle of a Spring {@code ApplicationContext}.
3739
*
38-
* <p>This factory implements {@link InitializingBean}, {@link DisposableBean}
39-
* and {@link Lifecycle} and is expected typically to be declared as a
40-
* Spring-managed bean.
40+
* <p>This factory implements {@link SmartLifecycle} and is expected typically
41+
* to be declared as a Spring-managed bean.
4142
*
42-
* <p>Notice that after a {@link Lifecycle} stop/restart, new instances of
43+
* <p>Notice that after a {@link SmartLifecycle} stop/restart, new instances of
4344
* the configured {@link LoopResources} and {@link ConnectionProvider} are
4445
* created, so any references to those should be updated.
4546
*
4647
* @author Rossen Stoyanchev
4748
* @author Brian Clozel
4849
* @author Sebastien Deleuze
50+
* @author Juergen Hoeller
4951
* @since 6.1
5052
*/
51-
public class ReactorResourceFactory implements InitializingBean, DisposableBean, Lifecycle {
53+
public class ReactorResourceFactory
54+
implements ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {
5255

5356
private boolean useGlobalResources = true;
5457

@@ -73,6 +76,9 @@ public class ReactorResourceFactory implements InitializingBean, DisposableBean,
7376

7477
private Duration shutdownTimeout = Duration.ofSeconds(LoopResources.DEFAULT_SHUTDOWN_TIMEOUT);
7578

79+
@Nullable
80+
private ApplicationContext applicationContext;
81+
7682
private volatile boolean running;
7783

7884
private final Object lifecycleMonitor = new Object();
@@ -202,15 +208,30 @@ public void setShutdownTimeout(Duration shutdownTimeout) {
202208
this.shutdownTimeout = shutdownTimeout;
203209
}
204210

211+
/**
212+
* Setting an {@link ApplicationContext} is optional: If set, Reactor resources
213+
* will be initialized in the {@link #start() lifecycle start} phase and closed
214+
* in the {@link #stop() lifecycle stop} phase. If not set, it will happen in
215+
* {@link #afterPropertiesSet()} and {@link #destroy()}, respectively.
216+
*/
217+
@Override
218+
public void setApplicationContext(ApplicationContext applicationContext) {
219+
this.applicationContext = applicationContext;
220+
}
221+
205222

206223
@Override
207224
public void afterPropertiesSet() {
208-
start();
225+
if (this.applicationContext == null) {
226+
start();
227+
}
209228
}
210229

211230
@Override
212231
public void destroy() {
213-
stop();
232+
if (this.applicationContext == null) {
233+
stop();
234+
}
214235
}
215236

216237
@Override
+2-1
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
/**
2929
* @author Arjen Poutsma
3030
* @author Sebastien Deleuze
31+
* @since 6.1
3132
*/
32-
class ReactorNettyClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests {
33+
class ReactorNettyClientRequestFactoryTests extends AbstractHttpRequestFactoryTests {
3334

3435
@Override
3536
protected ClientHttpRequestFactory createRequestFactory() {
+66-31
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.http.client.reactive;
17+
package org.springframework.http.client;
1818

1919
import java.time.Duration;
2020
import java.util.concurrent.atomic.AtomicBoolean;
@@ -24,7 +24,7 @@
2424
import reactor.netty.resources.ConnectionProvider;
2525
import reactor.netty.resources.LoopResources;
2626

27-
import org.springframework.http.client.ReactorResourceFactory;
27+
import org.springframework.context.support.GenericApplicationContext;
2828

2929
import static org.assertj.core.api.Assertions.assertThat;
3030
import static org.mockito.ArgumentMatchers.eq;
@@ -37,6 +37,7 @@
3737
*
3838
* @author Rossen Stoyanchev
3939
* @author Sebastien Deleuze
40+
* @author Juergen Hoeller
4041
*/
4142
class ReactorResourceFactoryTests {
4243

@@ -49,37 +50,34 @@ class ReactorResourceFactoryTests {
4950

5051
@Test
5152
void globalResources() {
52-
5353
this.resourceFactory.setUseGlobalResources(true);
54-
this.resourceFactory.afterPropertiesSet();
54+
this.resourceFactory.start();
5555

5656
HttpResources globalResources = HttpResources.get();
5757
assertThat(this.resourceFactory.getConnectionProvider()).isSameAs(globalResources);
5858
assertThat(this.resourceFactory.getLoopResources()).isSameAs(globalResources);
5959
assertThat(globalResources.isDisposed()).isFalse();
6060

61-
this.resourceFactory.destroy();
61+
this.resourceFactory.stop();
6262

6363
assertThat(globalResources.isDisposed()).isTrue();
6464
}
6565

6666
@Test
6767
void globalResourcesWithConsumer() {
68-
6968
AtomicBoolean invoked = new AtomicBoolean();
7069

7170
this.resourceFactory.addGlobalResourcesConsumer(httpResources -> invoked.set(true));
72-
this.resourceFactory.afterPropertiesSet();
71+
this.resourceFactory.start();
7372

7473
assertThat(invoked.get()).isTrue();
75-
this.resourceFactory.destroy();
74+
this.resourceFactory.stop();
7675
}
7776

7877
@Test
7978
void localResources() {
80-
8179
this.resourceFactory.setUseGlobalResources(false);
82-
this.resourceFactory.afterPropertiesSet();
80+
this.resourceFactory.start();
8381

8482
ConnectionProvider connectionProvider = this.resourceFactory.getConnectionProvider();
8583
LoopResources loopResources = this.resourceFactory.getLoopResources();
@@ -91,19 +89,18 @@ void localResources() {
9189
// assertFalse(connectionProvider.isDisposed());
9290
assertThat(loopResources.isDisposed()).isFalse();
9391

94-
this.resourceFactory.destroy();
92+
this.resourceFactory.stop();
9593

9694
assertThat(connectionProvider.isDisposed()).isTrue();
9795
assertThat(loopResources.isDisposed()).isTrue();
9896
}
9997

10098
@Test
10199
void localResourcesViaSupplier() {
102-
103100
this.resourceFactory.setUseGlobalResources(false);
104101
this.resourceFactory.setConnectionProviderSupplier(() -> this.connectionProvider);
105102
this.resourceFactory.setLoopResourcesSupplier(() -> this.loopResources);
106-
this.resourceFactory.afterPropertiesSet();
103+
this.resourceFactory.start();
107104

108105
ConnectionProvider connectionProvider = this.resourceFactory.getConnectionProvider();
109106
LoopResources loopResources = this.resourceFactory.getLoopResources();
@@ -113,9 +110,9 @@ void localResourcesViaSupplier() {
113110

114111
verifyNoMoreInteractions(this.connectionProvider, this.loopResources);
115112

116-
this.resourceFactory.destroy();
113+
this.resourceFactory.stop();
117114

118-
// Managed (destroy disposes)..
115+
// Managed (stop disposes)..
119116
verify(this.connectionProvider).disposeLater();
120117
verify(this.loopResources).disposeLater(eq(Duration.ofSeconds(LoopResources.DEFAULT_SHUTDOWN_QUIET_PERIOD)), eq(Duration.ofSeconds(LoopResources.DEFAULT_SHUTDOWN_TIMEOUT)));
121118
verifyNoMoreInteractions(this.connectionProvider, this.loopResources);
@@ -130,8 +127,8 @@ void customShutdownDurations() {
130127
this.resourceFactory.setLoopResourcesSupplier(() -> this.loopResources);
131128
this.resourceFactory.setShutdownQuietPeriod(quietPeriod);
132129
this.resourceFactory.setShutdownTimeout(shutdownTimeout);
133-
this.resourceFactory.afterPropertiesSet();
134-
this.resourceFactory.destroy();
130+
this.resourceFactory.start();
131+
this.resourceFactory.stop();
135132

136133
verify(this.connectionProvider).disposeLater();
137134
verify(this.loopResources).disposeLater(eq(quietPeriod), eq(shutdownTimeout));
@@ -140,11 +137,10 @@ void customShutdownDurations() {
140137

141138
@Test
142139
void externalResources() {
143-
144140
this.resourceFactory.setUseGlobalResources(false);
145141
this.resourceFactory.setConnectionProvider(this.connectionProvider);
146142
this.resourceFactory.setLoopResources(this.loopResources);
147-
this.resourceFactory.afterPropertiesSet();
143+
this.resourceFactory.start();
148144

149145
ConnectionProvider connectionProvider = this.resourceFactory.getConnectionProvider();
150146
LoopResources loopResources = this.resourceFactory.getLoopResources();
@@ -154,17 +150,16 @@ void externalResources() {
154150

155151
verifyNoMoreInteractions(this.connectionProvider, this.loopResources);
156152

157-
this.resourceFactory.destroy();
153+
this.resourceFactory.stop();
158154

159-
// Not managed (destroy has no impact)
155+
// Not managed (stop has no impact)
160156
verifyNoMoreInteractions(this.connectionProvider, this.loopResources);
161157
}
162158

163159
@Test
164160
void restartWithGlobalResources() {
165-
166161
this.resourceFactory.setUseGlobalResources(true);
167-
this.resourceFactory.afterPropertiesSet();
162+
this.resourceFactory.start();
168163
this.resourceFactory.stop();
169164
this.resourceFactory.start();
170165

@@ -173,16 +168,15 @@ void restartWithGlobalResources() {
173168
assertThat(this.resourceFactory.getLoopResources()).isSameAs(globalResources);
174169
assertThat(globalResources.isDisposed()).isFalse();
175170

176-
this.resourceFactory.destroy();
171+
this.resourceFactory.stop();
177172

178173
assertThat(globalResources.isDisposed()).isTrue();
179174
}
180175

181176
@Test
182177
void restartWithLocalResources() {
183-
184178
this.resourceFactory.setUseGlobalResources(false);
185-
this.resourceFactory.afterPropertiesSet();
179+
this.resourceFactory.start();
186180
this.resourceFactory.stop();
187181
this.resourceFactory.start();
188182

@@ -196,19 +190,18 @@ void restartWithLocalResources() {
196190
// assertFalse(connectionProvider.isDisposed());
197191
assertThat(loopResources.isDisposed()).isFalse();
198192

199-
this.resourceFactory.destroy();
193+
this.resourceFactory.stop();
200194

201195
assertThat(connectionProvider.isDisposed()).isTrue();
202196
assertThat(loopResources.isDisposed()).isTrue();
203197
}
204198

205199
@Test
206200
void restartWithExternalResources() {
207-
208201
this.resourceFactory.setUseGlobalResources(false);
209202
this.resourceFactory.setConnectionProvider(this.connectionProvider);
210203
this.resourceFactory.setLoopResources(this.loopResources);
211-
this.resourceFactory.afterPropertiesSet();
204+
this.resourceFactory.start();
212205
this.resourceFactory.stop();
213206
this.resourceFactory.start();
214207

@@ -220,10 +213,52 @@ void restartWithExternalResources() {
220213

221214
verifyNoMoreInteractions(this.connectionProvider, this.loopResources);
222215

223-
this.resourceFactory.destroy();
216+
this.resourceFactory.stop();
224217

225-
// Not managed (destroy has no impact)...
218+
// Not managed (stop has no impact)...
226219
verifyNoMoreInteractions(this.connectionProvider, this.loopResources);
227220
}
228221

222+
@Test
223+
void restartWithinApplicationContext() {
224+
GenericApplicationContext context = new GenericApplicationContext();
225+
context.registerBean(ReactorResourceFactory.class);
226+
context.refresh();
227+
228+
ReactorResourceFactory resourceFactory = context.getBean(ReactorResourceFactory.class);
229+
assertThat(resourceFactory.isRunning()).isTrue();
230+
231+
HttpResources globalResources = HttpResources.get();
232+
assertThat(resourceFactory.getConnectionProvider()).isSameAs(globalResources);
233+
assertThat(resourceFactory.getLoopResources()).isSameAs(globalResources);
234+
assertThat(globalResources.isDisposed()).isFalse();
235+
236+
context.stop();
237+
assertThat(globalResources.isDisposed()).isTrue();
238+
239+
context.start();
240+
globalResources = HttpResources.get();
241+
assertThat(resourceFactory.getConnectionProvider()).isSameAs(globalResources);
242+
assertThat(resourceFactory.getLoopResources()).isSameAs(globalResources);
243+
assertThat(globalResources.isDisposed()).isFalse();
244+
assertThat(globalResources.isDisposed()).isFalse();
245+
246+
context.close();
247+
assertThat(globalResources.isDisposed()).isTrue();
248+
}
249+
250+
@Test
251+
void doNotStartBeforeApplicationContextFinish() {
252+
GenericApplicationContext context = new GenericApplicationContext() {
253+
@Override
254+
protected void finishRefresh() {
255+
}
256+
};
257+
context.registerBean(ReactorResourceFactory.class);
258+
context.refresh();
259+
260+
ReactorResourceFactory resourceFactory = context.getBeanFactory().getBean(ReactorResourceFactory.class);
261+
assertThat(resourceFactory.isRunning()).isFalse();
262+
}
263+
229264
}

0 commit comments

Comments
 (0)