diff --git a/core/spring-boot/src/main/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessor.java b/core/spring-boot/src/main/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessor.java index 420030bca9e..2a2fb7b3354 100644 --- a/core/spring-boot/src/main/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessor.java +++ b/core/spring-boot/src/main/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessor.java @@ -40,6 +40,7 @@ import org.springframework.util.StringUtils; * {@link SystemEnvironmentOrigin} for every system environment property. * * @author Madhura Bhave + * @author Phillip Webb * @since 4.0.0 */ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { @@ -53,10 +54,14 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + postProcessEnvironment(environment, application.getEnvironmentPrefix()); + } + + private void postProcessEnvironment(ConfigurableEnvironment environment, @Nullable String environmentPrefix) { String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME; PropertySource propertySource = environment.getPropertySources().get(sourceName); if (propertySource != null) { - replacePropertySource(environment, sourceName, propertySource, application.getEnvironmentPrefix()); + replacePropertySource(environment, sourceName, propertySource, environmentPrefix); } } @@ -78,6 +83,23 @@ public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements this.order = order; } + /** + * Post-process the given {@link ConfigurableEnvironment} by copying appropriate + * settings from a parent {@link ConfigurableEnvironment}. + * @param environment the environment to post-process + * @param parentEnvironment the parent environment + * @since 3.4.12 + */ + public static void postProcessEnvironment(ConfigurableEnvironment environment, + ConfigurableEnvironment parentEnvironment) { + PropertySource parentSystemEnvironmentPropertySource = parentEnvironment.getPropertySources() + .get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME); + if (parentSystemEnvironmentPropertySource instanceof OriginAwareSystemEnvironmentPropertySource parentOriginAwareSystemEnvironmentPropertySource) { + new SystemEnvironmentPropertySourceEnvironmentPostProcessor().postProcessEnvironment(environment, + parentOriginAwareSystemEnvironmentPropertySource.getPrefix()); + } + } + /** * {@link SystemEnvironmentPropertySource} that also tracks {@link Origin}. */ diff --git a/core/spring-boot/src/test/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessorTests.java b/core/spring-boot/src/test/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessorTests.java index 8f9037d6441..5a8763b16f8 100644 --- a/core/spring-boot/src/test/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessorTests.java +++ b/core/spring-boot/src/test/java/org/springframework/boot/support/SystemEnvironmentPropertySourceEnvironmentPostProcessorTests.java @@ -111,4 +111,19 @@ class SystemEnvironmentPropertySourceEnvironmentPostProcessorTests { assertThat(replaced.getPrefix()).isEqualTo("my"); } + @Test + void postProcessWithParentEnvironmentShouldApplyPrefix() { + SpringApplication application = new SpringApplication(); + application.setEnvironmentPrefix("my"); + new SystemEnvironmentPropertySourceEnvironmentPostProcessor().postProcessEnvironment(this.environment, + application); + StandardEnvironment child = new StandardEnvironment(); + SystemEnvironmentPropertySourceEnvironmentPostProcessor.postProcessEnvironment(child, this.environment); + OriginAwareSystemEnvironmentPropertySource replaced = (OriginAwareSystemEnvironmentPropertySource) child + .getPropertySources() + .get("systemEnvironment"); + assertThat(replaced).isNotNull(); + assertThat(replaced.getPrefix()).isEqualTo("my"); + } + } diff --git a/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ChildManagementContextInitializer.java b/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ChildManagementContextInitializer.java index c8b4ecc0fe8..a7a5a601f99 100644 --- a/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ChildManagementContextInitializer.java +++ b/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ChildManagementContextInitializer.java @@ -139,6 +139,10 @@ class ChildManagementContextInitializer implements BeanRegistrationAotProcessor, return false; } + @Nullable ConfigurableApplicationContext getManagementContext() { + return this.managementContext; + } + private void registerBeans(ConfigurableApplicationContext managementContext) { if (this.applicationContextInitializer != null) { this.applicationContextInitializer.initialize(managementContext); diff --git a/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextFactory.java b/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextFactory.java index 7345532d606..0bc4c5b8898 100644 --- a/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextFactory.java +++ b/module/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextFactory.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.ApplicationContextFactory; import org.springframework.boot.WebApplicationType; +import org.springframework.boot.support.SystemEnvironmentPropertySourceEnvironmentPostProcessor; import org.springframework.boot.web.server.WebServerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; @@ -62,8 +63,8 @@ public final class ManagementContextFactory { ConfigurableEnvironment childEnvironment = ApplicationContextFactory.DEFAULT .createEnvironment(this.webApplicationType); Assert.state(childEnvironment != null, "'childEnvironment' must not be null"); - if (parentEnvironment instanceof ConfigurableEnvironment configurableEnvironment) { - childEnvironment.setConversionService((configurableEnvironment).getConversionService()); + if (parentEnvironment instanceof ConfigurableEnvironment configurableParentEnvironment) { + postProcessChildEnvironment(childEnvironment, configurableParentEnvironment); } ConfigurableApplicationContext managementContext = ApplicationContextFactory.DEFAULT .create(this.webApplicationType); @@ -73,6 +74,13 @@ public final class ManagementContextFactory { return managementContext; } + private void postProcessChildEnvironment(ConfigurableEnvironment childEnvironment, + ConfigurableEnvironment parentEnvironment) { + childEnvironment.setConversionService((parentEnvironment).getConversionService()); + SystemEnvironmentPropertySourceEnvironmentPostProcessor.postProcessEnvironment(childEnvironment, + parentEnvironment); + } + public void registerWebServerFactoryBeans(ApplicationContext parentContext, ConfigurableApplicationContext managementContext, AnnotationConfigRegistry registry) { if (this.autoConfigurationClasses != null && this.autoConfigurationClasses.length > 0) { diff --git a/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/actuate/web/ServletManagementContextAutoConfigurationIntegrationTests.java b/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/actuate/web/ServletManagementContextAutoConfigurationIntegrationTests.java index 346f9165d2b..a4d38815a2e 100644 --- a/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/actuate/web/ServletManagementContextAutoConfigurationIntegrationTests.java +++ b/module/spring-boot-servlet/src/test/java/org/springframework/boot/servlet/autoconfigure/actuate/web/ServletManagementContextAutoConfigurationIntegrationTests.java @@ -16,21 +16,33 @@ package org.springframework.boot.servlet.autoconfigure.actuate.web; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.env.PropertySourceInfo; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.boot.tomcat.autoconfigure.actuate.web.server.TomcatServletManagementContextAutoConfiguration; import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration; import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -100,6 +112,31 @@ class ServletManagementContextAutoConfigurationIntegrationTests { .hasMessageStartingWith("Management-specific server address cannot be configured")); } + @Test // gh-45858 + void childEnvironmentShouldInheritPrefix() throws Exception { + SpringApplication application = new SpringApplication(ChildEnvironmentConfiguration.class); + Map properties = new LinkedHashMap<>(); + properties.put("server.port", "0"); + properties.put("management.server.port", "0"); + application.setDefaultProperties(properties); + application.setEnvironmentPrefix("my"); + try (ConfigurableApplicationContext parentContext = application.run()) { + Class initializerClass = ClassUtils.forName( + "org.springframework.boot.actuate.autoconfigure.web.server.ChildManagementContextInitializer", + null); + Object initializer = parentContext.getBean(initializerClass); + ConfigurableApplicationContext managementContext = (ConfigurableApplicationContext) ReflectionTestUtils + .getField(initializer, "managementContext"); + assertThat(managementContext).isNotNull(); + ConfigurableEnvironment managementEnvironment = managementContext.getEnvironment(); + assertThat(managementEnvironment).isNotNull(); + PropertySource systemEnvironmentPropertySource = managementEnvironment.getPropertySources() + .get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME); + assertThat(systemEnvironmentPropertySource).isNotNull(); + assertThat(((PropertySourceInfo) systemEnvironmentPropertySource).getPrefix()).isEqualTo("my"); + } + } + private Consumer numberOfOccurrences(String substring, int expectedCount) { return (charSequence) -> { int count = StringUtils.countOccurrencesOf(charSequence.toString(), substring); @@ -107,4 +144,12 @@ class ServletManagementContextAutoConfigurationIntegrationTests { }; } + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration({ ManagementContextAutoConfiguration.class, TomcatServletWebServerAutoConfiguration.class, + TomcatServletManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class, + WebEndpointAutoConfiguration.class, EndpointAutoConfiguration.class }) + static class ChildEnvironmentConfiguration { + + } + }