diff --git a/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortApplicationListener.java b/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizer.java similarity index 86% rename from module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortApplicationListener.java rename to module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizer.java index 00bf9b01301..e43ca14a349 100644 --- a/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortApplicationListener.java +++ b/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizer.java @@ -20,10 +20,8 @@ import java.util.Objects; import org.jspecify.annotations.Nullable; -import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; -import org.springframework.context.ApplicationEvent; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.event.SmartApplicationListener; -import org.springframework.core.Ordered; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; import org.springframework.core.env.ConfigurableEnvironment; @@ -33,6 +31,8 @@ import org.springframework.core.env.PropertyResolver; import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySources; import org.springframework.lang.Contract; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.util.ClassUtils; @@ -45,25 +45,13 @@ import org.springframework.util.ClassUtils; * @author Andy Wilkinson * @author Phillip Webb */ -class SpringBootTestRandomPortApplicationListener implements SmartApplicationListener, Ordered { +class SpringBootTestRandomPortContextCustomizer implements ContextCustomizer { private static final String TEST_SOURCE_NAME = TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME; @Override - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; - } - - @Override - public boolean supportsEventType(Class eventType) { - return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType); - } - - @Override - public void onApplicationEvent(ApplicationEvent event) { - if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) { - postProcessEnvironment(environmentPreparedEvent.getEnvironment()); - } + public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { + postProcessEnvironment(context.getEnvironment()); } void postProcessEnvironment(ConfigurableEnvironment environment) { @@ -92,6 +80,16 @@ class SpringBootTestRandomPortApplicationListener implements SmartApplicationLis return (!managementPort.equals(serverPort)) ? "0" : ""; } + @Override + public boolean equals(Object obj) { + return (obj != null) && (obj.getClass() == getClass()); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + private enum Port { SERVER("server.port"), MANAGEMENT("management.server.port"); diff --git a/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerFactory.java b/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerFactory.java index 0c336e5e436..66f0d325c2e 100644 --- a/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerFactory.java +++ b/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerFactory.java @@ -20,15 +20,13 @@ import java.util.List; import org.jspecify.annotations.Nullable; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizerFactory; -import org.springframework.test.context.MergedContextConfiguration; /** - * {@link ContextCustomizerFactory} apply - * {@link SpringBootTestRandomPortApplicationListener} to tests. + * {@link ContextCustomizerFactory} implementation to apply + * {@link SpringBootTestRandomPortContextCustomizer} to tests. * * @author Phillip Webb */ @@ -37,26 +35,7 @@ class SpringBootTestRandomPortContextCustomizerFactory implements ContextCustomi @Override public @Nullable ContextCustomizer createContextCustomizer(Class testClass, List configAttributes) { - return new Customizer(); - } - - static class Customizer implements ContextCustomizer { - - @Override - public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { - context.addApplicationListener(new SpringBootTestRandomPortApplicationListener()); - } - - @Override - public boolean equals(Object obj) { - return (obj != null) && (obj.getClass() == getClass()); - } - - @Override - public int hashCode() { - return getClass().hashCode(); - } - + return new SpringBootTestRandomPortContextCustomizer(); } } diff --git a/module/spring-boot-web-server/src/test/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortApplicationListenerTests.java b/module/spring-boot-web-server/src/test/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerTests.java similarity index 88% rename from module/spring-boot-web-server/src/test/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortApplicationListenerTests.java rename to module/spring-boot-web-server/src/test/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerTests.java index 1d8439f71f6..9e67b726466 100644 --- a/module/spring-boot-web-server/src/test/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortApplicationListenerTests.java +++ b/module/spring-boot-web-server/src/test/java/org/springframework/boot/web/server/context/SpringBootTestRandomPortContextCustomizerTests.java @@ -34,14 +34,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** - * Tests for {@link SpringBootTestRandomPortApplicationListener}. + * Tests for {@link SpringBootTestRandomPortContextCustomizer}. * * @author Madhura Bhave * @author Andy Wilkinson */ -class SpringBootTestRandomPortApplicationListenerTests { +class SpringBootTestRandomPortContextCustomizerTests { - private final SpringBootTestRandomPortApplicationListener listener = new SpringBootTestRandomPortApplicationListener(); + private final SpringBootTestRandomPortContextCustomizer customizer = new SpringBootTestRandomPortContextCustomizer(); private MockEnvironment environment; @@ -56,7 +56,7 @@ class SpringBootTestRandomPortApplicationListenerTests { @Test void postProcessWhenServerAndManagementPortIsZeroInTestPropertySource() { addTestPropertySource("0", "0"); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0"); } @@ -67,7 +67,7 @@ class SpringBootTestRandomPortApplicationListenerTests { Map source = new HashMap<>(); source.put("management.server.port", "0"); this.propertySources.addLast(new MapPropertySource("other", source)); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0"); } @@ -77,7 +77,7 @@ class SpringBootTestRandomPortApplicationListenerTests { addTestPropertySource("8080", "8081"); this.environment.setProperty("server.port", "8080"); this.environment.setProperty("management.server.port", "8081"); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("8080"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("8081"); } @@ -85,7 +85,7 @@ class SpringBootTestRandomPortApplicationListenerTests { @Test void postProcessWhenTestServerPortIsZeroAndTestManagementPortIsNotNull() { addTestPropertySource("0", "8080"); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("8080"); } @@ -93,7 +93,7 @@ class SpringBootTestRandomPortApplicationListenerTests { @Test void postProcessWhenTestServerPortIsZeroAndManagementPortIsNull() { addTestPropertySource("0", null); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isNull(); } @@ -106,7 +106,7 @@ class SpringBootTestRandomPortApplicationListenerTests { other.put("management.server.port", "8081"); MapPropertySource otherSource = new MapPropertySource("other", other); this.propertySources.addLast(otherSource); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEmpty(); } @@ -118,7 +118,7 @@ class SpringBootTestRandomPortApplicationListenerTests { addTestPropertySource("0", null); this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "8080"))); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEmpty(); } @@ -128,7 +128,7 @@ class SpringBootTestRandomPortApplicationListenerTests { addTestPropertySource("0", null); this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "8081"))); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0"); } @@ -138,7 +138,7 @@ class SpringBootTestRandomPortApplicationListenerTests { addTestPropertySource("0", null); this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "-1"))); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("-1"); } @@ -148,7 +148,7 @@ class SpringBootTestRandomPortApplicationListenerTests { addTestPropertySource("0", null); this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", 8081))); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0"); } @@ -162,7 +162,7 @@ class SpringBootTestRandomPortApplicationListenerTests { testPropertySource.getSource().put("port", "9090"); this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}"))); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0"); } @@ -173,7 +173,7 @@ class SpringBootTestRandomPortApplicationListenerTests { this.propertySources .addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}"))); assertThatExceptionOfType(PlaceholderResolutionException.class) - .isThrownBy(() -> this.listener.postProcessEnvironment(this.environment)) + .isThrownBy(() -> this.customizer.postProcessEnvironment(this.environment)) .withMessage("Could not resolve placeholder 'port' in value \"${port}\""); } @@ -188,7 +188,7 @@ class SpringBootTestRandomPortApplicationListenerTests { source.put("server.port", "${port}"); source.put("management.server.port", "9090"); this.propertySources.addLast(new MapPropertySource("other", source)); - this.listener.postProcessEnvironment(this.environment); + this.customizer.postProcessEnvironment(this.environment); assertThat(this.environment.getProperty("server.port")).isEqualTo("0"); assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0"); } @@ -201,7 +201,7 @@ class SpringBootTestRandomPortApplicationListenerTests { source.put("management.server.port", "9090"); this.propertySources.addLast(new MapPropertySource("other", source)); assertThatExceptionOfType(PlaceholderResolutionException.class) - .isThrownBy(() -> this.listener.postProcessEnvironment(this.environment)) + .isThrownBy(() -> this.customizer.postProcessEnvironment(this.environment)) .withMessage("Could not resolve placeholder 'port' in value \"${port}\""); } diff --git a/smoke-test/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementRandomPortWithConfiguredPortSampleActuatorApplicationTests.java b/smoke-test/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementRandomPortWithConfiguredPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..8ce53f3fd59 --- /dev/null +++ b/smoke-test/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementRandomPortWithConfiguredPortSampleActuatorApplicationTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-present 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 + * + * https://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 smoketest.actuator; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for separate management and main service ports when a management port + * is set. + * + * @author Stephane Nicoll + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, args = "--management.server.port=1234") +class ManagementRandomPortWithConfiguredPortSampleActuatorApplicationTests { + + @LocalServerPort + private int serverPort; + + @LocalManagementPort + private int managementPort; + + @Test + void randomPortsShouldBeDifferent() { + assertThat(this.serverPort).isPositive(); + assertThat(this.managementPort).isPositive(); + assertThat(this.serverPort).isNotEqualTo(this.managementPort); + } + + @Test + void randomManagementPortShouldBeApplied() { + assertThat(this.managementPort).isNotEqualTo(1234); + } + + @Test + void testHealth() { + ResponseEntity entity = new TestRestTemplate().withBasicAuth("user", "password") + .getForEntity("http://localhost:" + this.managementPort + "/actuator/health", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"status\":\"UP\""); + } + +}