From 9ffbfb0d80fbcde84f2d6183a18337f946c0d806 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 4 Sep 2017 10:58:58 +0100 Subject: [PATCH] Ensure that endpoints are created before their web extensions Closes gh-10140 --- .../ManagementContextAutoConfiguration.java | 3 + .../web/HealthWebEndpointConfiguration.java | 23 ++-- ...ointAutoConfigurationIntegrationTests.java | 105 ++++++++++++++++++ 3 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationIntegrationTests.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextAutoConfiguration.java index 98a2eac0d28..3bdf56e4506 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementContextAutoConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties; import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; @@ -33,6 +34,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; @@ -51,6 +53,7 @@ import org.springframework.web.context.ConfigurableWebApplicationContext; * @since 2.0.0 */ @Configuration +@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) public class ManagementContextAutoConfiguration { @Configuration diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/HealthWebEndpointConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/HealthWebEndpointConfiguration.java index 91c8b48e416..59fa01f6667 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/HealthWebEndpointConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/HealthWebEndpointConfiguration.java @@ -38,14 +38,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * Configuration for web-specific health endpoints - * . + * Configuration for web-specific health endpoints . * @author Stephane Nicoll * @since 2.0.0 */ @@ -73,17 +71,18 @@ public class HealthWebEndpointConfiguration { ReactiveWebHealthConfiguration(ObjectProvider healthAggregator, ObjectProvider> reactiveHealthIndicators, ObjectProvider> healthIndicators) { - this.reactiveHealthIndicator = new CompositeReactiveHealthIndicatorFactory().createReactiveHealthIndicator( - healthAggregator.getIfAvailable(OrderedHealthAggregator::new), - reactiveHealthIndicators.getIfAvailable(Collections::emptyMap), - healthIndicators.getIfAvailable(Collections::emptyMap)); + this.reactiveHealthIndicator = new CompositeReactiveHealthIndicatorFactory() + .createReactiveHealthIndicator( + healthAggregator.getIfAvailable(OrderedHealthAggregator::new), + reactiveHealthIndicators + .getIfAvailable(Collections::emptyMap), + healthIndicators.getIfAvailable(Collections::emptyMap)); } - @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - @ConditionalOnBean(value = HealthEndpoint.class, search = SearchStrategy.CURRENT) + @ConditionalOnBean(HealthEndpoint.class) public HealthReactiveWebEndpointExtension healthWebEndpointExtension( HealthStatusHttpMapper healthStatusHttpMapper) { return new HealthReactiveWebEndpointExtension(this.reactiveHealthIndicator, @@ -93,7 +92,7 @@ public class HealthWebEndpointConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - @ConditionalOnBean(value = StatusEndpoint.class, search = SearchStrategy.CURRENT) + @ConditionalOnBean(StatusEndpoint.class) public StatusReactiveWebEndpointExtension statusWebEndpointExtension( HealthStatusHttpMapper healthStatusHttpMapper) { return new StatusReactiveWebEndpointExtension(this.reactiveHealthIndicator, @@ -109,7 +108,7 @@ public class HealthWebEndpointConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - @ConditionalOnBean(value = HealthEndpoint.class, search = SearchStrategy.CURRENT) + @ConditionalOnBean(HealthEndpoint.class) public HealthWebEndpointExtension healthWebEndpointExtension( HealthEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) { return new HealthWebEndpointExtension(delegate, healthStatusHttpMapper); @@ -118,7 +117,7 @@ public class HealthWebEndpointConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - @ConditionalOnBean(value = StatusEndpoint.class, search = SearchStrategy.CURRENT) + @ConditionalOnBean(StatusEndpoint.class) public StatusWebEndpointExtension statusWebEndpointExtension( StatusEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) { return new StatusWebEndpointExtension(delegate, healthStatusHttpMapper); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationIntegrationTests.java new file mode 100644 index 00000000000..7b5f3a2d3d8 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfigurationIntegrationTests.java @@ -0,0 +1,105 @@ +/* + * 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.endpoint.web; + +import org.junit.Test; + +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.actuate.endpoint.web.HealthReactiveWebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.StatusReactiveWebEndpointExtension; +import org.springframework.boot.actuate.endpoint.web.StatusWebEndpointExtension; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration; +import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration; +import org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration; +import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration; +import org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration; +import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration; +import org.springframework.boot.context.annotation.UserConfigurations; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for the auto-configuration of web endpoints. + * + * @author Andy Wilkinson + */ +public class WebEndpointAutoConfigurationIntegrationTests { + + @Test + public void healthEndpointWebExtensionIsAutoConfigured() { + servletWebRunner().run((context) -> assertThat(context) + .hasSingleBean(HealthWebEndpointExtension.class)); + } + + @Test + public void statusEndpointWebExtensionIsAutoConfigured() { + servletWebRunner().run((context) -> assertThat(context) + .hasSingleBean(StatusWebEndpointExtension.class)); + } + + @Test + public void healthEndpointReactiveWebExtensionIsAutoConfigured() { + reactiveWebRunner().run((context) -> assertThat(context) + .hasSingleBean(HealthReactiveWebEndpointExtension.class)); + } + + @Test + public void statusEndpointReactiveWebExtensionIsAutoConfigured() { + reactiveWebRunner().run((context) -> assertThat(context) + .hasSingleBean(StatusReactiveWebEndpointExtension.class)); + } + + private WebApplicationContextRunner servletWebRunner() { + return new WebApplicationContextRunner().withConfiguration( + UserConfigurations.of(WebEndpointTestApplication.class)); + } + + private ReactiveWebApplicationContextRunner reactiveWebRunner() { + return new ReactiveWebApplicationContextRunner().withConfiguration( + UserConfigurations.of(WebEndpointTestApplication.class)); + } + + @EnableAutoConfiguration(exclude = { FlywayAutoConfiguration.class, + LiquibaseAutoConfiguration.class, CassandraAutoConfiguration.class, + CassandraDataAutoConfiguration.class, Neo4jDataAutoConfiguration.class, + Neo4jRepositoriesAutoConfiguration.class, MongoAutoConfiguration.class, + RepositoryRestMvcAutoConfiguration.class, HazelcastAutoConfiguration.class, + MongoDataAutoConfiguration.class, ElasticsearchAutoConfiguration.class, + ElasticsearchDataAutoConfiguration.class, JestAutoConfiguration.class, + SolrRepositoriesAutoConfiguration.class, SolrAutoConfiguration.class, + RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class }) + @SpringBootConfiguration + public static class WebEndpointTestApplication { + + } + +}