diff --git a/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java b/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcServerPortInfoAutoConfiguration.java similarity index 66% rename from module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java rename to module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcServerPortInfoAutoConfiguration.java index 76f1e0a3067..80f0109b182 100644 --- a/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializer.java +++ b/module/spring-boot-grpc-test/src/main/java/org/springframework/boot/grpc/test/autoconfigure/GrpcServerPortInfoAutoConfiguration.java @@ -21,11 +21,14 @@ import java.util.Map; import io.grpc.Server; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.env.ConfigTreePropertySource.Value; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; @@ -36,34 +39,41 @@ import org.springframework.grpc.server.InProcessGrpcServerFactory; import org.springframework.grpc.server.lifecycle.GrpcServerStartedEvent; /** - * {@link ApplicationContextInitializer} that sets {@link Environment} properties for the - * ports that {@link Server gRPC servers} are actually listening on. The property - * {@literal "local.grpc.server.port"} can be injected directly into tests using - * {@link Value @Value} or obtained through the {@link Environment}. + * {@link EnableAutoConfiguration Auto-configuration} for an {@link ApplicationListener} + * that sets {@link Environment} properties for the port that a {@link Server gRPC server} + * is actually listening on. The property {@value PROPERTY_NAME} can be injected directly + * into tests using {@link Value @Value} or obtained through the {@link Environment}. *

* Properties are automatically propagated up to any parent context. * * @author Dave Syer * @author Chris Bono * @author Phillip Webb + * @author Stephane Nicoll + * @since 4.1.1 */ -class GrpcPortInfoApplicationContextInitializer - implements ApplicationContextInitializer { +@AutoConfiguration(beforeName = "org.springframework.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration") +@ConditionalOnClass(GrpcServerStartedEvent.class) +public final class GrpcServerPortInfoAutoConfiguration { - @Override - public void initialize(ConfigurableApplicationContext applicationContext) { - applicationContext.addApplicationListener(new Listener(applicationContext)); + /** + * Property that contains the port that a {@link Server gRPC server} is actually + * listening on. + */ + public static final String PROPERTY_NAME = "local.grpc.server.port"; + + @Bean + GrpcPortInfoApplicationListener grpcPortInfoApplicationListener(ConfigurableApplicationContext applicationContext) { + return new GrpcPortInfoApplicationListener(applicationContext); } - private static class Listener implements ApplicationListener { - - private static final String PROPERTY_NAME = "local.grpc.server.port"; + static class GrpcPortInfoApplicationListener implements ApplicationListener { private static final String PROPERTY_SOURCE_NAME = "server.ports"; private final ConfigurableApplicationContext applicationContext; - Listener(ConfigurableApplicationContext applicationContext) { + GrpcPortInfoApplicationListener(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; } diff --git a/module/spring-boot-grpc-test/src/main/resources/META-INF/spring.factories b/module/spring-boot-grpc-test/src/main/resources/META-INF/spring.factories deleted file mode 100644 index a9432eff747..00000000000 --- a/module/spring-boot-grpc-test/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,3 +0,0 @@ -# Application Context Initializers -org.springframework.context.ApplicationContextInitializer=\ -org.springframework.boot.grpc.test.autoconfigure.GrpcPortInfoApplicationContextInitializer diff --git a/module/spring-boot-grpc-test/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/module/spring-boot-grpc-test/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..6ff75cfc88f --- /dev/null +++ b/module/spring-boot-grpc-test/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springframework.boot.grpc.test.autoconfigure.GrpcServerPortInfoAutoConfiguration diff --git a/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java b/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcServerPortInfoAutoConfigurationTests.java similarity index 65% rename from module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java rename to module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcServerPortInfoAutoConfigurationTests.java index 7e37a43647c..73815d94837 100644 --- a/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcPortInfoApplicationContextInitializerTests.java +++ b/module/spring-boot-grpc-test/src/test/java/org/springframework/boot/grpc/test/autoconfigure/GrpcServerPortInfoAutoConfigurationTests.java @@ -20,10 +20,10 @@ import io.grpc.Server; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.grpc.test.autoconfigure.GrpcServerPortInfoAutoConfiguration.GrpcPortInfoApplicationListener; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.grpc.server.GrpcServerFactory; import org.springframework.grpc.server.InProcessGrpcServerFactory; import org.springframework.grpc.server.NettyGrpcServerFactory; @@ -35,22 +35,36 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** - * Tests for {@link GrpcPortInfoApplicationContextInitializer}. + * Tests for {@link GrpcServerPortInfoAutoConfiguration}. * * @author Phillip Webb */ -class GrpcPortInfoApplicationContextInitializerTests { +class GrpcServerPortInfoAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(GrpcServerPortInfoAutoConfiguration.class)); private static final String PORT_PROPERTY = "local.grpc.server.port"; @Test - void whenServerHasAddressInitializerSetsPortProperty() { + void createsGrpcPortInfoApplicationListenerBean() { + this.contextRunner.run((context) -> assertThat(context).hasSingleBean(GrpcPortInfoApplicationListener.class)); + } + + @Test + void whenNoGrpcServerStartedEventClassDoesNotCreateBean() { + this.contextRunner.withClassLoader(new FilteredClassLoader(GrpcServerStartedEvent.class)) + .run((context) -> assertThat(context).doesNotHaveBean(GrpcPortInfoApplicationListener.class)); + } + + @Test + void whenServerHasAddressListenerSetsPortProperty() { NettyGrpcServerFactory factory = mock(); testListener(factory, 65535, "65535"); } @Test - void whenServerHasNoAddressInitializerSetsNoPortProperty() { + void whenServerHasNoAddressListenerSetsNoPortProperty() { NettyGrpcServerFactory factory = mock(); testListener(factory, -1, null); } @@ -68,25 +82,14 @@ class GrpcPortInfoApplicationContextInitializerTests { } private void testListener(GrpcServerFactory factory, int port, @Nullable String expected) { - try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(Config.class)) { - context.getBean(GrpcPortInfoApplicationContextInitializer.class).initialize(context); + this.contextRunner.run((context) -> { GrpcServerLifecycle lifecycle = mock(); Server server = mock(); given(lifecycle.getFactory()).willReturn(factory); GrpcServerStartedEvent event = new GrpcServerStartedEvent(lifecycle, server, "localhost", port); context.publishEvent(event); assertThat(context.getEnvironment().getProperty(PORT_PROPERTY)).isEqualTo(expected); - } - } - - @Configuration(proxyBeanMethods = false) - static class Config { - - @Bean - GrpcPortInfoApplicationContextInitializer grpcPortInfoApplicationContextInitializer() { - return new GrpcPortInfoApplicationContextInitializer(); - } - + }); } } diff --git a/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/build.gradle b/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/build.gradle index 76fbfdca0df..3997c3e266a 100644 --- a/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/build.gradle +++ b/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/build.gradle @@ -28,7 +28,7 @@ dependencies { } implementation("io.grpc:grpc-netty-shaded") - dockerTestImplementation(project(":starter:spring-boot-starter-test")) + dockerTestImplementation(project(":starter:spring-boot-starter-grpc-server-test")) dockerTestImplementation("org.testcontainers:testcontainers-junit-jupiter") } diff --git a/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/src/dockerTest/java/smoketest/grpcservernettyshaded/SampleGrpcServerNettyShadedApplicationTests.java b/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/src/dockerTest/java/smoketest/grpcservernettyshaded/SampleGrpcServerNettyShadedApplicationTests.java index 63af451767b..8aa6a766755 100644 --- a/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/src/dockerTest/java/smoketest/grpcservernettyshaded/SampleGrpcServerNettyShadedApplicationTests.java +++ b/smoke-test/spring-boot-smoke-test-grpc-server-netty-shaded/src/dockerTest/java/smoketest/grpcservernettyshaded/SampleGrpcServerNettyShadedApplicationTests.java @@ -21,13 +21,9 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import smoketest.grpcservernettyshaded.SampleGrpcServerNettyShadedApplicationTests.GrpcServerStartedEventListener; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Import; -import org.springframework.grpc.server.lifecycle.GrpcServerStartedEvent; import static org.assertj.core.api.Assertions.assertThat; @@ -38,18 +34,16 @@ import static org.assertj.core.api.Assertions.assertThat; */ @SpringBootTest(properties = "spring.grpc.server.port=0") @Testcontainers(disabledWithoutDocker = true) -@Import(GrpcServerStartedEventListener.class) class SampleGrpcServerNettyShadedApplicationTests { - @Autowired - private GrpcServerStartedEventListener startedEventListener; + @Value("${local.grpc.server.port}") + private int port; @Test @SuppressWarnings("resource") void test() { - int port = this.startedEventListener.getPort(); - String address = "host.testcontainers.internal:" + port; - org.testcontainers.Testcontainers.exposeHostPorts(port); + String address = "host.testcontainers.internal:" + this.port; + org.testcontainers.Testcontainers.exposeHostPorts(this.port); try (GenericContainer container = new GenericContainer<>( DockerImageName.parse("fullstorydev/grpcurl:v1.9.3")) .withCommand("-d", "{\"name\": \"spring\"}", "--plaintext", address, "HelloWorld/SayHello") @@ -60,19 +54,4 @@ class SampleGrpcServerNettyShadedApplicationTests { } - static class GrpcServerStartedEventListener implements ApplicationListener { - - private int port; - - @Override - public void onApplicationEvent(GrpcServerStartedEvent event) { - this.port = event.getPort(); - } - - int getPort() { - return this.port; - } - - } - } diff --git a/smoke-test/spring-boot-smoke-test-grpc-server/build.gradle b/smoke-test/spring-boot-smoke-test-grpc-server/build.gradle index 28ab9ef252b..b14151d0f48 100644 --- a/smoke-test/spring-boot-smoke-test-grpc-server/build.gradle +++ b/smoke-test/spring-boot-smoke-test-grpc-server/build.gradle @@ -25,7 +25,7 @@ description = "Spring Boot gRPC server smoke test" dependencies { implementation(project(":starter:spring-boot-starter-grpc-server")) - dockerTestImplementation(project(":starter:spring-boot-starter-test")) + dockerTestImplementation(project(":starter:spring-boot-starter-grpc-server-test")) dockerTestImplementation("org.testcontainers:testcontainers-junit-jupiter") } diff --git a/smoke-test/spring-boot-smoke-test-grpc-server/src/dockerTest/java/smoketest/grpcserver/SampleGrpcServerApplicationTests.java b/smoke-test/spring-boot-smoke-test-grpc-server/src/dockerTest/java/smoketest/grpcserver/SampleGrpcServerApplicationTests.java index ca79ef5cf5d..c36a5fa1aa8 100644 --- a/smoke-test/spring-boot-smoke-test-grpc-server/src/dockerTest/java/smoketest/grpcserver/SampleGrpcServerApplicationTests.java +++ b/smoke-test/spring-boot-smoke-test-grpc-server/src/dockerTest/java/smoketest/grpcserver/SampleGrpcServerApplicationTests.java @@ -21,35 +21,30 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import smoketest.grpcserver.SampleGrpcServerApplicationTests.GrpcServerStartedEventListener; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Import; -import org.springframework.grpc.server.lifecycle.GrpcServerStartedEvent; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for the default Spring gRPC netty server. + * Integration tests for the default Spring gRPC Netty server. * * @author Phillip Webb + * @author Stephane Nicoll */ @SpringBootTest(properties = "spring.grpc.server.port=0") @Testcontainers(disabledWithoutDocker = true) -@Import(GrpcServerStartedEventListener.class) class SampleGrpcServerApplicationTests { - @Autowired - private GrpcServerStartedEventListener startedEventListener; + @Value("${local.grpc.server.port}") + private int port; @Test @SuppressWarnings("resource") void test() { - int port = this.startedEventListener.getPort(); - String address = "host.testcontainers.internal:" + port; - org.testcontainers.Testcontainers.exposeHostPorts(port); + String address = "host.testcontainers.internal:" + this.port; + org.testcontainers.Testcontainers.exposeHostPorts(this.port); try (GenericContainer container = new GenericContainer<>( DockerImageName.parse("fullstorydev/grpcurl:v1.9.3")) .withNetworkAliases("") @@ -61,19 +56,4 @@ class SampleGrpcServerApplicationTests { } - static class GrpcServerStartedEventListener implements ApplicationListener { - - private int port; - - @Override - public void onApplicationEvent(GrpcServerStartedEvent event) { - this.port = event.getPort(); - } - - int getPort() { - return this.port; - } - - } - }