Skip to content

Commit 88917b5

Browse files
committed
Merge branch '3.2.x'
Closes gh-40846
2 parents 336dcab + 39ab959 commit 88917b5

File tree

6 files changed

+73
-5
lines changed

6 files changed

+73
-5
lines changed

spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/web/graceful-shutdown.adoc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44
Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and servlet-based web applications.
55
It occurs as part of closing the application context and is performed in the earliest phase of stopping `SmartLifecycle` beans.
66
This stop processing uses a timeout which provides a grace period during which existing requests will be allowed to complete but no new requests will be permitted.
7+
78
The exact way in which new requests are not permitted varies depending on the web server that is being used.
8-
Jetty, Reactor Netty, and Tomcat will stop accepting requests at the network layer.
9-
Undertow will accept requests but respond immediately with a service unavailable (503) response.
9+
Implementations may stop accepting requests at the network layer, or they may return a response with a specific HTTP status code or HTTP header.
10+
The use of persistent connections can also change the way that requests stop being accepted.
11+
12+
TIP: To learn about more the specific method used with your web server, see the `shutDownGracefully` javadoc for xref:api:java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[TomcatWebServer], xref:api:java/org/springframework/boot/web/embedded/netty/NettyWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[NettyWebServer], xref:api:java/org/springframework/boot/web/embedded/jetty/JettyWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[JettyWebServer] or xref:api:java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[UndertowWebServer].
13+
14+
Jetty, Reactor Netty, and Tomcat will stop accepting new requests at the network layer.
15+
Undertow will accept new connections but respond immediately with a service unavailable (503) response.
1016

1117
NOTE: Graceful shutdown with Tomcat requires Tomcat 9.0.33 or later.
1218

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ private int getLocalPort(Connector connector) {
299299
return 0;
300300
}
301301

302+
/**
303+
* Initiates a graceful shutdown of the Jetty web server. Handling of new requests is
304+
* prevented and the given {@code callback} is invoked at the end of the attempt. The
305+
* attempt can be explicitly ended by invoking {@link #stop}.
306+
* <p>
307+
* Once shutdown has been initiated Jetty will reject any new connections. Requests on
308+
* existing connections will be accepted, however, a {@code Connection: close} header
309+
* will be returned in the response.
310+
*/
302311
@Override
303312
public void shutDownGracefully(GracefulShutdownCallback callback) {
304313
if (this.gracefulShutdown == null) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,14 @@ private boolean isPermissionDenied(Throwable bindExceptionCause) {
197197
return false;
198198
}
199199

200+
/**
201+
* Initiates a graceful shutdown of the Netty web server. Handling of new requests is
202+
* prevented and the given {@code callback} is invoked at the end of the attempt. The
203+
* attempt can be explicitly ended by invoking {@link #stop}.
204+
* <p>
205+
* Once shutdown has been initiated Netty will reject any new connections. Requests +
206+
* on existing idle connections will also be rejected.
207+
*/
200208
@Override
201209
public void shutDownGracefully(GracefulShutdownCallback callback) {
202210
if (this.gracefulShutdown == null) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatWebServer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,14 @@ public Tomcat getTomcat() {
441441
return this.tomcat;
442442
}
443443

444+
/**
445+
* Initiates a graceful shutdown of the Tomcat web server. Handling of new requests is
446+
* prevented and the given {@code callback} is invoked at the end of the attempt. The
447+
* attempt can be explicitly ended by invoking {@link #stop}.
448+
* <p>
449+
* Once shutdown has been initiated Tomcat will reject any new connections. Requests
450+
* on existing idle connections will also be rejected.
451+
*/
444452
@Override
445453
public void shutDownGracefully(GracefulShutdownCallback callback) {
446454
if (this.gracefulShutdown == null) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowWebServer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,14 @@ public Undertow getUndertow() {
308308
return this.undertow;
309309
}
310310

311+
/**
312+
* Initiates a graceful shutdown of the Undertow web server. Handling of new requests
313+
* is prevented and the given {@code callback} is invoked at the end of the attempt.
314+
* The attempt can be explicitly ended by invoking {@link #stop}.
315+
* <p>
316+
* Once shutdown has been initiated Undertow will return an {@code HTTP 503} response
317+
* for any new or existing connections.
318+
*/
311319
@Override
312320
public void shutDownGracefully(GracefulShutdownCallback callback) {
313321
if (this.gracefulShutdown == null) {

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactoryTests.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-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.
@@ -36,6 +36,8 @@
3636
import io.undertow.servlet.api.DeploymentInfo;
3737
import io.undertow.servlet.api.ServletContainer;
3838
import jakarta.servlet.ServletRegistration.Dynamic;
39+
import org.apache.hc.client5.http.classic.HttpClient;
40+
import org.apache.hc.client5.http.impl.classic.HttpClients;
3941
import org.apache.hc.core5.http.HttpResponse;
4042
import org.apache.jasper.servlet.JspServlet;
4143
import org.awaitility.Awaitility;
@@ -212,18 +214,45 @@ void whenServerIsShuttingDownGracefullyThenRequestsAreRejectedWithServiceUnavail
212214
this.webServer.stop();
213215
}
214216

217+
@Test
218+
void whenServerIsShuttingDownARequestOnAnIdleConnectionAreRejectedWithServiceUnavailable() throws Exception {
219+
AbstractServletWebServerFactory factory = getFactory();
220+
factory.setShutdown(Shutdown.GRACEFUL);
221+
BlockingServlet blockingServlet = new BlockingServlet();
222+
this.webServer = factory.getWebServer((context) -> {
223+
Dynamic registration = context.addServlet("blockingServlet", blockingServlet);
224+
registration.addMapping("/blocking");
225+
registration.setAsyncSupported(true);
226+
});
227+
HttpClient httpClient = HttpClients.createMinimal();
228+
this.webServer.start();
229+
int port = this.webServer.getPort();
230+
Future<Object> keepAliveRequest = initiateGetRequest(httpClient, port, "/blocking");
231+
blockingServlet.awaitQueue();
232+
blockingServlet.admitOne();
233+
assertThat(keepAliveRequest.get()).isInstanceOf(HttpResponse.class);
234+
Future<Object> request = initiateGetRequest(port, "/blocking");
235+
blockingServlet.awaitQueue();
236+
this.webServer.shutDownGracefully((result) -> {
237+
});
238+
HttpResponse idleConnectionResponse = (HttpResponse) initiateGetRequest(httpClient, port, "/").get();
239+
assertThat(idleConnectionResponse.getCode()).isEqualTo(503);
240+
blockingServlet.admitOne();
241+
Object response = request.get();
242+
assertThat(response).isInstanceOf(HttpResponse.class);
243+
this.webServer.stop();
244+
}
245+
215246
@Test
216247
@Override
217248
@Disabled("Restart after stop is not supported with Undertow")
218249
protected void restartAfterStop() {
219-
220250
}
221251

222252
@Test
223253
@Override
224254
@Disabled("Undertow's architecture prevents separating stop and destroy")
225255
protected void servletContextListenerContextDestroyedIsNotCalledWhenContainerIsStopped() {
226-
227256
}
228257

229258
private void testAccessLog(String prefix, String suffix, String expectedFile)

0 commit comments

Comments
 (0)