|
|
|
|
@ -18,16 +18,14 @@ package org.springframework.boot.actuate.endpoint.web.test;
@@ -18,16 +18,14 @@ package org.springframework.boot.actuate.endpoint.web.test;
|
|
|
|
|
|
|
|
|
|
import java.time.Duration; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.Collection; |
|
|
|
|
import java.util.Collections; |
|
|
|
|
import java.util.HashSet; |
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.function.Function; |
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
|
import org.glassfish.jersey.server.ResourceConfig; |
|
|
|
|
import org.glassfish.jersey.server.model.Resource; |
|
|
|
|
import org.junit.jupiter.api.extension.AfterEachCallback; |
|
|
|
|
import org.junit.jupiter.api.extension.BeforeEachCallback; |
|
|
|
|
import org.junit.jupiter.api.extension.Extension; |
|
|
|
|
@ -38,29 +36,10 @@ import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
@@ -38,29 +36,10 @@ import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
|
|
|
|
|
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; |
|
|
|
|
import org.junit.platform.commons.util.AnnotationUtils; |
|
|
|
|
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; |
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest.Infrastructure; |
|
|
|
|
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; |
|
|
|
|
import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration; |
|
|
|
|
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration; |
|
|
|
|
import org.springframework.boot.jersey.actuate.endpoint.web.JerseyEndpointResourceFactory; |
|
|
|
|
import org.springframework.boot.jersey.autoconfigure.JerseyAutoConfiguration; |
|
|
|
|
import org.springframework.boot.jersey.autoconfigure.ResourceConfigCustomizer; |
|
|
|
|
import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory; |
|
|
|
|
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; |
|
|
|
|
import org.springframework.boot.web.server.context.WebServerInitializedEvent; |
|
|
|
|
import org.springframework.boot.web.server.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; |
|
|
|
|
import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext; |
|
|
|
|
import org.springframework.boot.webflux.autoconfigure.WebFluxAutoConfiguration; |
|
|
|
|
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; |
|
|
|
|
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration; |
|
|
|
|
import org.springframework.context.ApplicationContext; |
|
|
|
|
import org.springframework.context.ApplicationListener; |
|
|
|
|
import org.springframework.context.ConfigurableApplicationContext; |
|
|
|
|
import org.springframework.context.annotation.AnnotationConfigRegistry; |
|
|
|
|
@ -68,12 +47,10 @@ import org.springframework.context.annotation.Bean;
@@ -68,12 +47,10 @@ import org.springframework.context.annotation.Bean;
|
|
|
|
|
import org.springframework.context.annotation.Configuration; |
|
|
|
|
import org.springframework.core.annotation.MergedAnnotations; |
|
|
|
|
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; |
|
|
|
|
import org.springframework.core.io.support.SpringFactoriesLoader; |
|
|
|
|
import org.springframework.http.HttpMethod; |
|
|
|
|
import org.springframework.http.server.reactive.HttpHandler; |
|
|
|
|
import org.springframework.test.web.reactive.server.WebTestClient; |
|
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
|
import org.springframework.web.cors.CorsConfiguration; |
|
|
|
|
import org.springframework.web.server.adapter.WebHttpHandlerBuilder; |
|
|
|
|
import org.springframework.web.util.DefaultUriBuilderFactory; |
|
|
|
|
import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; |
|
|
|
|
|
|
|
|
|
@ -81,10 +58,29 @@ import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
@@ -81,10 +58,29 @@ import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
|
|
|
|
|
* {@link TestTemplateInvocationContextProvider} for |
|
|
|
|
* {@link WebEndpointTest @WebEndpointTest}. |
|
|
|
|
* |
|
|
|
|
* @author Andy Wilkinson |
|
|
|
|
* @author Andy Wilkinson` |
|
|
|
|
* @author Stephane Nicoll |
|
|
|
|
*/ |
|
|
|
|
class WebEndpointTestInvocationContextProvider implements TestTemplateInvocationContextProvider { |
|
|
|
|
|
|
|
|
|
private final Map<Infrastructure, List<Class<?>>> infrastructures; |
|
|
|
|
|
|
|
|
|
private final Map<Infrastructure, Function<List<Class<?>>, ConfigurableApplicationContext>> contextFactories; |
|
|
|
|
|
|
|
|
|
WebEndpointTestInvocationContextProvider() { |
|
|
|
|
this.infrastructures = new HashMap<>(); |
|
|
|
|
List<WebEndpointInfrastructureProvider> providers = SpringFactoriesLoader |
|
|
|
|
.loadFactories(WebEndpointInfrastructureProvider.class, getClass().getClassLoader()); |
|
|
|
|
Stream.of(Infrastructure.values()) |
|
|
|
|
.forEach((infrastructure) -> providers.stream() |
|
|
|
|
.filter((provider) -> provider.supports(infrastructure)) |
|
|
|
|
.findFirst() |
|
|
|
|
.ifPresent((provider) -> this.infrastructures.put(infrastructure, |
|
|
|
|
provider.getInfrastructureConfiguration(infrastructure)))); |
|
|
|
|
this.contextFactories = Map.of(Infrastructure.JERSEY, this::createJerseyContext, Infrastructure.MVC, |
|
|
|
|
this::createWebMvcContext, Infrastructure.WEBFLUX, this::createWebFluxContext); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean supportsTestTemplate(ExtensionContext context) { |
|
|
|
|
return true; |
|
|
|
|
@ -97,33 +93,49 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
@@ -97,33 +93,49 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
|
|
|
|
|
.findAnnotation(extensionContext.getRequiredTestMethod(), WebEndpointTest.class) |
|
|
|
|
.orElseThrow(() -> new IllegalStateException("Unable to find WebEndpointTest annotation on %s" |
|
|
|
|
.formatted(extensionContext.getRequiredTestMethod()))); |
|
|
|
|
return Stream.of(webEndpointTest.infrastructure()).distinct().map(Infrastructure::createInvocationContext); |
|
|
|
|
return Stream.of(webEndpointTest.infrastructure()).distinct().map(this::createInvocationContext); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private WebEndpointsInvocationContext createInvocationContext(Infrastructure infrastructure) { |
|
|
|
|
return infrastructure.createInvocationContext(this.contextFactories.get(infrastructure)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static ConfigurableApplicationContext createJerseyContext(List<Class<?>> classes) { |
|
|
|
|
private ConfigurableApplicationContext createJerseyContext(List<Class<?>> classes) { |
|
|
|
|
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); |
|
|
|
|
classes.add(JerseyEndpointConfiguration.class); |
|
|
|
|
classes.addAll(getEndpointInfrastructureConfiguration(Infrastructure.JERSEY)); |
|
|
|
|
context.register(ClassUtils.toClassArray(classes)); |
|
|
|
|
context.refresh(); |
|
|
|
|
return context; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static ConfigurableApplicationContext createWebMvcContext(List<Class<?>> classes) { |
|
|
|
|
private ConfigurableApplicationContext createWebMvcContext(List<Class<?>> classes) { |
|
|
|
|
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); |
|
|
|
|
classes.add(WebMvcEndpointConfiguration.class); |
|
|
|
|
classes.addAll(getEndpointInfrastructureConfiguration(Infrastructure.MVC)); |
|
|
|
|
context.register(ClassUtils.toClassArray(classes)); |
|
|
|
|
context.refresh(); |
|
|
|
|
return context; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static ConfigurableApplicationContext createWebFluxContext(List<Class<?>> classes) { |
|
|
|
|
private ConfigurableApplicationContext createWebFluxContext(List<Class<?>> classes) { |
|
|
|
|
AnnotationConfigReactiveWebServerApplicationContext context = new AnnotationConfigReactiveWebServerApplicationContext(); |
|
|
|
|
classes.add(WebFluxEndpointConfiguration.class); |
|
|
|
|
classes.addAll(getEndpointInfrastructureConfiguration(Infrastructure.WEBFLUX)); |
|
|
|
|
context.register(ClassUtils.toClassArray(classes)); |
|
|
|
|
context.refresh(); |
|
|
|
|
return context; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private List<Class<?>> getEndpointInfrastructureConfiguration(Infrastructure infrastructure) { |
|
|
|
|
List<Class<?>> configurationClasses = new ArrayList<>(); |
|
|
|
|
configurationClasses.add(EndpointBaseConfiguration.class); |
|
|
|
|
List<Class<?>> endpointConfiguration = this.infrastructures.get(infrastructure); |
|
|
|
|
if (endpointConfiguration == null) { |
|
|
|
|
throw new IllegalStateException( |
|
|
|
|
"No endpoint infrastructure configuration found for " + infrastructure.name()); |
|
|
|
|
} |
|
|
|
|
configurationClasses.addAll(endpointConfiguration); |
|
|
|
|
return configurationClasses; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static class WebEndpointsInvocationContext |
|
|
|
|
implements TestTemplateInvocationContext, BeforeEachCallback, AfterEachCallback, ParameterResolver { |
|
|
|
|
|
|
|
|
|
@ -206,69 +218,16 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
@@ -206,69 +218,16 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private int determinePort() { |
|
|
|
|
if (this.context instanceof AnnotationConfigServletWebServerApplicationContext webServerContext) { |
|
|
|
|
return webServerContext.getWebServer().getPort(); |
|
|
|
|
} |
|
|
|
|
return this.context.getBean(PortHolder.class).getPort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, JerseyAutoConfiguration.class }) |
|
|
|
|
static class JerseyEndpointConfiguration { |
|
|
|
|
|
|
|
|
|
private final ApplicationContext applicationContext; |
|
|
|
|
|
|
|
|
|
JerseyEndpointConfiguration(ApplicationContext applicationContext) { |
|
|
|
|
this.applicationContext = applicationContext; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
TomcatServletWebServerFactory tomcat() { |
|
|
|
|
return new TomcatServletWebServerFactory(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
ResourceConfig resourceConfig() { |
|
|
|
|
return new ResourceConfig(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
ResourceConfigCustomizer webEndpointRegistrar() { |
|
|
|
|
return this::customize; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void customize(ResourceConfig config) { |
|
|
|
|
EndpointMediaTypes endpointMediaTypes = EndpointMediaTypes.DEFAULT; |
|
|
|
|
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(this.applicationContext, |
|
|
|
|
new ConversionServiceParameterValueMapper(), endpointMediaTypes, null, Collections.emptyList(), |
|
|
|
|
Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); |
|
|
|
|
Collection<Resource> resources = new JerseyEndpointResourceFactory().createEndpointResources( |
|
|
|
|
new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes, |
|
|
|
|
new EndpointLinksResolver(discoverer.getEndpoints()), true); |
|
|
|
|
config.registerResources(new HashSet<>(resources)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, WebFluxAutoConfiguration.class }) |
|
|
|
|
static class WebFluxEndpointConfiguration implements ApplicationListener<WebServerInitializedEvent> { |
|
|
|
|
|
|
|
|
|
private final ApplicationContext applicationContext; |
|
|
|
|
static class EndpointBaseConfiguration implements ApplicationListener<WebServerInitializedEvent> { |
|
|
|
|
|
|
|
|
|
private final PortHolder portHolder = new PortHolder(); |
|
|
|
|
|
|
|
|
|
WebFluxEndpointConfiguration(ApplicationContext applicationContext) { |
|
|
|
|
this.applicationContext = applicationContext; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
NettyReactiveWebServerFactory netty() { |
|
|
|
|
return new NettyReactiveWebServerFactory(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
PortHolder portHolder() { |
|
|
|
|
return this.portHolder; |
|
|
|
|
@ -279,51 +238,6 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
@@ -279,51 +238,6 @@ class WebEndpointTestInvocationContextProvider implements TestTemplateInvocation
|
|
|
|
|
this.portHolder.setPort(event.getWebServer().getPort()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
HttpHandler httpHandler(ApplicationContext applicationContext) { |
|
|
|
|
return WebHttpHandlerBuilder.applicationContext(applicationContext).build(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping() { |
|
|
|
|
EndpointMediaTypes endpointMediaTypes = EndpointMediaTypes.DEFAULT; |
|
|
|
|
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(this.applicationContext, |
|
|
|
|
new ConversionServiceParameterValueMapper(), endpointMediaTypes, Collections.emptyList(), |
|
|
|
|
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); |
|
|
|
|
return new WebFluxEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), |
|
|
|
|
endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), |
|
|
|
|
true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, |
|
|
|
|
WebMvcAutoConfiguration.class, DispatcherServletAutoConfiguration.class }) |
|
|
|
|
static class WebMvcEndpointConfiguration { |
|
|
|
|
|
|
|
|
|
private final ApplicationContext applicationContext; |
|
|
|
|
|
|
|
|
|
WebMvcEndpointConfiguration(ApplicationContext applicationContext) { |
|
|
|
|
this.applicationContext = applicationContext; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
TomcatServletWebServerFactory tomcat() { |
|
|
|
|
return new TomcatServletWebServerFactory(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() { |
|
|
|
|
EndpointMediaTypes endpointMediaTypes = EndpointMediaTypes.DEFAULT; |
|
|
|
|
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(this.applicationContext, |
|
|
|
|
new ConversionServiceParameterValueMapper(), endpointMediaTypes, Collections.emptyList(), |
|
|
|
|
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); |
|
|
|
|
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(), |
|
|
|
|
endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()), |
|
|
|
|
true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static final class PortHolder { |