Commit 67a29902 authored by Andy Wilkinson's avatar Andy Wilkinson

Rework mappings endpoint

Improve the structure of the response and include mappings from
WebFlux and Servlet and Filter registrations in addition to the
mappings from Spring MVC.

Closes gh-9979
parent 1f26a031
...@@ -435,6 +435,11 @@ ...@@ -435,6 +435,11 @@
<artifactId>spring-restdocs-mockmvc</artifactId> <artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-webtestclient</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId> <artifactId>spring-security-test</artifactId>
......
[[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[]
...@@ -61,6 +61,7 @@ include::endpoints/info.adoc[leveloffset=+1] ...@@ -61,6 +61,7 @@ include::endpoints/info.adoc[leveloffset=+1]
include::endpoints/liquibase.adoc[leveloffset=+1] include::endpoints/liquibase.adoc[leveloffset=+1]
include::endpoints/logfile.adoc[leveloffset=+1] include::endpoints/logfile.adoc[leveloffset=+1]
include::endpoints/loggers.adoc[leveloffset=+1] include::endpoints/loggers.adoc[leveloffset=+1]
include::endpoints/mappings.adoc[leveloffset=+1]
include::endpoints/metrics.adoc[leveloffset=+1] include::endpoints/metrics.adoc[leveloffset=+1]
include::endpoints/prometheus.adoc[leveloffset=+1] include::endpoints/prometheus.adoc[leveloffset=+1]
include::endpoints/scheduledtasks.adoc[leveloffset=+1] include::endpoints/scheduledtasks.adoc[leveloffset=+1]
......
/*
* 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<Collection<MappingDescriptionProvider>> 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();
}
}
}
/*
* 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<AbstractUrlHandlerMapping> handlerMappings = Collections.emptyList();
private List<AbstractHandlerMethodMapping<?>> 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<AbstractUrlHandlerMapping> handlerMappings) {
this.handlerMappings = handlerMappings;
}
/**
* Set the method mappings.
* @param methodMappings the method mappings
*/
public void setMethodMappings(List<AbstractHandlerMethodMapping<?>> methodMappings) {
this.methodMappings = methodMappings;
}
@ReadOperation
public Map<String, Object> mappings() {
Map<String, Object> 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<String, Object> result) {
if (applicationContext != null) {
for (Entry<String, AbstractHandlerMethodMapping> bean : applicationContext
.getBeansOfType(AbstractHandlerMethodMapping.class).entrySet()) {
@SuppressWarnings("unchecked")
Map<?, HandlerMethod> methods = bean.getValue().getHandlerMethods();
for (Entry<?, HandlerMethod> method : methods.entrySet()) {
Map<String, String> 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<String, Object> result) {
if (applicationContext != null) {
Map<String, AbstractUrlHandlerMapping> mappings = applicationContext
.getBeansOfType(AbstractUrlHandlerMapping.class);
for (Entry<String, AbstractUrlHandlerMapping> mapping : mappings.entrySet()) {
Map<String, Object> handlers = getHandlerMap(mapping.getValue());
for (Entry<String, Object> handler : handlers.entrySet()) {
result.put(handler.getKey(),
Collections.singletonMap("bean", mapping.getKey()));
}
}
}
}
private Map<String, Object> 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<AbstractUrlHandlerMapping> handlerMappings,
Map<String, Object> result) {
for (AbstractUrlHandlerMapping mapping : handlerMappings) {
Map<String, Object> handlers = mapping.getHandlerMap();
for (Map.Entry<String, Object> entry : handlers.entrySet()) {
Class<? extends Object> handlerClass = entry.getValue().getClass();
result.put(entry.getKey(),
Collections.singletonMap("type", handlerClass.getName()));
}
}
}
protected void extractMethodMappings(
Collection<AbstractHandlerMethodMapping<?>> methodMappings,
Map<String, Object> 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())));
}
}
}
}
...@@ -41,10 +41,10 @@ org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorAu ...@@ -41,10 +41,10 @@ org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorAu
org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.trace.TraceRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.trace.TraceRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.trace.TraceWebFilterAutoConfiguration,\ 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.reactive.ReactiveManagementContextAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\ org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration,\ org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration,\
......
...@@ -26,33 +26,26 @@ import java.util.stream.Stream; ...@@ -26,33 +26,26 @@ import java.util.stream.Stream;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; 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.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; 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.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; 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.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; 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.Configuration;
import org.springframework.context.annotation.Import; 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.ContentModifyingOperationPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationPreprocessor; import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
...@@ -62,30 +55,9 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWit ...@@ -62,30 +55,9 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWit
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
@RunWith(SpringRunner.class) @TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
@SpringBootTest(properties = { "spring.jackson.serialization.indent_output=true",
"management.endpoints.web.expose=*" }) "management.endpoints.web.expose=*" })
public abstract class AbstractEndpointDocumentationTests { public 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;
}
protected String describeEnumValues(Class<? extends Enum<?>> enumType) { protected String describeEnumValues(Class<? extends Enum<?>> enumType) {
return StringUtils return StringUtils
...@@ -160,7 +132,9 @@ public abstract class AbstractEndpointDocumentationTests { ...@@ -160,7 +132,9 @@ public abstract class AbstractEndpointDocumentationTests {
DispatcherServletAutoConfiguration.class, EndpointAutoConfiguration.class, DispatcherServletAutoConfiguration.class, EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
WebMvcEndpointManagementContextConfiguration.class, WebMvcEndpointManagementContextConfiguration.class,
PropertyPlaceholderAutoConfiguration.class }) WebFluxEndpointManagementContextConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebFluxAutoConfiguration.class,
HttpHandlerAutoConfiguration.class })
static class BaseDocumentationConfiguration { static class BaseDocumentationConfiguration {
} }
......
...@@ -49,7 +49,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -49,7 +49,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class AuditEventsEndpointDocumentationTests public class AuditEventsEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@MockBean @MockBean
private AuditEventRepository repository; private AuditEventRepository repository;
......
...@@ -45,7 +45,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -45,7 +45,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class BeansEndpointDocumentationTests extends AbstractEndpointDocumentationTests { public class BeansEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
@Test @Test
public void beans() throws Exception { public void beans() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -51,7 +51,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -51,7 +51,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class ConditionsReportEndpointDocumentationTests public class ConditionsReportEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Rule @Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
......
...@@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class ConfigurationPropertiesReportEndpointDocumentationTests public class ConfigurationPropertiesReportEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void configProps() throws Exception { public void configProps() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -58,7 +58,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -58,7 +58,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@TestPropertySource(properties = "spring.config.location=classpath:/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/") @TestPropertySource(properties = "spring.config.location=classpath:/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/")
public class EnvironmentEndpointDocumentationTests public class EnvironmentEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
private static final FieldDescriptor activeProfiles = fieldWithPath("activeProfiles") private static final FieldDescriptor activeProfiles = fieldWithPath("activeProfiles")
.description("Names of the active profiles, if any."); .description("Names of the active profiles, if any.");
......
...@@ -45,7 +45,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -45,7 +45,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
@AutoConfigureTestDatabase @AutoConfigureTestDatabase
public class FlywayEndpointDocumentationTests extends AbstractEndpointDocumentationTests { public class FlywayEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
@Test @Test
public void flyway() throws Exception { public void flyway() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -47,7 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -47,7 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentationTests { public class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
@Test @Test
public void health() throws Exception { public void health() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class HeapDumpWebEndpointDocumentationTests public class HeapDumpWebEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void heapDump() throws Exception { public void heapDump() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -44,7 +44,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -44,7 +44,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* *
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class InfoEndpointDocumentationTests extends AbstractEndpointDocumentationTests { public class InfoEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
@Test @Test
public void info() throws Exception { public void info() throws Exception {
......
...@@ -43,7 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -43,7 +43,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class LiquibaseEndpointDocumentationTests public class LiquibaseEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void liquibase() throws Exception { public void liquibase() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -36,7 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -36,7 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@TestPropertySource(properties = "logging.file=src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log") @TestPropertySource(properties = "logging.file=src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log")
public class LogFileWebEndpointDocumentationTests public class LogFileWebEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void logFile() throws Exception { public void logFile() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -49,7 +49,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -49,7 +49,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class LoggersEndpointDocumentationTests public class LoggersEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
private static final List<FieldDescriptor> levelFields = Arrays.asList( private static final List<FieldDescriptor> levelFields = Arrays.asList(
fieldWithPath("configuredLevel") fieldWithPath("configuredLevel")
......
/*
* 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.endpoint.web.documentation;
import java.util.Collection;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
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.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
/**
* Tests for generating documentation describing {@link MappingsEndpoint}.
*
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
public class MappingsEndpointReactiveDocumentationTests
extends AbstractEndpointDocumentationTests {
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@LocalServerPort
private int port;
private WebTestClient client;
@Before
public void webTestClient() {
this.client = WebTestClient
.bindToServer().filter(documentationConfiguration(this.restDocumentation)
.snippets().withDefaults())
.baseUrl("http://localhost:" + this.port).build();
}
@Test
public void mappings() throws Exception {
this.client.get().uri("/actuator/mappings").exchange().expectStatus().isOk()
.expectBody()
.consumeWith(document("mappings",
responseFields(
beneathPath("contexts.*.mappings.dispatcherHandlers")
.withSubsectionId("dispatcher-handlers"),
fieldWithPath("*").description(
"Dispatcher handler mappings, if any, keyed by "
+ "dispatcher handler bean name."),
fieldWithPath("*.[].handler")
.description("Handler for the mapping."),
fieldWithPath("*.[].predicate")
.description("Predicate for the mapping."))));
}
@Configuration
@Import(BaseDocumentationConfiguration.class)
static class TestConfiguration {
@Bean
public NettyReactiveWebServerFactory netty() {
return new NettyReactiveWebServerFactory(0);
}
@Bean
public DispatcherHandlersMappingDescriptionProvider dispatcherHandlersMappingDescriptionProvider() {
return new DispatcherHandlersMappingDescriptionProvider();
}
@Bean
public MappingsEndpoint mappingsEndpoint(
Collection<MappingDescriptionProvider> descriptionProviders,
ConfigurableApplicationContext context) {
return new MappingsEndpoint(descriptionProviders, context);
}
}
}
/*
* 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.endpoint.web.documentation;
import java.util.Collection;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.web.MappingDescriptionProvider;
import org.springframework.boot.actuate.web.MappingsEndpoint;
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.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
/**
* Tests for generating documentation describing {@link MappingsEndpoint}.
*
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MappingsEndpointServletDocumentationTests
extends AbstractEndpointDocumentationTests {
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@LocalServerPort
private int port;
private WebTestClient client;
@Before
public void webTestClient() {
this.client = WebTestClient.bindToServer()
.filter(documentationConfiguration(this.restDocumentation))
.baseUrl("http://localhost:" + this.port).build();
}
@Test
public void mappings() throws Exception {
ResponseFieldsSnippet commonResponseFields = responseFields(
fieldWithPath("contexts")
.description("Application contexts keyed by id."),
fieldWithPath("contexts.*.mappings")
.description("Mappings in the context, keyed by mapping type."),
subsectionWithPath("contexts.*.mappings.dispatcherServlets")
.description("Dispatcher servlet mappings, if any."),
subsectionWithPath("contexts.*.mappings.servletFilters")
.description("Servlet filter mappings, if any."),
subsectionWithPath("contexts.*.mappings.servlets")
.description("Servlet mappings, if any."),
subsectionWithPath("contexts.*.mappings.dispatcherHandlers")
.description("Dispatcher handler mappings, if any.").optional()
.type(JsonFieldType.OBJECT),
parentIdField());
this.client.get().uri("/actuator/mappings").exchange().expectBody()
.consumeWith(document("mappings", commonResponseFields,
responseFields(
beneathPath("contexts.*.mappings.dispatcherServlets")
.withSubsectionId("dispatcher-servlets"),
fieldWithPath("*").description(
"Dispatcher servlet mappings, if any, keyed by "
+ "dispatcher servlet bean name."),
fieldWithPath("*.[].handler")
.description("Handler for the mapping."),
fieldWithPath("*.[].predicate")
.description("Predicate for the mapping.")),
responseFields(
beneathPath("contexts.*.mappings.servletFilters")
.withSubsectionId("servlet-filters"),
fieldWithPath("[].servletNameMappings").description(
"Names of the servlets to which the filter is mapped."),
fieldWithPath("[].urlPatternMappings").description(
"URL pattern to which the filter is mapped."),
fieldWithPath("[].name")
.description("Name of the filter."),
fieldWithPath("[].className")
.description("Class name of the filter")),
responseFields(
beneathPath("contexts.*.mappings.servlets")
.withSubsectionId("servlets"),
fieldWithPath("[].mappings")
.description("Mappings of the servlet."),
fieldWithPath("[].name")
.description("Name of the servlet."),
fieldWithPath("[].className")
.description("Class name of the servlet"))));
}
@Configuration
@Import(BaseDocumentationConfiguration.class)
static class TestConfiguration {
@Bean
public TomcatServletWebServerFactory tomcat() {
return new TomcatServletWebServerFactory(0);
}
@Bean
public DispatcherServletsMappingDescriptionProvider dispatcherServletsMappingDescriptionProvider() {
return new DispatcherServletsMappingDescriptionProvider();
}
@Bean
public ServletsMappingDescriptionProvider servletsMappingDescriptionProvider() {
return new ServletsMappingDescriptionProvider();
}
@Bean
public FiltersMappingDescriptionProvider filtersMappingDescriptionProvider() {
return new FiltersMappingDescriptionProvider();
}
@Bean
public MappingsEndpoint mappingsEndpoint(
Collection<MappingDescriptionProvider> descriptionProviders,
ConfigurableApplicationContext context) {
return new MappingsEndpoint(descriptionProviders, context);
}
}
}
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class MetricsEndpointDocumentationTests public class MetricsEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void metricNames() throws Exception { public void metricNames() throws Exception {
......
/*
* 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.endpoint.web.documentation;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
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.web.context.WebApplicationContext;
/**
* Abstract base class for tests that generate endpoint documentation using Spring REST
* Docs and {@link MockMvc}.
*
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(properties = { "spring.jackson.serialization.indent_output=true",
"management.endpoints.web.expose=*" })
public abstract class MockMvcEndpointDocumentationTests
extends 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;
}
}
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class PrometheusScrapeEndpointDocumentationTests public class PrometheusScrapeEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void prometheus() throws Exception { public void prometheus() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -44,7 +44,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -44,7 +44,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class ScheduledTasksEndpointDocumentationTests public class ScheduledTasksEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void scheduledTasks() throws Exception { public void scheduledTasks() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -55,7 +55,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -55,7 +55,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@TestPropertySource(properties = "spring.jackson.serialization.write-dates-as-timestamps=false") @TestPropertySource(properties = "spring.jackson.serialization.write-dates-as-timestamps=false")
public class SessionsEndpointDocumentationTests public class SessionsEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
private static final Session sessionOne = createSession( private static final Session sessionOne = createSession(
Instant.now().minusSeconds(60 * 60 * 12), Instant.now().minusSeconds(45)); Instant.now().minusSeconds(60 * 60 * 12), Instant.now().minusSeconds(45));
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -37,7 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -37,7 +37,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class ShutdownEndpointDocumentationTests public class ShutdownEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void shutdown() throws Exception { public void shutdown() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
public class ThreadDumpEndpointDocumentationTests public class ThreadDumpEndpointDocumentationTests
extends AbstractEndpointDocumentationTests { extends MockMvcEndpointDocumentationTests {
@Test @Test
public void threadDump() throws Exception { public void threadDump() throws Exception {
......
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -29,7 +29,7 @@ import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoC ...@@ -29,7 +29,7 @@ import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoC
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.trace.TraceEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.RequestMappingEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.MappingsEndpointAutoConfiguration;
/** /**
* A list of all endpoint auto-configuration classes for use in tests. * A list of all endpoint auto-configuration classes for use in tests.
...@@ -50,7 +50,7 @@ final class EndpointAutoConfigurationClasses { ...@@ -50,7 +50,7 @@ final class EndpointAutoConfigurationClasses {
all.add(InfoEndpointAutoConfiguration.class); all.add(InfoEndpointAutoConfiguration.class);
all.add(ThreadDumpEndpointAutoConfiguration.class); all.add(ThreadDumpEndpointAutoConfiguration.class);
all.add(TraceEndpointAutoConfiguration.class); all.add(TraceEndpointAutoConfiguration.class);
all.add(RequestMappingEndpointAutoConfiguration.class); all.add(MappingsEndpointAutoConfiguration.class);
ALL = all.toArray(new Class<?>[] {}); ALL = all.toArray(new Class<?>[] {});
} }
......
/*
* 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.Arrays;
import java.util.Collections;
import java.util.Map;
import org.junit.Test;
import org.springframework.boot.actuate.endpoint.EndpointInfo;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.OperationRequestPredicate;
import org.springframework.boot.actuate.endpoint.web.WebEndpointHttpMethod;
import org.springframework.boot.actuate.endpoint.web.WebOperation;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link RequestMappingEndpoint}.
*
* @author Dave Syer
*/
public class RequestMappingEndpointTests {
private RequestMappingEndpoint endpoint = new RequestMappingEndpoint();
@Test
public void concreteUrlMappings() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(Collections.singletonMap("/foo", new Object()));
mapping.setApplicationContext(new StaticApplicationContext());
mapping.initApplicationContext();
this.endpoint.setHandlerMappings(Collections.singletonList(mapping));
Map<String, Object> result = this.endpoint.mappings();
assertThat(result).hasSize(1);
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) result.get("/foo");
assertThat(map.get("type")).isEqualTo("java.lang.Object");
}
@Test
public void beanUrlMappings() {
StaticApplicationContext context = new StaticApplicationContext();
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(Collections.singletonMap("/foo", new Object()));
mapping.setApplicationContext(context);
mapping.initApplicationContext();
context.getDefaultListableBeanFactory().registerSingleton("mapping", mapping);
this.endpoint.setApplicationContext(context);
Map<String, Object> result = this.endpoint.mappings();
assertThat(result).hasSize(1);
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) result.get("/foo");
assertThat(map.get("bean")).isEqualTo("mapping");
}
@Test
public void beanUrlMappingsProxy() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
MappingConfiguration.class);
this.endpoint.setApplicationContext(context);
Map<String, Object> result = this.endpoint.mappings();
assertThat(result).hasSize(1);
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) result.get("/foo");
assertThat(map.get("bean")).isEqualTo("scopedTarget.mapping");
}
@SuppressWarnings("unchecked")
@Test
public void beanMethodMappings() {
StaticApplicationContext context = new StaticApplicationContext();
context.getDefaultListableBeanFactory().registerSingleton("mapping",
createHandlerMapping());
this.endpoint.setApplicationContext(context);
Map<String, Object> result = this.endpoint.mappings();
assertThat(result).hasSize(2);
assertThat(result.keySet()).filteredOn((key) -> key.contains("[/actuator/test]"))
.hasOnlyOneElementSatisfying(
(key) -> assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("bean", "method"));
assertThat(result.keySet()).filteredOn((key) -> key.contains("[/actuator]"))
.hasOnlyOneElementSatisfying(
(key) -> assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("bean", "method"));
}
@SuppressWarnings("unchecked")
@Test
public void concreteMethodMappings() {
WebMvcEndpointHandlerMapping mapping = createHandlerMapping();
this.endpoint.setMethodMappings(Collections.singletonList(mapping));
Map<String, Object> result = this.endpoint.mappings();
assertThat(result).hasSize(2);
assertThat(result.keySet()).filteredOn((key) -> key.contains("[/actuator/test]"))
.hasOnlyOneElementSatisfying(
(key) -> assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("method"));
assertThat(result.keySet()).filteredOn((key) -> key.contains("[/actuator]"))
.hasOnlyOneElementSatisfying(
(key) -> assertThat((Map<String, Object>) result.get(key))
.containsOnlyKeys("method"));
}
private WebMvcEndpointHandlerMapping createHandlerMapping() {
OperationRequestPredicate requestPredicate = new OperationRequestPredicate("test",
WebEndpointHttpMethod.GET, Collections.singletonList("application/json"),
Collections.singletonList("application/json"));
WebOperation operation = new WebOperation(OperationType.READ,
(arguments) -> "Invoked", true, requestPredicate, "test");
WebMvcEndpointHandlerMapping mapping = new WebMvcEndpointHandlerMapping(
new EndpointMapping("actuator"),
Collections.singleton(new EndpointInfo<>("test", true,
Collections.singleton(operation))),
new EndpointMediaTypes(Arrays.asList("application/vnd.test+json"),
Arrays.asList("application/vnd.test+json")));
mapping.setApplicationContext(new StaticApplicationContext());
mapping.afterPropertiesSet();
return mapping;
}
@Configuration
protected static class MappingConfiguration {
@Bean
@Lazy
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public AbstractUrlHandlerMapping mapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(Collections.singletonMap("/foo", new Object()));
return mapping;
}
}
}
...@@ -66,6 +66,17 @@ ...@@ -66,6 +66,17 @@
<artifactId>jest</artifactId> <artifactId>jest</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>javax.cache</groupId> <groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId> <artifactId>cache-api</artifactId>
...@@ -191,6 +202,11 @@ ...@@ -191,6 +202,11 @@
<artifactId>spring-data-redis</artifactId> <artifactId>spring-data-redis</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-solr</artifactId> <artifactId>spring-data-solr</artifactId>
...@@ -281,22 +297,6 @@ ...@@ -281,22 +297,6 @@
<artifactId>reactor-netty</artifactId> <artifactId>reactor-netty</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.hsqldb</groupId> <groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>
......
/*
* 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);
}
/*
* 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<MappingDescriptionProvider> descriptionProviders;
private final ApplicationContext context;
public MappingsEndpoint(Collection<MappingDescriptionProvider> descriptionProviders,
ApplicationContext context) {
this.descriptionProviders = descriptionProviders;
this.context = context;
}
@ReadOperation
public ApplicationMappings mappings() {
ApplicationContext target = this.context;
Map<String, ContextMappings> 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<String, Object> 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<String, ContextMappings> contextMappings;
private ApplicationMappings(Map<String, ContextMappings> contextMappings) {
this.contextMappings = contextMappings;
}
public Map<String, ContextMappings> 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<String, Object> mappings;
private final String parentId;
private ContextMappings(Map<String, Object> mappings, String parentId) {
this.mappings = mappings;
this.parentId = parentId;
}
public String getParentId() {
return this.parentId;
}
public Map<String, Object> getMappings() {
return this.mappings;
}
}
}
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,31 +14,33 @@ ...@@ -14,31 +14,33 @@
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.boot.actuate.autoconfigure.web.servlet; package org.springframework.boot.actuate.web.reactive;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.web.servlet.DispatcherServlet;
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}. * A description of a mapping known to a {@link DispatcherServlet}.
* *
* @author Phillip Webb * @author Andy Wilkinson
* @since 2.0.0 * @since 2.0.0
*/ */
@Configuration public class DispatcherHandlerMappingDescription {
@ConditionalOnClass(AbstractHandlerMethodMapping.class)
public class RequestMappingEndpointAutoConfiguration { private final String predicate;
@Bean private final String handler;
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint DispatcherHandlerMappingDescription(String predicate, String handler) {
public RequestMappingEndpoint requestMappingEndpoint() { this.predicate = predicate;
return new RequestMappingEndpoint(); this.handler = handler;
}
public String getHandler() {
return this.handler;
}
public String getPredicate() {
return this.predicate;
} }
} }
/*
* 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<HandlerMappingDescriptionProvider<? extends HandlerMapping>> descriptionProviders = Arrays
.asList(new RequestMappingInfoHandlerMappingDescriptionProvider(),
new UrlHandlerMappingDescriptionProvider(),
new RouterFunctionMappingDescriptionProvider());
@Override
public String getMappingName() {
return "dispatcherHandlers";
}
@Override
public Map<String, List<DispatcherHandlerMappingDescription>> describeMappings(
ApplicationContext context) {
Map<String, List<DispatcherHandlerMappingDescription>> mappings = new HashMap<>();
context.getBeansOfType(DispatcherHandler.class).forEach(
(name, handler) -> mappings.put(name, describeMappings(handler)));
return mappings;
}
private List<DispatcherHandlerMappingDescription> describeMappings(
DispatcherHandler dispatcherHandler) {
return dispatcherHandler.getHandlerMappings().stream().flatMap(this::describe)
.collect(Collectors.toList());
}
@SuppressWarnings("unchecked")
private <T extends HandlerMapping> Stream<DispatcherHandlerMappingDescription> describe(
T handlerMapping) {
for (HandlerMappingDescriptionProvider<?> descriptionProvider : descriptionProviders) {
if (descriptionProvider.getMappingClass().isInstance(handlerMapping)) {
return ((HandlerMappingDescriptionProvider<T>) descriptionProvider)
.describe(handlerMapping).stream();
}
}
return Stream.empty();
}
private interface HandlerMappingDescriptionProvider<T extends HandlerMapping> {
Class<T> getMappingClass();
List<DispatcherHandlerMappingDescription> describe(T handlerMapping);
}
private static final class RequestMappingInfoHandlerMappingDescriptionProvider
implements
HandlerMappingDescriptionProvider<RequestMappingInfoHandlerMapping> {
@Override
public Class<RequestMappingInfoHandlerMapping> getMappingClass() {
return RequestMappingInfoHandlerMapping.class;
}
@Override
public List<DispatcherHandlerMappingDescription> describe(
RequestMappingInfoHandlerMapping handlerMapping) {
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping
.getHandlerMethods();
return handlerMethods.entrySet().stream().map(this::describe)
.collect(Collectors.toList());
}
private DispatcherHandlerMappingDescription describe(
Entry<RequestMappingInfo, HandlerMethod> mapping) {
return new DispatcherHandlerMappingDescription(mapping.getKey().toString(),
mapping.getValue().toString());
}
}
private static final class UrlHandlerMappingDescriptionProvider
implements HandlerMappingDescriptionProvider<AbstractUrlHandlerMapping> {
@Override
public Class<AbstractUrlHandlerMapping> getMappingClass() {
return AbstractUrlHandlerMapping.class;
}
@Override
public List<DispatcherHandlerMappingDescription> describe(
AbstractUrlHandlerMapping handlerMapping) {
return handlerMapping.getHandlerMap().entrySet().stream().map(this::describe)
.collect(Collectors.toList());
}
private DispatcherHandlerMappingDescription describe(
Entry<PathPattern, Object> mapping) {
return new DispatcherHandlerMappingDescription(
mapping.getKey().getPatternString(), mapping.getValue().toString());
}
}
private static final class RouterFunctionMappingDescriptionProvider
implements HandlerMappingDescriptionProvider<RouterFunctionMapping> {
@Override
public Class<RouterFunctionMapping> getMappingClass() {
return RouterFunctionMapping.class;
}
@Override
public List<DispatcherHandlerMappingDescription> 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<DispatcherHandlerMappingDescription> 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<ServerRequest, Mono<Resource>> lookupFunction) {
}
@Override
public void unknown(RouterFunction<?> routerFunction) {
}
}
}
/*
* 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<HandlerMapping> getHandlerMappings() {
List<HandlerMapping> 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<Context> 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
}
}
}
}
/*
* 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;
}
}
/*
* 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<HandlerMappingDescriptionProvider<? extends HandlerMapping>> descriptionProviders;
static {
List<HandlerMappingDescriptionProvider<? extends HandlerMapping>> 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<String, List<DispatcherServletMappingDescription>> describeMappings(
ApplicationContext context) {
if (context instanceof WebApplicationContext) {
return describeMappings((WebApplicationContext) context);
}
return Collections.emptyMap();
}
private Map<String, List<DispatcherServletMappingDescription>> describeMappings(
WebApplicationContext context) {
Map<String, List<DispatcherServletMappingDescription>> mappings = new HashMap<>();
context.getBeansOfType(DispatcherServlet.class)
.forEach((name, dispatcherServlet) -> mappings.put(name,
describeMappings(new DispatcherServletHandlerMappings(name,
dispatcherServlet, context))));
return mappings;
}
private List<DispatcherServletMappingDescription> describeMappings(
DispatcherServletHandlerMappings mappings) {
return mappings.getHandlerMappings().stream().flatMap(this::describe)
.collect(Collectors.toList());
}
private <T extends HandlerMapping> Stream<DispatcherServletMappingDescription> describe(
T handlerMapping) {
return describe(handlerMapping, descriptionProviders).stream();
}
@SuppressWarnings("unchecked")
private static <T extends HandlerMapping> List<DispatcherServletMappingDescription> describe(
T handlerMapping,
List<HandlerMappingDescriptionProvider<?>> descriptionProviders) {
for (HandlerMappingDescriptionProvider<?> descriptionProvider : descriptionProviders) {
if (descriptionProvider.getMappingClass().isInstance(handlerMapping)) {
return ((HandlerMappingDescriptionProvider<T>) descriptionProvider)
.describe(handlerMapping);
}
}
return Collections.emptyList();
}
private interface HandlerMappingDescriptionProvider<T extends HandlerMapping> {
Class<T> getMappingClass();
List<DispatcherServletMappingDescription> describe(T handlerMapping);
}
private static final class RequestMappingInfoHandlerMappingDescriptionProvider
implements
HandlerMappingDescriptionProvider<RequestMappingInfoHandlerMapping> {
@Override
public Class<RequestMappingInfoHandlerMapping> getMappingClass() {
return RequestMappingInfoHandlerMapping.class;
}
@Override
public List<DispatcherServletMappingDescription> describe(
RequestMappingInfoHandlerMapping handlerMapping) {
Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping
.getHandlerMethods();
return handlerMethods.entrySet().stream().map(this::describe)
.collect(Collectors.toList());
}
private DispatcherServletMappingDescription describe(
Entry<RequestMappingInfo, HandlerMethod> mapping) {
return new DispatcherServletMappingDescription(mapping.getKey().toString(),
mapping.getValue().toString());
}
}
private static final class UrlHandlerMappingDescriptionProvider
implements HandlerMappingDescriptionProvider<AbstractUrlHandlerMapping> {
@Override
public Class<AbstractUrlHandlerMapping> getMappingClass() {
return AbstractUrlHandlerMapping.class;
}
@Override
public List<DispatcherServletMappingDescription> describe(
AbstractUrlHandlerMapping handlerMapping) {
return handlerMapping.getHandlerMap().entrySet().stream().map(this::describe)
.collect(Collectors.toList());
}
private DispatcherServletMappingDescription describe(
Entry<String, Object> mapping) {
return new DispatcherServletMappingDescription(mapping.getKey().toString(),
mapping.getValue().toString());
}
}
private static final class DelegatingHandlerMappingDescriptionProvider
implements HandlerMappingDescriptionProvider<DelegatingHandlerMapping> {
private final List<HandlerMappingDescriptionProvider<?>> descriptionProviders;
private DelegatingHandlerMappingDescriptionProvider(
List<HandlerMappingDescriptionProvider<?>> descriptionProviders) {
this.descriptionProviders = descriptionProviders;
}
@Override
public Class<DelegatingHandlerMapping> getMappingClass() {
return DelegatingHandlerMapping.class;
}
@Override
public List<DispatcherServletMappingDescription> describe(
DelegatingHandlerMapping handlerMapping) {
List<DispatcherServletMappingDescription> descriptions = new ArrayList<>();
for (HandlerMapping delegate : handlerMapping.getDelegates()) {
descriptions.addAll(DispatcherServletsMappingDescriptionProvider
.describe(delegate, this.descriptionProviders));
}
return descriptions;
}
}
}
/*
* 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<FilterRegistration> {
/**
* 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<String> getServletNameMappings() {
return this.getRegistration().getServletNameMappings();
}
/**
* Returns the URL pattern mappings for the registered filter.
*
* @return the mappings
*/
public Collection<String> getUrlPatternMappings() {
return this.getRegistration().getUrlPatternMappings();
}
}
/*
* 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<FilterRegistrationMappingDescription> 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";
}
}
/*
* 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 <T> type of the registration
* @author Andy Wilkinson
* @since 2.0.0
*/
public class RegistrationMappingDescription<T extends Registration> {
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;
}
}
/*
* 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<ServletRegistration> {
/**
* 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<String> getMappings() {
return getRegistration().getMappings();
}
}
/*
* 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<ServletRegistrationMappingDescription> 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";
}
}
/*
* 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<String>().elements());
given(servletContext.getAttributeNames())
.willReturn(new Vector<String>().elements());
FilterRegistration filterRegistration = mock(FilterRegistration.class);
given((Map<String, FilterRegistration>) servletContext.getFilterRegistrations())
.willReturn(Collections.singletonMap("testFilter", filterRegistration));
ServletRegistration servletRegistration = mock(ServletRegistration.class);
given((Map<String, ServletRegistration>) 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<String, List<DispatcherServletMappingDescription>> dispatcherServlets = mappings(
contextMappings, "dispatcherServlets");
assertThat(dispatcherServlets).containsOnlyKeys("dispatcherServlet");
List<DispatcherServletMappingDescription> handlerMappings = dispatcherServlets
.get("dispatcherServlet");
assertThat(handlerMappings).hasSize(1);
List<ServletRegistrationMappingDescription> servlets = mappings(
contextMappings, "servlets");
assertThat(servlets).hasSize(1);
List<FilterRegistrationMappingDescription> 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<String, List<DispatcherHandlerMappingDescription>> dispatcherHandlers = mappings(
contextMappings, "dispatcherHandlers");
assertThat(dispatcherHandlers).containsOnlyKeys("webHandler");
List<DispatcherHandlerMappingDescription> 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> T mappings(ContextMappings contextMappings, String key) {
return (T) contextMappings.getMappings().get(key);
}
@Configuration
static class EndpointConfiguration {
@Bean
public MappingsEndpoint mappingsEndpoint(
Collection<MappingDescriptionProvider> descriptionProviders,
ApplicationContext context) {
return new MappingsEndpoint(descriptionProviders, context);
}
}
@Configuration
@EnableWebFlux
@Controller
static class ReactiveWebConfiguration {
@Bean
public DispatcherHandlersMappingDescriptionProvider dispatcherHandlersMappingDescriptionProvider() {
return new DispatcherHandlersMappingDescriptionProvider();
}
@Bean
public RouterFunction<ServerResponse> 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() {
}
}
}
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -168,6 +168,12 @@ public class UndertowServletWebServer implements WebServer { ...@@ -168,6 +168,12 @@ public class UndertowServletWebServer implements WebServer {
} }
} }
public DeploymentManager getDeploymentManager() {
synchronized (this.monitor) {
return this.manager;
}
}
private void stopSilently() { private void stopSilently() {
try { try {
if (this.undertow != null) { if (this.undertow != null) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment