Detect DispatcherServlets registered via ServletRegistrationBean
Previously, when a DispatcherServlet was registered via a ServletRegistrationBean, the mappings endpoint did not expose any information about it as it wasn't detected. This commit fixes the detection of available DispatcherServlets in the context so that the mappings endpoint include them all. Closes gh-13186
This commit is contained in:
@@ -19,14 +19,18 @@ package org.springframework.boot.actuate.web.mappings.servlet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription;
|
||||
import org.springframework.boot.actuate.web.mappings.MappingDescriptionProvider;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -44,6 +48,7 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi
|
||||
* DispatcherServlets}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class DispatcherServletsMappingDescriptionProvider
|
||||
@@ -81,13 +86,33 @@ public class DispatcherServletsMappingDescriptionProvider
|
||||
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))));
|
||||
determineDispatcherServlets(context).forEach((name, dispatcherServlet) -> mappings
|
||||
.put(name, describeMappings(new DispatcherServletHandlerMappings(name,
|
||||
dispatcherServlet, context))));
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private Map<String, DispatcherServlet> determineDispatcherServlets(
|
||||
WebApplicationContext context) {
|
||||
Map<String, DispatcherServlet> dispatcherServlets = new LinkedHashMap<>();
|
||||
context.getBeansOfType(ServletRegistrationBean.class).values()
|
||||
.forEach((registration) -> {
|
||||
Servlet servlet = registration.getServlet();
|
||||
if (servlet instanceof DispatcherServlet
|
||||
&& !dispatcherServlets.containsValue(servlet)) {
|
||||
dispatcherServlets.put(registration.getServletName(),
|
||||
(DispatcherServlet) servlet);
|
||||
}
|
||||
});
|
||||
context.getBeansOfType(DispatcherServlet.class)
|
||||
.forEach((name, dispatcherServlet) -> {
|
||||
if (!dispatcherServlets.containsValue(dispatcherServlet)) {
|
||||
dispatcherServlets.put(name, dispatcherServlet);
|
||||
}
|
||||
});
|
||||
return dispatcherServlets;
|
||||
}
|
||||
|
||||
private List<DispatcherServletMappingDescription> describeMappings(
|
||||
DispatcherServletHandlerMappings mappings) {
|
||||
return mappings.getHandlerMappings().stream().flatMap(this::describe)
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.boot.actuate.web.mappings.servlet.ServletRegistration
|
||||
import org.springframework.boot.actuate.web.mappings.servlet.ServletsMappingDescriptionProvider;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -67,28 +68,14 @@ import static org.springframework.web.reactive.function.server.RouterFunctions.r
|
||||
* Tests for {@link MappingsEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MappingsEndpointTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void servletWebMappings() {
|
||||
ServletContext servletContext = mock(ServletContext.class);
|
||||
given(servletContext.getInitParameterNames())
|
||||
.willReturn(Collections.emptyEnumeration());
|
||||
given(servletContext.getAttributeNames())
|
||||
.willReturn(Collections.emptyEnumeration());
|
||||
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));
|
||||
Supplier<ConfigurableWebApplicationContext> contextSupplier = () -> {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(servletContext);
|
||||
return context;
|
||||
};
|
||||
Supplier<ConfigurableWebApplicationContext> contextSupplier = prepareContextSupplier();
|
||||
new WebApplicationContextRunner(contextSupplier)
|
||||
.withUserConfiguration(EndpointConfiguration.class,
|
||||
ServletWebConfiguration.class)
|
||||
@@ -112,6 +99,47 @@ public class MappingsEndpointTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void servletWebMappingsWithAdditionalDispatcherServlets() {
|
||||
Supplier<ConfigurableWebApplicationContext> contextSupplier = prepareContextSupplier();
|
||||
new WebApplicationContextRunner(contextSupplier).withUserConfiguration(
|
||||
EndpointConfiguration.class, ServletWebConfiguration.class,
|
||||
CustomDispatcherServletConfiguration.class).run((context) -> {
|
||||
ContextMappings contextMappings = contextMappings(context);
|
||||
Map<String, List<DispatcherServletMappingDescription>> dispatcherServlets = mappings(
|
||||
contextMappings, "dispatcherServlets");
|
||||
assertThat(dispatcherServlets).containsOnlyKeys("dispatcherServlet",
|
||||
"customDispatcherServletRegistration",
|
||||
"anotherDispatcherServletRegistration");
|
||||
assertThat(dispatcherServlets.get("dispatcherServlet")).hasSize(1);
|
||||
assertThat(
|
||||
dispatcherServlets.get("customDispatcherServletRegistration"))
|
||||
.hasSize(1);
|
||||
assertThat(dispatcherServlets
|
||||
.get("anotherDispatcherServletRegistration")).hasSize(1);
|
||||
});
|
||||
}
|
||||
|
||||
private Supplier<ConfigurableWebApplicationContext> prepareContextSupplier() {
|
||||
ServletContext servletContext = mock(ServletContext.class);
|
||||
given(servletContext.getInitParameterNames())
|
||||
.willReturn(Collections.emptyEnumeration());
|
||||
given(servletContext.getAttributeNames())
|
||||
.willReturn(Collections.emptyEnumeration());
|
||||
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));
|
||||
return () -> {
|
||||
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
context.setServletContext(servletContext);
|
||||
return context;
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reactiveWebMappings() {
|
||||
new ReactiveWebApplicationContextRunner()
|
||||
@@ -213,4 +241,44 @@ public class MappingsEndpointTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomDispatcherServletConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<DispatcherServlet> customDispatcherServletRegistration(
|
||||
WebApplicationContext context) {
|
||||
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
|
||||
createTestDispatcherServlet(context));
|
||||
registration.setName("customDispatcherServletRegistration");
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DispatcherServlet anotherDispatcherServlet(WebApplicationContext context) {
|
||||
return createTestDispatcherServlet(context);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<DispatcherServlet> anotherDispatcherServletRegistration(
|
||||
WebApplicationContext context) {
|
||||
ServletRegistrationBean<DispatcherServlet> registrationBean = new ServletRegistrationBean<>(
|
||||
anotherDispatcherServlet(context));
|
||||
registrationBean.setName("anotherDispatcherServletRegistration");
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
private DispatcherServlet createTestDispatcherServlet(
|
||||
WebApplicationContext context) {
|
||||
try {
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
|
||||
dispatcherServlet.init(new MockServletConfig());
|
||||
return dispatcherServlet;
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -100,14 +100,6 @@ public class ServletRegistrationBean<T extends Servlet>
|
||||
this.urlMappings.addAll(Arrays.asList(urlMappings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the servlet being registered.
|
||||
* @return the servlet
|
||||
*/
|
||||
protected T getServlet() {
|
||||
return this.servlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the servlet to be registered.
|
||||
* @param servlet the servlet
|
||||
@@ -117,6 +109,15 @@ public class ServletRegistrationBean<T extends Servlet>
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the servlet being registered.
|
||||
* @return the servlet
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public T getServlet() {
|
||||
return this.servlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL mappings for the servlet. If not specified the mapping will default to
|
||||
* '/'. This will replace any previously specified mappings.
|
||||
|
||||
Reference in New Issue
Block a user