diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
index 2d793ae26686..de859f77c1c7 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml
@@ -435,6 +435,11 @@
spring-restdocs-mockmvc
test
+
+ org.springframework.restdocs
+ spring-restdocs-webtestclient
+ test
+
org.springframework.security
spring-security-test
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc
new file mode 100644
index 000000000000..2ef662f1a533
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/endpoints/mappings.adoc
@@ -0,0 +1,80 @@
+[[mappings]]
+= Mappings (`mappings`)
+
+The `mappings` endpoint provides information about the application's request mappings.
+
+
+
+[[mappings-retrieving]]
+== Retrieving the Mappings
+
+To retrieve the mappings, make a `GET` request to `/actuator/mappings`, as shown in the
+following curl-based example:
+
+include::{snippets}mappings/curl-request.adoc[]
+
+The resulting response is similar to the following:
+
+include::{snippets}mappings/http-response.adoc[]
+
+
+
+[[mappings-retrieving-response-structure]]
+=== Response Structure
+
+The response contains details of the application's mappings. The items found in the
+response depend on the type of web application (reactive or Servlet-based). The
+following table describes the structure of the common elements of the response:
+
+[cols="2,1,3"]
+include::{snippets}mappings/response-fields.adoc[]
+
+The entries that may be found in `contexts.*.mappings` are described in the
+following sections.
+
+
+[[mappings-retrieving-response-structure-dispatcher-servlets]]
+=== Dispatcher Servlets Response Structure
+
+When using Spring MVC, the response contains details of any `DispatcherServlet`
+request mappings beneath `contexts.*.mappings.dispatcherServlets`. The following
+table describes the structure of this section of the response:
+
+[cols="2,1,3"]
+include::{snippets}mappings/response-fields-dispatcher-servlets.adoc[]
+
+
+
+[[mappings-retrieving-response-structure-servlets]]
+=== Servlets Response Structure
+
+When using the Servlet stack, the response contains details of any `Servlet` mappings
+beneath `contexts.*.mappings.servlets`. The following table describes the structure of
+this section of the response:
+
+[cols="2,1,3"]
+include::{snippets}mappings/response-fields-servlets.adoc[]
+
+
+
+[[mappings-retrieving-response-structure-servlet-filters]]
+=== Servlet Filters Response Structure
+
+When using the Servlet stack, the response contains details of any `Filter` mappings
+beneath `contexts.*.mappings.servletFilters`. The following table describes the
+structure of this section of the response:
+
+[cols="2,1,3"]
+include::{snippets}mappings/response-fields-servlet-filters.adoc[]
+
+
+
+[[mappings-retrieving-response-structure-dispatcher-servlets]]
+=== Dispatcher Handlers Response Structure
+
+When using Spring WebFlux, the response contains details of any `DispatcherHandler`
+request mappings beneath `contexts.*.mappings.dispatcherHandlers`. The following
+table describes the structure of this section of the response:
+
+[cols="2,1,3"]
+include::{snippets}mappings/response-fields-dispatcher-handlers.adoc[]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc
index 14af3216ef0e..c0d6a3ac0423 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/asciidoc/index.adoc
@@ -61,6 +61,7 @@ include::endpoints/info.adoc[leveloffset=+1]
include::endpoints/liquibase.adoc[leveloffset=+1]
include::endpoints/logfile.adoc[leveloffset=+1]
include::endpoints/loggers.adoc[leveloffset=+1]
+include::endpoints/mappings.adoc[leveloffset=+1]
include::endpoints/metrics.adoc[leveloffset=+1]
include::endpoints/prometheus.adoc[leveloffset=+1]
include::endpoints/scheduledtasks.adoc[leveloffset=+1]
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/MappingsEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/MappingsEndpointAutoConfiguration.java
new file mode 100644
index 000000000000..52aa8e73fa33
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/MappingsEndpointAutoConfiguration.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.autoconfigure.web;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
+import org.springframework.boot.actuate.web.MappingDescriptionProvider;
+import org.springframework.boot.actuate.web.MappingsEndpoint;
+import org.springframework.boot.actuate.web.reactive.DispatcherHandlersMappingDescriptionProvider;
+import org.springframework.boot.actuate.web.servlet.DispatcherServletsMappingDescriptionProvider;
+import org.springframework.boot.actuate.web.servlet.FiltersMappingDescriptionProvider;
+import org.springframework.boot.actuate.web.servlet.ServletsMappingDescriptionProvider;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.DispatcherHandler;
+import org.springframework.web.servlet.DispatcherServlet;
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for {@link MappingsEndpoint}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+@ManagementContextConfiguration
+public class MappingsEndpointAutoConfiguration {
+
+ @Bean
+ @ConditionalOnEnabledEndpoint
+ public MappingsEndpoint mappingsEndpoint(ApplicationContext applicationContext,
+ ObjectProvider> descriptionProviders) {
+ return new MappingsEndpoint(
+ descriptionProviders.getIfAvailable(Collections::emptyList),
+ applicationContext);
+ }
+
+ @Configuration
+ @ConditionalOnWebApplication(type = Type.SERVLET)
+ static class ServletWebConfiguration {
+
+ @Bean
+ ServletsMappingDescriptionProvider servletMappingDescriptionProvider() {
+ return new ServletsMappingDescriptionProvider();
+ }
+
+ @Bean
+ FiltersMappingDescriptionProvider filterMappingDescriptionProvider() {
+ return new FiltersMappingDescriptionProvider();
+ }
+
+ @Configuration
+ @ConditionalOnClass(DispatcherServlet.class)
+ @ConditionalOnBean(DispatcherServlet.class)
+ static class SpringMvcConfiguration {
+
+ @Bean
+ DispatcherServletsMappingDescriptionProvider dispatcherServletMappingDescriptionProvider(
+ ApplicationContext applicationContext) {
+ return new DispatcherServletsMappingDescriptionProvider();
+ }
+
+ }
+
+ }
+
+ @Configuration
+ @ConditionalOnWebApplication(type = Type.REACTIVE)
+ @ConditionalOnClass(DispatcherHandler.class)
+ @ConditionalOnBean(DispatcherHandler.class)
+ static class ReactiveWebConfiguration {
+
+ @Bean
+ public DispatcherHandlersMappingDescriptionProvider dispatcherHandlerMappingDescriptionProvider(
+ ApplicationContext applicationContext) {
+ return new DispatcherHandlersMappingDescriptionProvider();
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpoint.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpoint.java
deleted file mode 100644
index 5ac586493201..000000000000
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpoint.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2012-2017 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
- *
- * http://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.actuate.autoconfigure.web.servlet;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.springframework.aop.support.AopUtils;
-import org.springframework.beans.BeansException;
-import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
-import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
-import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
-
-/**
- * {@link Endpoint} to expose Spring MVC mappings.
- *
- * @author Dave Syer
- * @author Andy Wilkinson
- * @since 2.0.0
- */
-@Endpoint(id = "mappings")
-public class RequestMappingEndpoint implements ApplicationContextAware {
-
- private List handlerMappings = Collections.emptyList();
-
- private List> methodMappings = Collections
- .emptyList();
-
- private ApplicationContext applicationContext;
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext)
- throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- /**
- * Set the handler mappings.
- * @param handlerMappings the handler mappings
- */
- public void setHandlerMappings(List handlerMappings) {
- this.handlerMappings = handlerMappings;
- }
-
- /**
- * Set the method mappings.
- * @param methodMappings the method mappings
- */
- public void setMethodMappings(List> methodMappings) {
- this.methodMappings = methodMappings;
- }
-
- @ReadOperation
- public Map mappings() {
- Map result = new LinkedHashMap<>();
- extractHandlerMappings(this.handlerMappings, result);
- extractHandlerMappings(this.applicationContext, result);
- extractMethodMappings(this.methodMappings, result);
- extractMethodMappings(this.applicationContext, result);
- return result;
- }
-
- @SuppressWarnings("rawtypes")
- protected void extractMethodMappings(ApplicationContext applicationContext,
- Map result) {
- if (applicationContext != null) {
- for (Entry bean : applicationContext
- .getBeansOfType(AbstractHandlerMethodMapping.class).entrySet()) {
- @SuppressWarnings("unchecked")
- Map, HandlerMethod> methods = bean.getValue().getHandlerMethods();
- for (Entry, HandlerMethod> method : methods.entrySet()) {
- Map map = new LinkedHashMap<>();
- map.put("bean", bean.getKey());
- map.put("method", method.getValue().toString());
- result.put(method.getKey().toString(), map);
- }
- }
- }
- }
-
- protected void extractHandlerMappings(ApplicationContext applicationContext,
- Map result) {
- if (applicationContext != null) {
- Map mappings = applicationContext
- .getBeansOfType(AbstractUrlHandlerMapping.class);
- for (Entry mapping : mappings.entrySet()) {
- Map handlers = getHandlerMap(mapping.getValue());
- for (Entry handler : handlers.entrySet()) {
- result.put(handler.getKey(),
- Collections.singletonMap("bean", mapping.getKey()));
- }
- }
- }
- }
-
- private Map getHandlerMap(AbstractUrlHandlerMapping mapping) {
- if (AopUtils.isCglibProxy(mapping)) {
- // If the AbstractUrlHandlerMapping is a cglib proxy we can't call
- // the final getHandlerMap() method.
- return Collections.emptyMap();
- }
- return mapping.getHandlerMap();
- }
-
- protected void extractHandlerMappings(
- Collection handlerMappings,
- Map result) {
- for (AbstractUrlHandlerMapping mapping : handlerMappings) {
- Map handlers = mapping.getHandlerMap();
- for (Map.Entry entry : handlers.entrySet()) {
- Class extends Object> handlerClass = entry.getValue().getClass();
- result.put(entry.getKey(),
- Collections.singletonMap("type", handlerClass.getName()));
- }
- }
- }
-
- protected void extractMethodMappings(
- Collection> methodMappings,
- Map result) {
- for (AbstractHandlerMethodMapping> mapping : methodMappings) {
- Map, HandlerMethod> methods = mapping.getHandlerMethods();
- for (Map.Entry, HandlerMethod> entry : methods.entrySet()) {
- result.put(String.valueOf(entry.getKey()), Collections
- .singletonMap("method", String.valueOf(entry.getValue())));
- }
- }
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointAutoConfiguration.java
deleted file mode 100644
index 04bac88a4f58..000000000000
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/RequestMappingEndpointAutoConfiguration.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2012-2017 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
- *
- * http://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.actuate.autoconfigure.web.servlet;
-
-import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
-
-/**
- * {@link EnableAutoConfiguration Auto-configuration} for {@link RequestMappingEndpoint}.
- *
- * @author Phillip Webb
- * @since 2.0.0
- */
-@Configuration
-@ConditionalOnClass(AbstractHandlerMethodMapping.class)
-public class RequestMappingEndpointAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnEnabledEndpoint
- public RequestMappingEndpoint requestMappingEndpoint() {
- return new RequestMappingEndpoint();
- }
-
-}
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories
index d76062be2036..6bd280cc930f 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -41,10 +41,10 @@ org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorAu
org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.trace.TraceRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.trace.TraceWebFilterAutoConfiguration,\
+org.springframework.boot.actuate.autoconfigure.web.MappingsEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementContextAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration,\
-org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration,\
-org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration
+org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration,\
diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java
index 2ff5eb4234c1..46c5e7eca260 100644
--- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java
+++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java
@@ -26,33 +26,26 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
-import org.springframework.restdocs.JUnitRestDocumentation;
-import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.test.context.TestPropertySource;
import org.springframework.util.StringUtils;
-import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
@@ -62,30 +55,9 @@
*
* @author Andy Wilkinson
*/
-@RunWith(SpringRunner.class)
-@SpringBootTest(properties = { "spring.jackson.serialization.indent_output=true",
+@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"management.endpoints.web.expose=*" })
-public abstract class AbstractEndpointDocumentationTests {
-
- @Rule
- public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
-
- protected MockMvc mockMvc;
-
- @Autowired
- private WebApplicationContext applicationContext;
-
- @Before
- public void before() {
- this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext)
- .apply(MockMvcRestDocumentation
- .documentationConfiguration(this.restDocumentation).uris())
- .build();
- }
-
- protected WebApplicationContext getApplicationContext() {
- return this.applicationContext;
- }
+public class AbstractEndpointDocumentationTests {
protected String describeEnumValues(Class extends Enum>> enumType) {
return StringUtils
@@ -160,7 +132,9 @@ private List
+
+ io.undertow
+ undertow-servlet
+ true
+
+
+ org.jboss.spec.javax.servlet
+ jboss-servlet-api_3.1_spec
+
+
+
javax.cache
cache-api
@@ -191,6 +202,11 @@
spring-data-redis
true
+
+ org.springframework.data
+ spring-data-rest-webmvc
+ true
+
org.springframework.data
spring-data-solr
@@ -281,22 +297,6 @@
reactor-netty
test
-
- io.undertow
- undertow-core
- test
-
-
- io.undertow
- undertow-servlet
- test
-
-
- org.jboss.spec.javax.servlet
- jboss-servlet-api_3.1_spec
-
-
-
org.hsqldb
hsqldb
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/MappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/MappingDescriptionProvider.java
new file mode 100644
index 000000000000..09e2eb074d7a
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/MappingDescriptionProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web;
+
+import java.util.List;
+
+import org.springframework.context.ApplicationContext;
+
+/**
+ * A {@link MappingDescriptionProvider} provides a {@link List} of mapping descriptions
+ * via implementation-specific introspection of an application context.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public interface MappingDescriptionProvider {
+
+ /**
+ * Returns the name of the mappings described by this provider.
+ *
+ * @return the name of the mappings
+ */
+ String getMappingName();
+
+ /**
+ * Produce the descriptions of the mappings identified by this provider in the given
+ * {@code context}.
+ * @param context the application context to introspect
+ * @return the mapping descriptions
+ */
+ Object describeMappings(ApplicationContext context);
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/MappingsEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/MappingsEndpoint.java
new file mode 100644
index 000000000000..65e4a4001bbd
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/MappingsEndpoint.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
+import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * {@link Endpoint} to expose HTTP request mappings.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+@Endpoint(id = "mappings")
+public class MappingsEndpoint {
+
+ private final Collection descriptionProviders;
+
+ private final ApplicationContext context;
+
+ public MappingsEndpoint(Collection descriptionProviders,
+ ApplicationContext context) {
+ this.descriptionProviders = descriptionProviders;
+ this.context = context;
+ }
+
+ @ReadOperation
+ public ApplicationMappings mappings() {
+ ApplicationContext target = this.context;
+ Map contextMappings = new HashMap<>();
+ while (target != null) {
+ contextMappings.put(target.getId(), mappingsForContext(target));
+ target = target.getParent();
+ }
+ return new ApplicationMappings(contextMappings);
+ }
+
+ private ContextMappings mappingsForContext(ApplicationContext applicationContext) {
+ Map mappings = new HashMap<>();
+ this.descriptionProviders
+ .forEach((provider) -> mappings.put(provider.getMappingName(),
+ provider.describeMappings(applicationContext)));
+ return new ContextMappings(mappings, applicationContext.getParent() == null ? null
+ : applicationContext.getId());
+ }
+
+ /**
+ * A description of an application's request mappings. Primarily intended for
+ * serialization to JSON.
+ */
+ public static final class ApplicationMappings {
+
+ private final Map contextMappings;
+
+ private ApplicationMappings(Map contextMappings) {
+ this.contextMappings = contextMappings;
+ }
+
+ public Map getContexts() {
+ return this.contextMappings;
+ }
+
+ }
+
+ /**
+ * A description of an application context's request mappings. Primarily intended for
+ * serialization to JSON.
+ */
+ public static final class ContextMappings {
+
+ private final Map mappings;
+
+ private final String parentId;
+
+ private ContextMappings(Map mappings, String parentId) {
+ this.mappings = mappings;
+ this.parentId = parentId;
+ }
+
+ public String getParentId() {
+ return this.parentId;
+ }
+
+ public Map getMappings() {
+ return this.mappings;
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/reactive/DispatcherHandlerMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/reactive/DispatcherHandlerMappingDescription.java
new file mode 100644
index 000000000000..44316a9b34ec
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/reactive/DispatcherHandlerMappingDescription.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.reactive;
+
+import org.springframework.web.servlet.DispatcherServlet;
+
+/**
+ * A description of a mapping known to a {@link DispatcherServlet}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class DispatcherHandlerMappingDescription {
+
+ private final String predicate;
+
+ private final String handler;
+
+ DispatcherHandlerMappingDescription(String predicate, String handler) {
+ this.predicate = predicate;
+ this.handler = handler;
+ }
+
+ public String getHandler() {
+ return this.handler;
+ }
+
+ public String getPredicate() {
+ return this.predicate;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/reactive/DispatcherHandlersMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/reactive/DispatcherHandlersMappingDescriptionProvider.java
new file mode 100644
index 000000000000..3085048f8061
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/reactive/DispatcherHandlersMappingDescriptionProvider.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.reactive;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.boot.actuate.web.MappingDescriptionProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.reactive.DispatcherHandler;
+import org.springframework.web.reactive.HandlerMapping;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.RequestPredicate;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions.Visitor;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
+import org.springframework.web.reactive.handler.AbstractUrlHandlerMapping;
+import org.springframework.web.reactive.result.method.RequestMappingInfo;
+import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
+import org.springframework.web.util.pattern.PathPattern;
+
+/**
+ * A {@link MappingDescriptionProvider} that introspects the {@link HandlerMapping
+ * HandlerMappings} that are known to a {@link DispatcherHandler}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class DispatcherHandlersMappingDescriptionProvider
+ implements MappingDescriptionProvider {
+
+ private static final List> descriptionProviders = Arrays
+ .asList(new RequestMappingInfoHandlerMappingDescriptionProvider(),
+ new UrlHandlerMappingDescriptionProvider(),
+ new RouterFunctionMappingDescriptionProvider());
+
+ @Override
+ public String getMappingName() {
+ return "dispatcherHandlers";
+ }
+
+ @Override
+ public Map> describeMappings(
+ ApplicationContext context) {
+ Map> mappings = new HashMap<>();
+ context.getBeansOfType(DispatcherHandler.class).forEach(
+ (name, handler) -> mappings.put(name, describeMappings(handler)));
+ return mappings;
+ }
+
+ private List describeMappings(
+ DispatcherHandler dispatcherHandler) {
+ return dispatcherHandler.getHandlerMappings().stream().flatMap(this::describe)
+ .collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Stream describe(
+ T handlerMapping) {
+ for (HandlerMappingDescriptionProvider> descriptionProvider : descriptionProviders) {
+ if (descriptionProvider.getMappingClass().isInstance(handlerMapping)) {
+ return ((HandlerMappingDescriptionProvider) descriptionProvider)
+ .describe(handlerMapping).stream();
+
+ }
+ }
+ return Stream.empty();
+ }
+
+ private interface HandlerMappingDescriptionProvider {
+
+ Class getMappingClass();
+
+ List describe(T handlerMapping);
+
+ }
+
+ private static final class RequestMappingInfoHandlerMappingDescriptionProvider
+ implements
+ HandlerMappingDescriptionProvider {
+
+ @Override
+ public Class getMappingClass() {
+ return RequestMappingInfoHandlerMapping.class;
+ }
+
+ @Override
+ public List describe(
+ RequestMappingInfoHandlerMapping handlerMapping) {
+ Map handlerMethods = handlerMapping
+ .getHandlerMethods();
+ return handlerMethods.entrySet().stream().map(this::describe)
+ .collect(Collectors.toList());
+ }
+
+ private DispatcherHandlerMappingDescription describe(
+ Entry mapping) {
+ return new DispatcherHandlerMappingDescription(mapping.getKey().toString(),
+ mapping.getValue().toString());
+ }
+
+ }
+
+ private static final class UrlHandlerMappingDescriptionProvider
+ implements HandlerMappingDescriptionProvider {
+
+ @Override
+ public Class getMappingClass() {
+ return AbstractUrlHandlerMapping.class;
+ }
+
+ @Override
+ public List describe(
+ AbstractUrlHandlerMapping handlerMapping) {
+ return handlerMapping.getHandlerMap().entrySet().stream().map(this::describe)
+ .collect(Collectors.toList());
+ }
+
+ private DispatcherHandlerMappingDescription describe(
+ Entry mapping) {
+ return new DispatcherHandlerMappingDescription(
+ mapping.getKey().getPatternString(), mapping.getValue().toString());
+ }
+
+ }
+
+ private static final class RouterFunctionMappingDescriptionProvider
+ implements HandlerMappingDescriptionProvider {
+
+ @Override
+ public Class getMappingClass() {
+ return RouterFunctionMapping.class;
+ }
+
+ @Override
+ public List describe(
+ RouterFunctionMapping handlerMapping) {
+ MappingDescriptionVisitor visitor = new MappingDescriptionVisitor();
+ RouterFunction> routerFunction = handlerMapping.getRouterFunction();
+ if (routerFunction != null) {
+ routerFunction.accept(visitor);
+ }
+ return visitor.descriptions;
+ }
+
+ }
+
+ private static final class MappingDescriptionVisitor implements Visitor {
+
+ private final List descriptions = new ArrayList<>();
+
+ @Override
+ public void startNested(RequestPredicate predicate) {
+ }
+
+ @Override
+ public void endNested(RequestPredicate predicate) {
+ }
+
+ @Override
+ public void route(RequestPredicate predicate,
+ HandlerFunction> handlerFunction) {
+ this.descriptions.add(new DispatcherHandlerMappingDescription(
+ predicate.toString(), handlerFunction.toString()));
+ }
+
+ @Override
+ public void resources(Function> lookupFunction) {
+ }
+
+ @Override
+ public void unknown(RouterFunction> routerFunction) {
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletHandlerMappings.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletHandlerMappings.java
new file mode 100644
index 000000000000..07855073df7e
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletHandlerMappings.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.core.StandardWrapper;
+
+import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServer;
+import org.springframework.boot.web.server.WebServer;
+import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.HandlerMapping;
+
+/**
+ * {@code DispatcherServletHandlerMappings} provides access to a {@link DispatcherServlet
+ * DispatcherServlet's} handler mappings, triggering initialization of the dispatcher
+ * servlet if necessary.
+ *
+ * @author Andy Wilkinson
+ */
+final class DispatcherServletHandlerMappings {
+
+ private final String name;
+
+ private final DispatcherServlet dispatcherServlet;
+
+ private final WebApplicationContext applicationContext;
+
+ DispatcherServletHandlerMappings(String name, DispatcherServlet dispatcherServlet,
+ WebApplicationContext applicationContext) {
+ this.name = name;
+ this.dispatcherServlet = dispatcherServlet;
+ this.applicationContext = applicationContext;
+ }
+
+ public List getHandlerMappings() {
+ List handlerMappings = this.dispatcherServlet
+ .getHandlerMappings();
+ if (handlerMappings == null) {
+ initializeDispatcherServletIfPossible();
+ handlerMappings = this.dispatcherServlet.getHandlerMappings();
+ }
+ return handlerMappings == null ? Collections.emptyList() : handlerMappings;
+ }
+
+ private void initializeDispatcherServletIfPossible() {
+ if (!(this.applicationContext instanceof ServletWebServerApplicationContext)) {
+ return;
+ }
+ WebServer webServer = ((ServletWebServerApplicationContext) this.applicationContext)
+ .getWebServer();
+ if (webServer instanceof UndertowServletWebServer) {
+ new UndertowServletInitializer((UndertowServletWebServer) webServer)
+ .initializeServlet(this.name);
+ }
+ else if (webServer instanceof TomcatWebServer) {
+ new TomcatServletInitializer((TomcatWebServer) webServer)
+ .initializeServlet(this.name);
+ }
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ private static final class TomcatServletInitializer {
+
+ private final TomcatWebServer webServer;
+
+ private TomcatServletInitializer(TomcatWebServer webServer) {
+ this.webServer = webServer;
+ }
+
+ void initializeServlet(String name) {
+ findContext().ifPresent((context) -> initializeServlet(context, name));
+ }
+
+ private Optional findContext() {
+ return Stream.of(this.webServer.getTomcat().getHost().findChildren())
+ .filter(Context.class::isInstance).map(Context.class::cast)
+ .findFirst();
+ }
+
+ private void initializeServlet(Context context, String name) {
+ Container child = context.findChild(name);
+ if (child instanceof StandardWrapper) {
+ try {
+ ((StandardWrapper) child).allocate();
+ }
+ catch (ServletException ex) {
+ // Continue
+ }
+ }
+ }
+
+ }
+
+ private static final class UndertowServletInitializer {
+
+ private final UndertowServletWebServer webServer;
+
+ private UndertowServletInitializer(UndertowServletWebServer webServer) {
+ this.webServer = webServer;
+ }
+
+ void initializeServlet(String name) {
+ try {
+ this.webServer.getDeploymentManager().getDeployment().getServlets()
+ .getManagedServlet(name).forceInit();
+ }
+ catch (ServletException ex) {
+ // Continue
+ }
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletMappingDescription.java
new file mode 100644
index 000000000000..3084b10fd4df
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletMappingDescription.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import org.springframework.web.servlet.DispatcherServlet;
+
+/**
+ * A description of a mapping known to a {@link DispatcherServlet}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class DispatcherServletMappingDescription {
+
+ private final String handler;
+
+ private final String predicate;
+
+ /**
+ * Creates a new {@code DispatcherServletMappingDescription} for the given
+ * {@code handler} that will receives requests that match the given {@code predicate}.
+ *
+ * @param predicate the predicate
+ * @param handler the handler
+ */
+ public DispatcherServletMappingDescription(String predicate, String handler) {
+ this.handler = handler;
+ this.predicate = predicate;
+ }
+
+ /**
+ * Returns the handler for the described mapping.
+ * @return the handler
+ */
+ public String getHandler() {
+ return this.handler;
+ }
+
+ /**
+ * Returns the predicate for the described mapping.
+ * @return the predicate
+ */
+ public String getPredicate() {
+ return this.predicate;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletsMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletsMappingDescriptionProvider.java
new file mode 100644
index 000000000000..0d59de3c34cf
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/DispatcherServletsMappingDescriptionProvider.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.springframework.boot.actuate.web.MappingDescriptionProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping;
+import org.springframework.util.ClassUtils;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.HandlerMapping;
+import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
+
+/**
+ * A {@link MappingDescriptionProvider} that introspects the {@link HandlerMapping
+ * HandlerMappings} that are known to one or more {@link DispatcherServlet
+ * DispatcherServlets}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class DispatcherServletsMappingDescriptionProvider
+ implements MappingDescriptionProvider {
+
+ private static final List> descriptionProviders;
+
+ static {
+ List> providers = new ArrayList<>();
+ providers.add(new RequestMappingInfoHandlerMappingDescriptionProvider());
+ providers.add(new UrlHandlerMappingDescriptionProvider());
+ if (ClassUtils.isPresent(
+ "org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping",
+ null)) {
+ providers.add(new DelegatingHandlerMappingDescriptionProvider(
+ new ArrayList<>(providers)));
+ }
+ descriptionProviders = Collections.unmodifiableList(providers);
+ }
+
+ @Override
+ public String getMappingName() {
+ return "dispatcherServlets";
+ }
+
+ @Override
+ public Map> describeMappings(
+ ApplicationContext context) {
+ if (context instanceof WebApplicationContext) {
+ return describeMappings((WebApplicationContext) context);
+ }
+ return Collections.emptyMap();
+ }
+
+ private Map> describeMappings(
+ WebApplicationContext context) {
+ Map> mappings = new HashMap<>();
+ context.getBeansOfType(DispatcherServlet.class)
+ .forEach((name, dispatcherServlet) -> mappings.put(name,
+ describeMappings(new DispatcherServletHandlerMappings(name,
+ dispatcherServlet, context))));
+ return mappings;
+ }
+
+ private List describeMappings(
+ DispatcherServletHandlerMappings mappings) {
+ return mappings.getHandlerMappings().stream().flatMap(this::describe)
+ .collect(Collectors.toList());
+ }
+
+ private Stream describe(
+ T handlerMapping) {
+ return describe(handlerMapping, descriptionProviders).stream();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static List describe(
+ T handlerMapping,
+ List> descriptionProviders) {
+ for (HandlerMappingDescriptionProvider> descriptionProvider : descriptionProviders) {
+ if (descriptionProvider.getMappingClass().isInstance(handlerMapping)) {
+ return ((HandlerMappingDescriptionProvider) descriptionProvider)
+ .describe(handlerMapping);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ private interface HandlerMappingDescriptionProvider {
+
+ Class getMappingClass();
+
+ List describe(T handlerMapping);
+
+ }
+
+ private static final class RequestMappingInfoHandlerMappingDescriptionProvider
+ implements
+ HandlerMappingDescriptionProvider {
+
+ @Override
+ public Class getMappingClass() {
+ return RequestMappingInfoHandlerMapping.class;
+ }
+
+ @Override
+ public List describe(
+ RequestMappingInfoHandlerMapping handlerMapping) {
+ Map handlerMethods = handlerMapping
+ .getHandlerMethods();
+ return handlerMethods.entrySet().stream().map(this::describe)
+ .collect(Collectors.toList());
+ }
+
+ private DispatcherServletMappingDescription describe(
+ Entry mapping) {
+ return new DispatcherServletMappingDescription(mapping.getKey().toString(),
+ mapping.getValue().toString());
+ }
+
+ }
+
+ private static final class UrlHandlerMappingDescriptionProvider
+ implements HandlerMappingDescriptionProvider {
+
+ @Override
+ public Class getMappingClass() {
+ return AbstractUrlHandlerMapping.class;
+ }
+
+ @Override
+ public List describe(
+ AbstractUrlHandlerMapping handlerMapping) {
+ return handlerMapping.getHandlerMap().entrySet().stream().map(this::describe)
+ .collect(Collectors.toList());
+ }
+
+ private DispatcherServletMappingDescription describe(
+ Entry mapping) {
+ return new DispatcherServletMappingDescription(mapping.getKey().toString(),
+ mapping.getValue().toString());
+ }
+
+ }
+
+ private static final class DelegatingHandlerMappingDescriptionProvider
+ implements HandlerMappingDescriptionProvider {
+
+ private final List> descriptionProviders;
+
+ private DelegatingHandlerMappingDescriptionProvider(
+ List> descriptionProviders) {
+ this.descriptionProviders = descriptionProviders;
+ }
+
+ @Override
+ public Class getMappingClass() {
+ return DelegatingHandlerMapping.class;
+ }
+
+ @Override
+ public List describe(
+ DelegatingHandlerMapping handlerMapping) {
+ List descriptions = new ArrayList<>();
+ for (HandlerMapping delegate : handlerMapping.getDelegates()) {
+ descriptions.addAll(DispatcherServletsMappingDescriptionProvider
+ .describe(delegate, this.descriptionProviders));
+ }
+ return descriptions;
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/FilterRegistrationMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/FilterRegistrationMappingDescription.java
new file mode 100644
index 000000000000..dcd6c0d5f2e6
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/FilterRegistrationMappingDescription.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import java.util.Collection;
+
+import javax.servlet.FilterRegistration;
+
+/**
+ * A {@link RegistrationMappingDescription} derived from a {@link FilterRegistration}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class FilterRegistrationMappingDescription
+ extends RegistrationMappingDescription {
+
+ /**
+ * Creates a new {@code FilterRegistrationMappingDescription} derived from the given
+ * {@code filterRegistration}.
+ * @param filterRegistration the filter registration
+ */
+ public FilterRegistrationMappingDescription(FilterRegistration filterRegistration) {
+ super(filterRegistration);
+ }
+
+ /**
+ * Returns the servlet name mappings for the registered filter.
+ *
+ * @return the mappings
+ */
+ public Collection getServletNameMappings() {
+ return this.getRegistration().getServletNameMappings();
+ }
+
+ /**
+ * Returns the URL pattern mappings for the registered filter.
+ *
+ * @return the mappings
+ */
+ public Collection getUrlPatternMappings() {
+ return this.getRegistration().getUrlPatternMappings();
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/FiltersMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/FiltersMappingDescriptionProvider.java
new file mode 100644
index 000000000000..83522138b765
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/FiltersMappingDescriptionProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+
+import org.springframework.boot.actuate.web.MappingDescriptionProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * A {@link MappingDescriptionProvider} that describes that mappings of any {@link Filter
+ * Filters} registered with a {@link ServletContext}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class FiltersMappingDescriptionProvider implements MappingDescriptionProvider {
+
+ @Override
+ public List describeMappings(
+ ApplicationContext context) {
+ if (!(context instanceof WebApplicationContext)) {
+ return Collections.emptyList();
+ }
+ return ((WebApplicationContext) context).getServletContext()
+ .getFilterRegistrations().values().stream()
+ .map(FilterRegistrationMappingDescription::new)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String getMappingName() {
+ return "servletFilters";
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/RegistrationMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/RegistrationMappingDescription.java
new file mode 100644
index 000000000000..45fb38454a93
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/RegistrationMappingDescription.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import javax.servlet.Registration;
+
+/**
+ * A mapping description derived from a {@link Registration}.
+ *
+ * @param type of the registration
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class RegistrationMappingDescription {
+
+ private final T registration;
+
+ /**
+ * Creates a new {@link RegistrationMappingDescription} derived from the given
+ * {@code registration} and with the given {@code predicate}.
+ * @param registration the registration
+ */
+ public RegistrationMappingDescription(T registration) {
+ this.registration = registration;
+ }
+
+ /**
+ * Returns the name of the registered Filter or Servlet.
+ *
+ * @return the name
+ */
+ public String getName() {
+ return this.registration.getName();
+ }
+
+ /**
+ * Returns the class name of the registered Filter or Servlet.
+ *
+ * @return the class name
+ */
+ public String getClassName() {
+ return this.registration.getClassName();
+ }
+
+ /**
+ * Returns the registration that is being described.
+ * @return the registration
+ */
+ protected final T getRegistration() {
+ return this.registration;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/ServletRegistrationMappingDescription.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/ServletRegistrationMappingDescription.java
new file mode 100644
index 000000000000..6c2a0affd5c0
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/ServletRegistrationMappingDescription.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import java.util.Collection;
+
+import javax.servlet.ServletRegistration;
+
+/**
+ * A mapping description derived from a {@link ServletRegistration}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0.0
+ */
+public class ServletRegistrationMappingDescription
+ extends RegistrationMappingDescription {
+
+ /**
+ * Creates a new {@code ServletRegistrationMappingDescription} derived from the given
+ * {@code servletRegistration}.
+ * @param servletRegistration the servlet registration
+ */
+ public ServletRegistrationMappingDescription(
+ ServletRegistration servletRegistration) {
+ super(servletRegistration);
+ }
+
+ /**
+ * Returns the mappings for the registered servlet.
+ *
+ * @return the mappings
+ */
+ public Collection getMappings() {
+ return getRegistration().getMappings();
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/ServletsMappingDescriptionProvider.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/ServletsMappingDescriptionProvider.java
new file mode 100644
index 000000000000..adeab11d8935
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/servlet/ServletsMappingDescriptionProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web.servlet;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+
+import org.springframework.boot.actuate.web.MappingDescriptionProvider;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * A {@link MappingDescriptionProvider} that describes that mappings of any {@link Servlet
+ * Servlets} registered with a {@link ServletContext}.
+ *
+ * @author Andy Wilkinson
+ * @since 2.0
+ */
+public class ServletsMappingDescriptionProvider implements MappingDescriptionProvider {
+
+ @Override
+ public List describeMappings(
+ ApplicationContext context) {
+ if (!(context instanceof WebApplicationContext)) {
+ return Collections.emptyList();
+ }
+ return ((WebApplicationContext) context).getServletContext()
+ .getServletRegistrations().values().stream()
+ .map(ServletRegistrationMappingDescription::new)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String getMappingName() {
+ return "servlets";
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/MappingsEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/MappingsEndpointTests.java
new file mode 100644
index 000000000000..6eb84f0e78e0
--- /dev/null
+++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/web/MappingsEndpointTests.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2012-2018 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
+ *
+ * http://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.actuate.web;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.FilterRegistration;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
+import org.junit.Test;
+
+import org.springframework.boot.actuate.web.MappingsEndpoint.ApplicationMappings;
+import org.springframework.boot.actuate.web.MappingsEndpoint.ContextMappings;
+import org.springframework.boot.actuate.web.reactive.DispatcherHandlerMappingDescription;
+import org.springframework.boot.actuate.web.reactive.DispatcherHandlersMappingDescriptionProvider;
+import org.springframework.boot.actuate.web.servlet.DispatcherServletMappingDescription;
+import org.springframework.boot.actuate.web.servlet.DispatcherServletsMappingDescriptionProvider;
+import org.springframework.boot.actuate.web.servlet.FilterRegistrationMappingDescription;
+import org.springframework.boot.actuate.web.servlet.FiltersMappingDescriptionProvider;
+import org.springframework.boot.actuate.web.servlet.ServletRegistrationMappingDescription;
+import org.springframework.boot.actuate.web.servlet.ServletsMappingDescriptionProvider;
+import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
+import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.mock.web.MockServletConfig;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.reactive.config.EnableWebFlux;
+import org.springframework.web.reactive.function.server.RequestPredicates;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests for {@link MappingsEndpoint}.
+ *
+ * @author Andy Wilkinson
+ */
+public class MappingsEndpointTests {
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void servletWebMappings() {
+ ServletContext servletContext = mock(ServletContext.class);
+ given(servletContext.getInitParameterNames())
+ .willReturn(new Vector().elements());
+ given(servletContext.getAttributeNames())
+ .willReturn(new Vector().elements());
+ FilterRegistration filterRegistration = mock(FilterRegistration.class);
+ given((Map) servletContext.getFilterRegistrations())
+ .willReturn(Collections.singletonMap("testFilter", filterRegistration));
+ ServletRegistration servletRegistration = mock(ServletRegistration.class);
+ given((Map) servletContext.getServletRegistrations())
+ .willReturn(Collections.singletonMap("testServlet", servletRegistration));
+ WebApplicationContextRunner runner = new WebApplicationContextRunner(() -> {
+ AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
+ context.setServletContext(servletContext);
+ return context;
+ }).withUserConfiguration(EndpointConfiguration.class,
+ ServletWebConfiguration.class);
+ runner.run((context) -> {
+ ContextMappings contextMappings = contextMappings(context);
+ assertThat(contextMappings.getParentId()).isNull();
+ assertThat(contextMappings.getMappings())
+ .containsOnlyKeys("dispatcherServlets", "servletFilters", "servlets");
+ Map> dispatcherServlets = mappings(
+ contextMappings, "dispatcherServlets");
+ assertThat(dispatcherServlets).containsOnlyKeys("dispatcherServlet");
+ List handlerMappings = dispatcherServlets
+ .get("dispatcherServlet");
+ assertThat(handlerMappings).hasSize(1);
+ List servlets = mappings(
+ contextMappings, "servlets");
+ assertThat(servlets).hasSize(1);
+ List filters = mappings(contextMappings,
+ "servletFilters");
+ assertThat(filters).hasSize(1);
+ });
+ }
+
+ @Test
+ public void reactiveWebMappings() {
+ ReactiveWebApplicationContextRunner runner = new ReactiveWebApplicationContextRunner()
+ .withUserConfiguration(EndpointConfiguration.class,
+ ReactiveWebConfiguration.class);
+ runner.run((context) -> {
+ ContextMappings contextMappings = contextMappings(context);
+ assertThat(contextMappings.getParentId()).isNull();
+ assertThat(contextMappings.getMappings())
+ .containsOnlyKeys("dispatcherHandlers");
+ Map> dispatcherHandlers = mappings(
+ contextMappings, "dispatcherHandlers");
+ assertThat(dispatcherHandlers).containsOnlyKeys("webHandler");
+ List handlerMappings = dispatcherHandlers
+ .get("webHandler");
+ assertThat(handlerMappings).hasSize(3);
+ });
+ }
+
+ private ContextMappings contextMappings(ApplicationContext context) {
+ ApplicationMappings applicationMappings = context.getBean(MappingsEndpoint.class)
+ .mappings();
+ assertThat(applicationMappings.getContexts()).containsOnlyKeys(context.getId());
+ return applicationMappings.getContexts().get(context.getId());
+ }
+
+ @SuppressWarnings("unchecked")
+ private T mappings(ContextMappings contextMappings, String key) {
+ return (T) contextMappings.getMappings().get(key);
+ }
+
+ @Configuration
+ static class EndpointConfiguration {
+
+ @Bean
+ public MappingsEndpoint mappingsEndpoint(
+ Collection descriptionProviders,
+ ApplicationContext context) {
+ return new MappingsEndpoint(descriptionProviders, context);
+ }
+
+ }
+
+ @Configuration
+ @EnableWebFlux
+ @Controller
+ static class ReactiveWebConfiguration {
+
+ @Bean
+ public DispatcherHandlersMappingDescriptionProvider dispatcherHandlersMappingDescriptionProvider() {
+ return new DispatcherHandlersMappingDescriptionProvider();
+ }
+
+ @Bean
+ public RouterFunction routerFunction() {
+ return RouterFunctions
+ .route(RequestPredicates.GET("/one"),
+ (request) -> ServerResponse.ok().build())
+ .andRoute(RequestPredicates.POST("/two"),
+ (request) -> ServerResponse.ok().build());
+ }
+
+ @RequestMapping("/three")
+ public void three() {
+
+ }
+
+ }
+
+ @Configuration
+ @EnableWebMvc
+ @Controller
+ static class ServletWebConfiguration {
+
+ @Bean
+ public DispatcherServletsMappingDescriptionProvider dispatcherServletsMappingDescriptionProvider() {
+ return new DispatcherServletsMappingDescriptionProvider();
+ }
+
+ @Bean
+ public ServletsMappingDescriptionProvider servletsMappingDescriptionProvider() {
+ return new ServletsMappingDescriptionProvider();
+ }
+
+ @Bean
+ public FiltersMappingDescriptionProvider filtersMappingDescriptionProvider() {
+ return new FiltersMappingDescriptionProvider();
+ }
+
+ @Bean
+ public DispatcherServlet dispatcherServlet(WebApplicationContext context)
+ throws ServletException {
+ DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
+ dispatcherServlet.init(new MockServletConfig());
+ return dispatcherServlet;
+ }
+
+ @RequestMapping("/three")
+ public void three() {
+
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java
index ed04430d716d..847ede37c57e 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2017 the original author or authors.
+ * Copyright 2012-2018 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.
@@ -168,6 +168,12 @@ public void start() throws WebServerException {
}
}
+ public DeploymentManager getDeploymentManager() {
+ synchronized (this.monitor) {
+ return this.manager;
+ }
+ }
+
private void stopSilently() {
try {
if (this.undertow != null) {