Skip to content

Commit fea237c

Browse files
committed
Lazily start and retain HttpClient once resource factory is running
Closes gh-33093
1 parent 8b11ee9 commit fea237c

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @author Brian Clozel
4545
* @author Rossen Stoyanchev
4646
* @author Sebastien Deleuze
47+
* @author Juergen Hoeller
4748
* @since 5.0
4849
* @see reactor.netty.http.client.HttpClient
4950
*/
@@ -63,6 +64,8 @@ public class ReactorClientHttpConnector implements ClientHttpConnector, SmartLif
6364
@Nullable
6465
private volatile HttpClient httpClient;
6566

67+
private boolean lazyStart = false;
68+
6669
private final Object lifecycleMonitor = new Object();
6770

6871

@@ -112,6 +115,9 @@ public ReactorClientHttpConnector(ReactorResourceFactory resourceFactory, Functi
112115
if (resourceFactory.isRunning()) {
113116
this.httpClient = createHttpClient(resourceFactory, mapper);
114117
}
118+
else {
119+
this.lazyStart = true;
120+
}
115121
}
116122

117123
private static HttpClient createHttpClient(ReactorResourceFactory factory, Function<HttpClient, HttpClient> mapper) {
@@ -127,7 +133,21 @@ public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
127133
HttpClient httpClient = this.httpClient;
128134
if (httpClient == null) {
129135
Assert.state(this.resourceFactory != null && this.mapper != null, "Illegal configuration");
130-
httpClient = createHttpClient(this.resourceFactory, this.mapper);
136+
if (this.resourceFactory.isRunning()) {
137+
// Retain HttpClient instance if resource factory has been started in the meantime,
138+
// considering this connector instance as lazily started as well.
139+
synchronized (this.lifecycleMonitor) {
140+
httpClient = this.httpClient;
141+
if (httpClient == null && this.lazyStart) {
142+
httpClient = createHttpClient(this.resourceFactory, this.mapper);
143+
this.httpClient = httpClient;
144+
this.lazyStart = false;
145+
}
146+
}
147+
}
148+
if (httpClient == null) {
149+
httpClient = createHttpClient(this.resourceFactory, this.mapper);
150+
}
131151
}
132152

133153
HttpClient.RequestSender requestSender = httpClient
@@ -176,6 +196,7 @@ public void start() {
176196
synchronized (this.lifecycleMonitor) {
177197
if (this.httpClient == null) {
178198
this.httpClient = createHttpClient(this.resourceFactory, this.mapper);
199+
this.lazyStart = false;
179200
}
180201
}
181202
}
@@ -190,6 +211,7 @@ public void stop() {
190211
if (this.resourceFactory != null && this.mapper != null) {
191212
synchronized (this.lifecycleMonitor) {
192213
this.httpClient = null;
214+
this.lazyStart = false;
193215
}
194216
}
195217
}

spring-web/src/test/java/org/springframework/http/client/reactive/ReactorClientHttpConnectorTests.java

Lines changed: 32 additions & 1 deletion
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.
@@ -16,17 +16,21 @@
1616

1717
package org.springframework.http.client.reactive;
1818

19+
import java.net.URI;
1920
import java.util.function.Function;
2021

2122
import org.junit.jupiter.api.Test;
23+
import reactor.core.publisher.Mono;
2224
import reactor.netty.http.client.HttpClient;
2325

26+
import org.springframework.http.HttpMethod;
2427
import org.springframework.http.client.ReactorResourceFactory;
2528

2629
import static org.assertj.core.api.Assertions.assertThat;
2730

2831
/**
2932
* @author Sebastien Deleuze
33+
* @author Juergen Hoeller
3034
* @since 6.1
3135
*/
3236
class ReactorClientHttpConnectorTests {
@@ -41,6 +45,8 @@ void restartWithDefaultConstructor() {
4145
assertThat(connector.isRunning()).isTrue();
4246
connector.start();
4347
assertThat(connector.isRunning()).isTrue();
48+
connector.stop();
49+
assertThat(connector.isRunning()).isTrue();
4450
}
4551

4652
@Test
@@ -54,6 +60,8 @@ void restartWithHttpClient() {
5460
assertThat(connector.isRunning()).isTrue();
5561
connector.start();
5662
assertThat(connector.isRunning()).isTrue();
63+
connector.stop();
64+
assertThat(connector.isRunning()).isTrue();
5765
}
5866

5967
@Test
@@ -69,6 +77,8 @@ void restartWithExternalResourceFactory() {
6977
assertThat(connector.isRunning()).isFalse();
7078
connector.start();
7179
assertThat(connector.isRunning()).isTrue();
80+
connector.stop();
81+
assertThat(connector.isRunning()).isFalse();
7282
}
7383

7484
@Test
@@ -84,6 +94,27 @@ void lateStartWithExternalResourceFactory() {
8494
assertThat(connector.isRunning()).isFalse();
8595
connector.start();
8696
assertThat(connector.isRunning()).isTrue();
97+
connector.stop();
98+
assertThat(connector.isRunning()).isFalse();
99+
}
100+
101+
@Test
102+
void lazyStartWithExternalResourceFactory() throws Exception {
103+
ReactorResourceFactory resourceFactory = new ReactorResourceFactory();
104+
Function<HttpClient, HttpClient> mapper = Function.identity();
105+
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(resourceFactory, mapper);
106+
assertThat(connector.isRunning()).isFalse();
107+
resourceFactory.start();
108+
connector.connect(HttpMethod.GET, new URI(""), request -> Mono.empty());
109+
assertThat(connector.isRunning()).isTrue();
110+
connector.stop();
111+
assertThat(connector.isRunning()).isFalse();
112+
connector.connect(HttpMethod.GET, new URI(""), request -> Mono.empty());
113+
assertThat(connector.isRunning()).isFalse();
114+
connector.start();
115+
assertThat(connector.isRunning()).isTrue();
116+
connector.stop();
117+
assertThat(connector.isRunning()).isFalse();
87118
}
88119

89120
}

0 commit comments

Comments
 (0)