diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/ManagementContextFactory.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/ManagementContextFactory.java index 4e806db463dd..bbefe35b7bb7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/ManagementContextFactory.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/ManagementContextFactory.java @@ -28,6 +28,8 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigRegistry; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; /** * Factory for creating a separate management context when the management web server is @@ -55,8 +57,15 @@ public ManagementContextFactory(WebApplicationType webApplicationType, } public ConfigurableApplicationContext createManagementContext(ApplicationContext parentContext) { + Environment parentEnvironment = parentContext.getEnvironment(); + ConfigurableEnvironment childEnvironment = ApplicationContextFactory.DEFAULT + .createEnvironment(this.webApplicationType); + if (parentEnvironment instanceof ConfigurableEnvironment) { + childEnvironment.setConversionService(((ConfigurableEnvironment) parentEnvironment).getConversionService()); + } ConfigurableApplicationContext managementContext = ApplicationContextFactory.DEFAULT .create(this.webApplicationType); + managementContext.setEnvironment(childEnvironment); managementContext.setParent(parentContext); return managementContext; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfigurationIntegrationTests.java index c9cfda2b0a8d..e785ca9d5665 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfigurationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -16,9 +16,13 @@ package org.springframework.boot.actuate.autoconfigure.web.reactive; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.function.Consumer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; @@ -29,12 +33,17 @@ import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration; import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration; +import org.springframework.boot.convert.ApplicationConversionService; +import org.springframework.boot.env.ConfigTreePropertySource; import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer; import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.http.MediaType; +import org.springframework.util.FileCopyUtils; import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; @@ -57,6 +66,9 @@ class ReactiveManagementChildContextConfigurationIntegrationTests { .withInitializer(new ServerPortInfoApplicationContextInitializer()).withPropertyValues( "server.port=0", "management.server.port=0", "management.endpoints.web.exposure.include=*"); + @TempDir + Path temp; + @Test void endpointsAreBeneathActuatorByDefault() { this.runner.withPropertyValues("management.server.port:0").run(withWebTestClient((client) -> { @@ -76,6 +88,28 @@ void whenManagementServerBasePathIsConfiguredThenEndpointsAreBeneathThatPath() { })); } + @Test // gh-32941 + void whenManagementServerPortLoadedFromConfigTree() { + this.runner.withInitializer(this::addConfigTreePropertySource) + .run((context) -> assertThat(context).hasNotFailed()); + } + + private void addConfigTreePropertySource(ConfigurableApplicationContext applicationContext) { + try { + applicationContext.getEnvironment().setConversionService( + (ConfigurableConversionService) ApplicationConversionService.getSharedInstance()); + Path configtree = this.temp.resolve("configtree"); + Path file = configtree.resolve("management/server/port"); + file.toFile().getParentFile().mkdirs(); + FileCopyUtils.copy("0".getBytes(StandardCharsets.UTF_8), file.toFile()); + ConfigTreePropertySource source = new ConfigTreePropertySource("configtree", configtree); + applicationContext.getEnvironment().getPropertySources().addFirst(source); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + private ContextConsumer withWebTestClient(Consumer webClient) { return (context) -> { String port = context.getEnvironment().getProperty("local.management.port"); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationIntegrationTests.java index e0404d4c4b11..106a4558f29b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationIntegrationTests.java @@ -16,6 +16,9 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.Collections; import java.util.Map; import java.util.function.Consumer; @@ -24,6 +27,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import reactor.core.publisher.Mono; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; @@ -36,13 +40,18 @@ import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; +import org.springframework.boot.convert.ApplicationConversionService; +import org.springframework.boot.env.ConfigTreePropertySource; import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.http.MediaType; +import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -74,6 +83,9 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests { "management.endpoints.web.exposure.include=*", "server.error.include-exception=true", "server.error.include-message=always", "server.error.include-binding-errors=always"); + @TempDir + Path temp; + @Test // gh-17938 void errorEndpointIsUsedWithEndpoint() { this.runner.run(withWebTestClient((client) -> { @@ -134,6 +146,28 @@ void whenManagementServerBasePathIsConfiguredThenEndpointsAreBeneathThatPath() { })); } + @Test // gh-32941 + void whenManagementServerPortLoadedFromConfigTree() { + this.runner.withInitializer(this::addConfigTreePropertySource) + .run((context) -> assertThat(context).hasNotFailed()); + } + + private void addConfigTreePropertySource(ConfigurableApplicationContext applicationContext) { + try { + applicationContext.getEnvironment().setConversionService( + (ConfigurableConversionService) ApplicationConversionService.getSharedInstance()); + Path configtree = this.temp.resolve("configtree"); + Path file = configtree.resolve("management/server/port"); + file.toFile().getParentFile().mkdirs(); + FileCopyUtils.copy("0".getBytes(StandardCharsets.UTF_8), file.toFile()); + ConfigTreePropertySource source = new ConfigTreePropertySource("configtree", configtree); + applicationContext.getEnvironment().getPropertySources().addFirst(source); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + private ContextConsumer withWebTestClient(Consumer webClient) { return (context) -> { String port = context.getEnvironment().getProperty("local.management.port");