Skip to content

Commit 39ab959

Browse files
committed
Merge branch '3.1.x' into 3.2.x
Closes gh-40845
2 parents 44619a9 + f743dc8 commit 39ab959

File tree

6 files changed

+75
-7
lines changed

6 files changed

+75
-7
lines changed

spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/graceful-shutdown.adoc

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
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.
44
It occurs as part of closing the application context and is performed in the earliest phase of stopping `SmartLifecycle` beans.
55
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.
6+
67
The exact way in which new requests are not permitted varies depending on the web server that is being used.
7-
Jetty, Reactor Netty, and Tomcat will stop accepting requests at the network layer.
8-
Undertow will accept requests but respond immediately with a service unavailable (503) response.
8+
Implementations may stop accepting requests at the network layer, or they may return a response with a specific HTTP status code or HTTP header.
9+
The use of persistent connections can also change the way that requests stop being accepted.
10+
11+
TIP: To learn about more the specific method used with your web server, see the `shutDownGracefully` javadoc for {spring-boot-module-api}/web/embedded/tomcat/TomcatWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[TomcatWebServer], {spring-boot-module-api}/web/embedded/netty/NettyWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[NettyWebServer], {spring-boot-module-api}/web/embedded/jetty/JettyWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[JettyWebServer] or {spring-boot-module-api}/web/embedded/undertow/UndertowWebServer.html#shutDownGracefully(org.springframework.boot.web.server.GracefulShutdownCallback)[UndertowWebServer].
12+
13+
Jetty, Reactor Netty, and Tomcat will stop accepting new requests at the network layer.
14+
Undertow will accept new connections but respond immediately with a service unavailable (503) response.
915

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

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

+10-1
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.
@@ -293,6 +293,15 @@ private int getLocalPort(Connector connector) {
293293
return 0;
294294
}
295295

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

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

+8
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,14 @@ private boolean isPermissionDenied(Throwable bindExceptionCause) {
196196
return false;
197197
}
198198

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

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

+9-1
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.
@@ -422,6 +422,14 @@ public Tomcat getTomcat() {
422422
return this.tomcat;
423423
}
424424

425+
/**
426+
* Initiates a graceful shutdown of the Tomcat web server. Handling of new requests is
427+
* prevented and the given {@code callback} is invoked at the end of the attempt. The
428+
* attempt can be explicitly ended by invoking {@link #stop}.
429+
* <p>
430+
* Once shutdown has been initiated Tomcat will reject any new connections. Requests
431+
* on existing idle connections will also be rejected.
432+
*/
425433
@Override
426434
public void shutDownGracefully(GracefulShutdownCallback callback) {
427435
if (this.gracefulShutdown == null) {

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

+8
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,14 @@ public int getPort() {
298298
return ports.get(0).getNumber();
299299
}
300300

301+
/**
302+
* Initiates a graceful shutdown of the Undertow web server. Handling of new requests
303+
* is prevented and the given {@code callback} is invoked at the end of the attempt.
304+
* The attempt can be explicitly ended by invoking {@link #stop}.
305+
* <p>
306+
* Once shutdown has been initiated Undertow will return an {@code HTTP 503} response
307+
* for any new or existing connections.
308+
*/
301309
@Override
302310
public void shutDownGracefully(GracefulShutdownCallback callback) {
303311
if (this.gracefulShutdown == null) {

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

+32-3
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)