mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Polish
This commit is contained in:
parent
4d521e712f
commit
12381467da
@ -20,6 +20,7 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||
import org.springframework.boot.web.error.ErrorAttributeOptions.Include;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@ -60,16 +61,16 @@ public class ManagementErrorEndpoint {
|
||||
private ErrorAttributeOptions getErrorAttributeOptions(ServletWebRequest request) {
|
||||
ErrorAttributeOptions options = ErrorAttributeOptions.defaults();
|
||||
if (this.errorProperties.isIncludeException()) {
|
||||
options = options.including(ErrorAttributeOptions.Include.EXCEPTION);
|
||||
options = options.including(Include.EXCEPTION);
|
||||
}
|
||||
if (includeStackTrace(request)) {
|
||||
options = options.including(ErrorAttributeOptions.Include.STACK_TRACE);
|
||||
options = options.including(Include.STACK_TRACE);
|
||||
}
|
||||
if (includeMessage(request)) {
|
||||
options = options.including(ErrorAttributeOptions.Include.MESSAGE);
|
||||
options = options.including(Include.MESSAGE);
|
||||
}
|
||||
if (includeBindingErrors(request)) {
|
||||
options = options.including(ErrorAttributeOptions.Include.BINDING_ERRORS);
|
||||
options = options.including(Include.BINDING_ERRORS);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.web.servlet;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -30,6 +30,7 @@ import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
/**
|
||||
* Tests for {@link ManagementErrorEndpoint}.
|
||||
@ -108,27 +109,27 @@ class ManagementErrorEndpointTests {
|
||||
@Test
|
||||
void errorResponseWithCustomErrorAttributesUsingDeprecatedApi() {
|
||||
ErrorAttributes attributes = new ErrorAttributes() {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("message", "An error occurred");
|
||||
return response;
|
||||
return Collections.singletonMap("message", "An error occurred");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable getError(WebRequest webRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
ManagementErrorEndpoint endpoint = new ManagementErrorEndpoint(attributes, this.errorProperties);
|
||||
Map<String, Object> response = endpoint.invoke(new ServletWebRequest(new MockHttpServletRequest()));
|
||||
assertThat(response).hasSize(1);
|
||||
assertThat(response).containsEntry("message", "An error occurred");
|
||||
assertThat(response).containsExactly(entry("message", "An error occurred"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorResponseWithDefaultErrorAttributesSubclassUsingDeprecatedApiAndDelegation() {
|
||||
ErrorAttributes attributes = new DefaultErrorAttributes() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||
@ -138,6 +139,7 @@ class ManagementErrorEndpointTests {
|
||||
response.remove("path");
|
||||
return response;
|
||||
}
|
||||
|
||||
};
|
||||
ManagementErrorEndpoint endpoint = new ManagementErrorEndpoint(attributes, this.errorProperties);
|
||||
Map<String, Object> response = endpoint.invoke(new ServletWebRequest(new MockHttpServletRequest()));
|
||||
@ -150,18 +152,16 @@ class ManagementErrorEndpointTests {
|
||||
@Test
|
||||
void errorResponseWithDefaultErrorAttributesSubclassUsingDeprecatedApiWithoutDelegation() {
|
||||
ErrorAttributes attributes = new DefaultErrorAttributes() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("error", "custom error");
|
||||
return response;
|
||||
return Collections.singletonMap("error", "custom error");
|
||||
}
|
||||
|
||||
};
|
||||
ManagementErrorEndpoint endpoint = new ManagementErrorEndpoint(attributes, this.errorProperties);
|
||||
Map<String, Object> response = endpoint.invoke(new ServletWebRequest(new MockHttpServletRequest()));
|
||||
assertThat(response).hasSize(1);
|
||||
assertThat(response).containsEntry("error", "custom error");
|
||||
assertThat(response).containsExactly(entry("error", "custom error"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,12 +56,21 @@ final class RemoteHttpClientTransport extends HttpClientTransport {
|
||||
|
||||
static RemoteHttpClientTransport createIfPossible(Environment environment, SslContextFactory sslContextFactory) {
|
||||
String host = environment.get(DOCKER_HOST);
|
||||
if (host == null || Files.exists(Paths.get(host))) {
|
||||
if (host == null || isLocalFileReference(host)) {
|
||||
return null;
|
||||
}
|
||||
return create(environment, sslContextFactory, HttpHost.create(host));
|
||||
}
|
||||
|
||||
private static boolean isLocalFileReference(String host) {
|
||||
try {
|
||||
return Files.exists(Paths.get(host));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static RemoteHttpClientTransport create(Environment environment, SslContextFactory sslContextFactory,
|
||||
HttpHost tcpHost) {
|
||||
HttpClientBuilder builder = HttpClients.custom();
|
||||
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.embedded.jetty;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
import org.springframework.boot.web.server.GracefulShutdownCallback;
|
||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
||||
/**
|
||||
* Handles Jetty graceful shutdown.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
final class GracefulShutdown {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(JettyWebServer.class);
|
||||
|
||||
private final Server server;
|
||||
|
||||
private final Supplier<Integer> activeRequests;
|
||||
|
||||
private volatile boolean shuttingDown = false;
|
||||
|
||||
GracefulShutdown(Server server, Supplier<Integer> activeRequests) {
|
||||
this.server = server;
|
||||
this.activeRequests = activeRequests;
|
||||
}
|
||||
|
||||
void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
for (Connector connector : this.server.getConnectors()) {
|
||||
shutdown(connector);
|
||||
}
|
||||
this.shuttingDown = true;
|
||||
new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start();
|
||||
|
||||
}
|
||||
|
||||
private void shutdown(Connector connector) {
|
||||
try {
|
||||
connector.shutdown().get();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
|
||||
private void awaitShutdown(GracefulShutdownCallback callback) {
|
||||
while (this.shuttingDown && this.activeRequests.get() > 0) {
|
||||
sleep(100);
|
||||
}
|
||||
this.shuttingDown = false;
|
||||
long activeRequests = this.activeRequests.get();
|
||||
if (activeRequests == 0) {
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}
|
||||
else {
|
||||
logger.info(LogMessage.format("Graceful shutdown aborted with %d request(s) still active", activeRequests));
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
private void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
void abort() {
|
||||
this.shuttingDown = false;
|
||||
}
|
||||
|
||||
}
|
@ -20,8 +20,6 @@ import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
@ -286,12 +284,11 @@ public class JettyWebServer implements WebServer {
|
||||
|
||||
@Override
|
||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
if (this.gracefulShutdown != null) {
|
||||
this.gracefulShutdown.shutDownGracefully(callback);
|
||||
}
|
||||
else {
|
||||
if (this.gracefulShutdown == null) {
|
||||
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
|
||||
return;
|
||||
}
|
||||
this.gracefulShutdown.shutDownGracefully(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,68 +299,4 @@ public class JettyWebServer implements WebServer {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
static final class GracefulShutdown {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GracefulShutdown.class);
|
||||
|
||||
private final Server server;
|
||||
|
||||
private final Supplier<Integer> activeRequests;
|
||||
|
||||
private volatile boolean shuttingDown = false;
|
||||
|
||||
private GracefulShutdown(Server server, Supplier<Integer> activeRequests) {
|
||||
this.server = server;
|
||||
this.activeRequests = activeRequests;
|
||||
}
|
||||
|
||||
private void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
for (Connector connector : this.server.getConnectors()) {
|
||||
shutdown(connector);
|
||||
}
|
||||
this.shuttingDown = true;
|
||||
new Thread(() -> {
|
||||
while (this.shuttingDown && this.activeRequests.get() > 0) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
this.shuttingDown = false;
|
||||
long activeRequests = this.activeRequests.get();
|
||||
if (activeRequests == 0) {
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}
|
||||
else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Graceful shutdown aborted with " + activeRequests + " request(s) still active");
|
||||
}
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
}
|
||||
}, "jetty-shutdown").start();
|
||||
|
||||
}
|
||||
|
||||
private void shutdown(Connector connector) {
|
||||
try {
|
||||
connector.shutdown().get();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (ExecutionException ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
|
||||
private void abort() {
|
||||
this.shuttingDown = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.embedded.netty;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.netty.DisposableServer;
|
||||
|
||||
import org.springframework.boot.web.server.GracefulShutdownCallback;
|
||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||
|
||||
/**
|
||||
* Handles Netty graceful shutdown.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
final class GracefulShutdown {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GracefulShutdown.class);
|
||||
|
||||
private final Supplier<DisposableServer> disposableServer;
|
||||
|
||||
private volatile Thread shutdownThread;
|
||||
|
||||
private volatile boolean shuttingDown;
|
||||
|
||||
GracefulShutdown(Supplier<DisposableServer> disposableServer) {
|
||||
this.disposableServer = disposableServer;
|
||||
}
|
||||
|
||||
void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
DisposableServer server = this.disposableServer.get();
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
this.shutdownThread = new Thread(() -> doShutdown(callback, server), "netty-shutdown");
|
||||
this.shutdownThread.start();
|
||||
}
|
||||
|
||||
private void doShutdown(GracefulShutdownCallback callback, DisposableServer server) {
|
||||
this.shuttingDown = true;
|
||||
try {
|
||||
server.disposeNow(Duration.ofMillis(Long.MAX_VALUE));
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.info("Graceful shutdown aborted with one or more active requests");
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
}
|
||||
finally {
|
||||
this.shutdownThread = null;
|
||||
this.shuttingDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
void abort() {
|
||||
Thread shutdownThread = this.shutdownThread;
|
||||
if (shutdownThread != null) {
|
||||
while (!this.shuttingDown) {
|
||||
sleep(50);
|
||||
}
|
||||
this.shutdownThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -21,7 +21,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.channel.unix.Errors.NativeIoException;
|
||||
@ -125,12 +124,11 @@ public class NettyWebServer implements WebServer {
|
||||
|
||||
@Override
|
||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
if (this.gracefulShutdown != null) {
|
||||
this.gracefulShutdown.shutDownGracefully(callback);
|
||||
}
|
||||
else {
|
||||
if (this.gracefulShutdown == null) {
|
||||
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
|
||||
return;
|
||||
}
|
||||
this.gracefulShutdown.shutDownGracefully(callback);
|
||||
}
|
||||
|
||||
private DisposableServer startHttpServer() {
|
||||
@ -197,60 +195,4 @@ public class NettyWebServer implements WebServer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static final class GracefulShutdown {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GracefulShutdown.class);
|
||||
|
||||
private final Supplier<DisposableServer> disposableServer;
|
||||
|
||||
private volatile Thread shutdownThread;
|
||||
|
||||
private volatile boolean shuttingDown;
|
||||
|
||||
private GracefulShutdown(Supplier<DisposableServer> disposableServer) {
|
||||
this.disposableServer = disposableServer;
|
||||
}
|
||||
|
||||
private void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
DisposableServer server = this.disposableServer.get();
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
this.shutdownThread = new Thread(() -> {
|
||||
this.shuttingDown = true;
|
||||
try {
|
||||
server.disposeNow(Duration.ofMillis(Long.MAX_VALUE));
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.info("Graceful shutdown aborted with one or more active requests");
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
}
|
||||
finally {
|
||||
this.shutdownThread = null;
|
||||
this.shuttingDown = false;
|
||||
}
|
||||
}, "netty-shutdown");
|
||||
this.shutdownThread.start();
|
||||
}
|
||||
|
||||
private void abort() {
|
||||
Thread shutdownThread = this.shutdownThread;
|
||||
if (shutdownThread != null) {
|
||||
while (!this.shuttingDown) {
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
this.shutdownThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.embedded.tomcat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Service;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.core.StandardWrapper;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.web.server.GracefulShutdownCallback;
|
||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||
|
||||
/**
|
||||
* Handles Tomcat graceful shutdown.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
final class GracefulShutdown {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GracefulShutdown.class);
|
||||
|
||||
private final Tomcat tomcat;
|
||||
|
||||
private volatile boolean aborted = false;
|
||||
|
||||
GracefulShutdown(Tomcat tomcat) {
|
||||
this.tomcat = tomcat;
|
||||
}
|
||||
|
||||
void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
new Thread(() -> doShutdown(callback), "tomcat-shutdown").start();
|
||||
}
|
||||
|
||||
private void doShutdown(GracefulShutdownCallback callback) {
|
||||
List<Connector> connectors = getConnectors();
|
||||
connectors.forEach(this::close);
|
||||
try {
|
||||
for (Container host : this.tomcat.getEngine().findChildren()) {
|
||||
for (Container context : host.findChildren()) {
|
||||
while (isActive(context)) {
|
||||
if (this.aborted) {
|
||||
logger.info("Graceful shutdown aborted with one or more requests still active");
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
return;
|
||||
}
|
||||
Thread.sleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}
|
||||
|
||||
private List<Connector> getConnectors() {
|
||||
List<Connector> connectors = new ArrayList<>();
|
||||
for (Service service : this.tomcat.getServer().findServices()) {
|
||||
Collections.addAll(connectors, service.findConnectors());
|
||||
}
|
||||
return connectors;
|
||||
}
|
||||
|
||||
private void close(Connector connector) {
|
||||
connector.pause();
|
||||
connector.getProtocolHandler().closeServerSocketGraceful();
|
||||
}
|
||||
|
||||
private boolean isActive(Container context) {
|
||||
try {
|
||||
if (((StandardContext) context).getInProgressAsyncCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
for (Container wrapper : context.findChildren()) {
|
||||
if (((StandardWrapper) wrapper).getCountAllocated() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void abort() {
|
||||
this.aborted = true;
|
||||
}
|
||||
|
||||
}
|
@ -179,7 +179,7 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
|
||||
int port = Math.max(getPort(), 0);
|
||||
connector.setPort(port);
|
||||
if (StringUtils.hasText(getServerHeader())) {
|
||||
connector.setAttribute("server", getServerHeader());
|
||||
connector.setProperty("server", getServerHeader());
|
||||
}
|
||||
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
|
||||
customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
|
||||
|
@ -301,7 +301,7 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
|
||||
int port = Math.max(getPort(), 0);
|
||||
connector.setPort(port);
|
||||
if (StringUtils.hasText(getServerHeader())) {
|
||||
connector.setAttribute("server", getServerHeader());
|
||||
connector.setProperty("server", getServerHeader());
|
||||
}
|
||||
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
|
||||
customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
|
||||
|
@ -16,11 +16,8 @@
|
||||
|
||||
package org.springframework.boot.web.embedded.tomcat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
@ -35,8 +32,6 @@ import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.Service;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.core.StandardWrapper;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@ -388,86 +383,11 @@ public class TomcatWebServer implements WebServer {
|
||||
|
||||
@Override
|
||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
if (this.gracefulShutdown != null) {
|
||||
this.gracefulShutdown.shutDownGracefully(callback);
|
||||
}
|
||||
else {
|
||||
if (this.gracefulShutdown == null) {
|
||||
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class GracefulShutdown {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GracefulShutdown.class);
|
||||
|
||||
private final Tomcat tomcat;
|
||||
|
||||
private volatile boolean aborted = false;
|
||||
|
||||
private GracefulShutdown(Tomcat tomcat) {
|
||||
this.tomcat = tomcat;
|
||||
}
|
||||
|
||||
private void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
|
||||
new Thread(() -> {
|
||||
List<Connector> connectors = getConnectors();
|
||||
for (Connector connector : connectors) {
|
||||
connector.pause();
|
||||
connector.getProtocolHandler().closeServerSocketGraceful();
|
||||
}
|
||||
try {
|
||||
for (Container host : this.tomcat.getEngine().findChildren()) {
|
||||
for (Container context : host.findChildren()) {
|
||||
while (active(context)) {
|
||||
if (this.aborted) {
|
||||
logger.info("Graceful shutdown aborted with one or more requests still active");
|
||||
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
|
||||
return;
|
||||
}
|
||||
Thread.sleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
logger.info("Graceful shutdown complete");
|
||||
callback.shutdownComplete(GracefulShutdownResult.IDLE);
|
||||
}, "tomcat-shutdown").start();
|
||||
}
|
||||
|
||||
private void abort() {
|
||||
this.aborted = true;
|
||||
}
|
||||
|
||||
private boolean active(Container context) {
|
||||
try {
|
||||
if (((StandardContext) context).getInProgressAsyncCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
for (Container wrapper : context.findChildren()) {
|
||||
if (((StandardWrapper) wrapper).getCountAllocated() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Connector> getConnectors() {
|
||||
List<Connector> connectors = new ArrayList<>();
|
||||
for (Service service : this.tomcat.getServer().findServices()) {
|
||||
Collections.addAll(connectors, service.findConnectors());
|
||||
}
|
||||
return connectors;
|
||||
}
|
||||
|
||||
this.gracefulShutdown.shutDownGracefully(callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -302,15 +302,14 @@ public class UndertowWebServer implements WebServer {
|
||||
|
||||
@Override
|
||||
public void shutDownGracefully(GracefulShutdownCallback callback) {
|
||||
if (this.gracefulShutdown != null) {
|
||||
logger.info("Commencing graceful shutdown. Wait for active requests to complete");
|
||||
this.gracefulShutdownCallback.set(callback);
|
||||
this.gracefulShutdown.shutdown();
|
||||
this.gracefulShutdown.addShutdownListener((success) -> notifyGracefulCallback(success));
|
||||
}
|
||||
else {
|
||||
if (this.gracefulShutdown == null) {
|
||||
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
|
||||
return;
|
||||
}
|
||||
logger.info("Commencing graceful shutdown. Wait for active requests to complete");
|
||||
this.gracefulShutdownCallback.set(callback);
|
||||
this.gracefulShutdown.shutdown();
|
||||
this.gracefulShutdown.addShutdownListener((success) -> notifyGracefulCallback(success));
|
||||
}
|
||||
|
||||
private void notifyGracefulCallback(boolean success) {
|
||||
|
@ -16,10 +16,6 @@
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.availability.AvailabilityChangeEvent;
|
||||
@ -28,11 +24,7 @@ import org.springframework.boot.web.context.ConfigurableWebServerApplicationCont
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@ -45,7 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
public class ReactiveWebServerApplicationContext extends GenericReactiveWebApplicationContext
|
||||
implements ConfigurableWebServerApplicationContext {
|
||||
|
||||
private volatile ServerManager serverManager;
|
||||
private volatile WebServerManager serverManager;
|
||||
|
||||
private String serverNamespace;
|
||||
|
||||
@ -70,9 +62,9 @@ public class ReactiveWebServerApplicationContext extends GenericReactiveWebAppli
|
||||
super.refresh();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
ServerManager serverManager = this.serverManager;
|
||||
WebServerManager serverManager = this.serverManager;
|
||||
if (serverManager != null) {
|
||||
serverManager.server.stop();
|
||||
serverManager.getWebServer().stop();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
@ -90,12 +82,12 @@ public class ReactiveWebServerApplicationContext extends GenericReactiveWebAppli
|
||||
}
|
||||
|
||||
private void createWebServer() {
|
||||
ServerManager serverManager = this.serverManager;
|
||||
WebServerManager serverManager = this.serverManager;
|
||||
if (serverManager == null) {
|
||||
String webServerFactoryBeanName = getWebServerFactoryBeanName();
|
||||
ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
|
||||
boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
|
||||
this.serverManager = new ServerManager(webServerFactory, this::getHttpHandler, lazyInit);
|
||||
this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);
|
||||
getBeanFactory().registerSingleton("webServerGracefulShutdown",
|
||||
new WebServerGracefulShutdownLifecycle(this.serverManager));
|
||||
getBeanFactory().registerSingleton("webServerStartStop",
|
||||
@ -155,8 +147,8 @@ public class ReactiveWebServerApplicationContext extends GenericReactiveWebAppli
|
||||
*/
|
||||
@Override
|
||||
public WebServer getWebServer() {
|
||||
ServerManager serverManager = this.serverManager;
|
||||
return (serverManager != null) ? serverManager.server : null;
|
||||
WebServerManager serverManager = this.serverManager;
|
||||
return (serverManager != null) ? serverManager.getWebServer() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,160 +161,4 @@ public class ReactiveWebServerApplicationContext extends GenericReactiveWebAppli
|
||||
this.serverNamespace = serverNamespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class used to manage the server and the {@link HttpHandler}, taking care
|
||||
* not to initialize the handler too early.
|
||||
*/
|
||||
final class ServerManager {
|
||||
|
||||
private final WebServer server;
|
||||
|
||||
private DelayedInitializationHttpHandler handler;
|
||||
|
||||
private ServerManager(ReactiveWebServerFactory factory, Supplier<HttpHandler> handlerSupplier,
|
||||
boolean lazyInit) {
|
||||
Assert.notNull(factory, "ReactiveWebServerFactory must not be null");
|
||||
this.handler = new DelayedInitializationHttpHandler(handlerSupplier, lazyInit);
|
||||
this.server = factory.getWebServer(this.handler);
|
||||
}
|
||||
|
||||
private void start() {
|
||||
this.handler.initializeHandler();
|
||||
this.server.start();
|
||||
ReactiveWebServerApplicationContext.this.publishEvent(
|
||||
new ReactiveWebServerInitializedEvent(this.server, ReactiveWebServerApplicationContext.this));
|
||||
}
|
||||
|
||||
void shutDownGracefully(Runnable callback) {
|
||||
this.server.shutDownGracefully((result) -> callback.run());
|
||||
}
|
||||
|
||||
void stop() {
|
||||
this.server.stop();
|
||||
}
|
||||
|
||||
HttpHandler getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class DelayedInitializationHttpHandler implements HttpHandler {
|
||||
|
||||
private final Supplier<HttpHandler> handlerSupplier;
|
||||
|
||||
private final boolean lazyInit;
|
||||
|
||||
private volatile HttpHandler delegate = this::handleUninitialized;
|
||||
|
||||
private DelayedInitializationHttpHandler(Supplier<HttpHandler> handlerSupplier, boolean lazyInit) {
|
||||
this.handlerSupplier = handlerSupplier;
|
||||
this.lazyInit = lazyInit;
|
||||
}
|
||||
|
||||
private Mono<Void> handleUninitialized(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
throw new IllegalStateException("The HttpHandler has not yet been initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.delegate.handle(request, response);
|
||||
}
|
||||
|
||||
private void initializeHandler() {
|
||||
this.delegate = this.lazyInit ? new LazyHttpHandler(Mono.fromSupplier(this.handlerSupplier))
|
||||
: this.handlerSupplier.get();
|
||||
}
|
||||
|
||||
HttpHandler getHandler() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} that initializes its delegate on first request.
|
||||
*/
|
||||
private static final class LazyHttpHandler implements HttpHandler {
|
||||
|
||||
private final Mono<HttpHandler> delegate;
|
||||
|
||||
private LazyHttpHandler(Mono<HttpHandler> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.delegate.flatMap((handler) -> handler.handle(request, response));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class WebServerStartStopLifecycle implements SmartLifecycle {
|
||||
|
||||
private final ServerManager serverManager;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
private WebServerStartStopLifecycle(ServerManager serverManager) {
|
||||
this.serverManager = serverManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.serverManager.start();
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
this.serverManager.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class WebServerGracefulShutdownLifecycle implements SmartLifecycle {
|
||||
|
||||
private final ServerManager serverManager;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
private WebServerGracefulShutdownLifecycle(ServerManager serverManager) {
|
||||
this.serverManager = serverManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
throw new UnsupportedOperationException("Stop must not be invoked directly");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
this.running = false;
|
||||
this.serverManager.shutDownGracefully(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* {@link SmartLifecycle} to trigger {@link WebServer} graceful shutdown in a
|
||||
* {@link ReactiveWebServerApplicationContext}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebServerGracefulShutdownLifecycle implements SmartLifecycle {
|
||||
|
||||
private final WebServerManager serverManager;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
WebServerGracefulShutdownLifecycle(WebServerManager serverManager) {
|
||||
this.serverManager = serverManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
throw new UnsupportedOperationException("Stop must not be invoked directly");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
this.running = false;
|
||||
this.serverManager.shutDownGracefully(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Internal class used to manage the server and the {@link HttpHandler}, taking care not
|
||||
* to initialize the handler too early.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebServerManager {
|
||||
|
||||
private final ReactiveWebServerApplicationContext applicationContext;
|
||||
|
||||
private final DelayedInitializationHttpHandler handler;
|
||||
|
||||
private final WebServer webServer;
|
||||
|
||||
WebServerManager(ReactiveWebServerApplicationContext applicationContext, ReactiveWebServerFactory factory,
|
||||
Supplier<HttpHandler> handlerSupplier, boolean lazyInit) {
|
||||
this.applicationContext = applicationContext;
|
||||
Assert.notNull(factory, "Factory must not be null");
|
||||
this.handler = new DelayedInitializationHttpHandler(handlerSupplier, lazyInit);
|
||||
this.webServer = factory.getWebServer(this.handler);
|
||||
}
|
||||
|
||||
void start() {
|
||||
this.handler.initializeHandler();
|
||||
this.webServer.start();
|
||||
this.applicationContext
|
||||
.publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
|
||||
}
|
||||
|
||||
void shutDownGracefully(Runnable callback) {
|
||||
this.webServer.shutDownGracefully((result) -> callback.run());
|
||||
}
|
||||
|
||||
void stop() {
|
||||
this.webServer.stop();
|
||||
}
|
||||
|
||||
WebServer getWebServer() {
|
||||
return this.webServer;
|
||||
}
|
||||
|
||||
HttpHandler getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* A delayed {@link HttpHandler} that doesn't initialize things too early.
|
||||
*/
|
||||
static final class DelayedInitializationHttpHandler implements HttpHandler {
|
||||
|
||||
private final Supplier<HttpHandler> handlerSupplier;
|
||||
|
||||
private final boolean lazyInit;
|
||||
|
||||
private volatile HttpHandler delegate = this::handleUninitialized;
|
||||
|
||||
private DelayedInitializationHttpHandler(Supplier<HttpHandler> handlerSupplier, boolean lazyInit) {
|
||||
this.handlerSupplier = handlerSupplier;
|
||||
this.lazyInit = lazyInit;
|
||||
}
|
||||
|
||||
private Mono<Void> handleUninitialized(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
throw new IllegalStateException("The HttpHandler has not yet been initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.delegate.handle(request, response);
|
||||
}
|
||||
|
||||
void initializeHandler() {
|
||||
this.delegate = this.lazyInit ? new LazyHttpHandler(this.handlerSupplier) : this.handlerSupplier.get();
|
||||
}
|
||||
|
||||
HttpHandler getHandler() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HttpHandler} that initializes its delegate on first request.
|
||||
*/
|
||||
private static final class LazyHttpHandler implements HttpHandler {
|
||||
|
||||
private final Mono<HttpHandler> delegate;
|
||||
|
||||
private LazyHttpHandler(Supplier<HttpHandler> handlerSupplier) {
|
||||
this.delegate = Mono.fromSupplier(handlerSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return this.delegate.flatMap((handler) -> handler.handle(request, response));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* {@link SmartLifecycle} to start and stop the {@link WebServer} in a
|
||||
* {@link ReactiveWebServerApplicationContext}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebServerStartStopLifecycle implements SmartLifecycle {
|
||||
|
||||
private final WebServerManager weServerManager;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
WebServerStartStopLifecycle(WebServerManager weServerManager) {
|
||||
this.weServerManager = weServerManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.weServerManager.start();
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
this.weServerManager.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
|
||||
}
|
@ -83,24 +83,6 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
||||
this.includeException = includeException;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||
errorAttributes.put("timestamp", new Date());
|
||||
errorAttributes.put("path", request.path());
|
||||
Throwable error = getError(request);
|
||||
MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations
|
||||
.from(error.getClass(), SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
|
||||
HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation);
|
||||
errorAttributes.put("status", errorStatus.value());
|
||||
errorAttributes.put("error", errorStatus.getReasonPhrase());
|
||||
errorAttributes.put("message", determineMessage(error, responseStatusAnnotation));
|
||||
errorAttributes.put("requestId", request.exchange().getRequest().getId());
|
||||
handleException(errorAttributes, determineException(error));
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
|
||||
Map<String, Object> errorAttributes = getErrorAttributes(request, options.isIncluded(Include.STACK_TRACE));
|
||||
@ -122,6 +104,24 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
|
||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||
errorAttributes.put("timestamp", new Date());
|
||||
errorAttributes.put("path", request.path());
|
||||
Throwable error = getError(request);
|
||||
MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations
|
||||
.from(error.getClass(), SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
|
||||
HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation);
|
||||
errorAttributes.put("status", errorStatus.value());
|
||||
errorAttributes.put("error", errorStatus.getReasonPhrase());
|
||||
errorAttributes.put("message", determineMessage(error, responseStatusAnnotation));
|
||||
errorAttributes.put("requestId", request.exchange().getRequest().getId());
|
||||
handleException(errorAttributes, determineException(error), includeStackTrace);
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
|
||||
if (error instanceof ResponseStatusException) {
|
||||
return ((ResponseStatusException) error).getStatus();
|
||||
@ -157,9 +157,11 @@ public class DefaultErrorAttributes implements ErrorAttributes {
|
||||
errorAttributes.put("trace", stackTrace.toString());
|
||||
}
|
||||
|
||||
private void handleException(Map<String, Object> errorAttributes, Throwable error) {
|
||||
private void handleException(Map<String, Object> errorAttributes, Throwable error, boolean includeStackTrace) {
|
||||
errorAttributes.put("exception", error.getClass().getName());
|
||||
addStackTrace(errorAttributes, error);
|
||||
if (includeStackTrace) {
|
||||
addStackTrace(errorAttributes, error);
|
||||
}
|
||||
if (error instanceof BindingResult) {
|
||||
BindingResult result = (BindingResult) error;
|
||||
if (result.hasErrors()) {
|
||||
|
@ -48,7 +48,6 @@ import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
@ -177,7 +176,8 @@ public class ServletWebServerApplicationContext extends GenericWebApplicationCon
|
||||
this.webServer = factory.getWebServer(getSelfInitializer());
|
||||
getBeanFactory().registerSingleton("webServerGracefulShutdown",
|
||||
new WebServerGracefulShutdownLifecycle(this.webServer));
|
||||
getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this.webServer));
|
||||
getBeanFactory().registerSingleton("webServerStartStop",
|
||||
new WebServerStartStopLifecycle(this, this.webServer));
|
||||
}
|
||||
else if (servletContext != null) {
|
||||
try {
|
||||
@ -370,72 +370,4 @@ public class ServletWebServerApplicationContext extends GenericWebApplicationCon
|
||||
|
||||
}
|
||||
|
||||
private final class WebServerStartStopLifecycle implements SmartLifecycle {
|
||||
|
||||
private final WebServer webServer;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
private WebServerStartStopLifecycle(WebServer webServer) {
|
||||
this.webServer = webServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.webServer.start();
|
||||
this.running = true;
|
||||
ServletWebServerApplicationContext.this.publishEvent(
|
||||
new ServletWebServerInitializedEvent(this.webServer, ServletWebServerApplicationContext.this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.webServer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class WebServerGracefulShutdownLifecycle implements SmartLifecycle {
|
||||
|
||||
private final WebServer webServer;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
WebServerGracefulShutdownLifecycle(WebServer webServer) {
|
||||
this.webServer = webServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
throw new UnsupportedOperationException("Stop must not be invoked directly");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
this.running = false;
|
||||
this.webServer.shutDownGracefully((result) -> callback.run());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.servlet.context;
|
||||
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* {@link SmartLifecycle} to trigger {@link WebServer} graceful shutdown in a
|
||||
* {@link ServletWebServerApplicationContext}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebServerGracefulShutdownLifecycle implements SmartLifecycle {
|
||||
|
||||
private final WebServer webServer;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
WebServerGracefulShutdownLifecycle(WebServer webServer) {
|
||||
this.webServer = webServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
throw new UnsupportedOperationException("Stop must not be invoked directly");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
this.running = false;
|
||||
this.webServer.shutDownGracefully((result) -> callback.run());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.servlet.context;
|
||||
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
|
||||
/**
|
||||
* {@link SmartLifecycle} to start and stop the {@link WebServer} in a
|
||||
* {@link ServletWebServerApplicationContext}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class WebServerStartStopLifecycle implements SmartLifecycle {
|
||||
|
||||
private final ServletWebServerApplicationContext applicationContext;
|
||||
|
||||
private final WebServer webServer;
|
||||
|
||||
private volatile boolean running;
|
||||
|
||||
WebServerStartStopLifecycle(ServletWebServerApplicationContext applicationContext, WebServer webServer) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.webServer = webServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.webServer.start();
|
||||
this.running = true;
|
||||
this.applicationContext
|
||||
.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.webServer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
|
||||
}
|
@ -105,17 +105,6 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
||||
request.setAttribute(ERROR_ATTRIBUTE, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||
errorAttributes.put("timestamp", new Date());
|
||||
addStatus(errorAttributes, webRequest);
|
||||
addErrorDetails(errorAttributes, webRequest);
|
||||
addPath(errorAttributes, webRequest);
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
|
||||
Map<String, Object> errorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));
|
||||
@ -137,6 +126,17 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
|
||||
Map<String, Object> errorAttributes = new LinkedHashMap<>();
|
||||
errorAttributes.put("timestamp", new Date());
|
||||
addStatus(errorAttributes, webRequest);
|
||||
addErrorDetails(errorAttributes, webRequest, includeStackTrace);
|
||||
addPath(errorAttributes, webRequest);
|
||||
return errorAttributes;
|
||||
}
|
||||
|
||||
private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {
|
||||
Integer status = getAttribute(requestAttributes, RequestDispatcher.ERROR_STATUS_CODE);
|
||||
if (status == null) {
|
||||
@ -154,14 +154,17 @@ public class DefaultErrorAttributes implements ErrorAttributes, HandlerException
|
||||
}
|
||||
}
|
||||
|
||||
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest) {
|
||||
private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest,
|
||||
boolean includeStackTrace) {
|
||||
Throwable error = getError(webRequest);
|
||||
if (error != null) {
|
||||
while (error instanceof ServletException && error.getCause() != null) {
|
||||
error = error.getCause();
|
||||
}
|
||||
errorAttributes.put("exception", error.getClass().getName());
|
||||
addStackTrace(errorAttributes, error);
|
||||
if (includeStackTrace) {
|
||||
addStackTrace(errorAttributes, error);
|
||||
}
|
||||
}
|
||||
addErrorMessage(errorAttributes, webRequest, error);
|
||||
}
|
||||
|
@ -129,7 +129,6 @@ class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor
|
||||
@Disabled("Flaky due to https://github.com/reactor/reactor-netty/issues/1093")
|
||||
@Override
|
||||
protected void whenARequestRemainsInFlightThenShutDownGracefullyDoesNotInvokeCallbackUntilTheRequestCompletes() {
|
||||
|
||||
}
|
||||
|
||||
protected Mono<String> testSslWithAlias(String alias) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,7 +18,7 @@ package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.DelayedInitializationHttpHandler;
|
||||
import org.springframework.boot.web.reactive.context.WebServerManager.DelayedInitializationHttpHandler;
|
||||
import org.springframework.boot.web.reactive.context.config.ExampleReactiveWebServerApplicationConfiguration;
|
||||
import org.springframework.boot.web.reactive.server.MockReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
|
Loading…
Reference in New Issue
Block a user