From ed2d97f38e374728ab0587720cd93ed71ffa621b Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Mon, 30 Jun 2025 15:38:52 -0500 Subject: [PATCH] Split service discoverer and configurer This commit simplifies the DefaultGrpcServiceDiscoverer by removing its dependence on the DefaultGrpcServiceConfigurer. Now the expectation is the discoverer returns a spec of the service definition rather than a bound service. The configurer then accepts that spec and binds and configures it. See #208 Signed-off-by: Chris Bono --- .../service/DefaultGrpcServiceConfigurer.java | 10 +- .../service/DefaultGrpcServiceDiscoverer.java | 29 +++-- .../server/service/GrpcServiceConfigurer.java | 20 ++-- .../server/service/GrpcServiceDiscoverer.java | 36 ++---- .../service/ServerServiceDefinitionSpec.java | 37 +++++++ .../DefaultGrpcServiceConfigurerTests.java | 5 +- .../DefaultGrpcServiceDiscovererTests.java | 104 ++++++++++-------- .../server/GrpcServerAutoConfiguration.java | 8 +- .../GrpcServerFactoryAutoConfiguration.java | 21 ++-- .../GrpcServerFactoryConfigurations.java | 18 +-- .../GrpcServerAutoConfigurationTests.java | 7 +- ...rpcServerHealthAutoConfigurationTests.java | 2 + .../security/GrpcReactiveRequestTests.java | 5 +- .../security/GrpcServletRequestTests.java | 13 +-- .../test/InProcessTestAutoConfiguration.java | 8 +- 15 files changed, 183 insertions(+), 140 deletions(-) create mode 100644 spring-grpc-core/src/main/java/org/springframework/grpc/server/service/ServerServiceDefinitionSpec.java diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurer.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurer.java index 3a54e807..572aaebc 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurer.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 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. @@ -31,8 +31,8 @@ import io.grpc.ServerServiceDefinition; /** - * Default {@link GrpcServiceConfigurer} that binds and configures services with - * interceptors. + * Default {@link GrpcServiceConfigurer} implementation that binds and configures services + * with interceptors. * * @author Chris Bono */ @@ -52,8 +52,8 @@ public void afterPropertiesSet() { } @Override - public ServerServiceDefinition configure(BindableService bindableService, @Nullable GrpcServiceInfo serviceInfo) { - return bindInterceptors(bindableService, serviceInfo); + public ServerServiceDefinition configure(ServerServiceDefinitionSpec serviceDefinitionSpec) { + return bindInterceptors(serviceDefinitionSpec.service(), serviceDefinitionSpec.serviceInfo()); } private List findGlobalInterceptors() { diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscoverer.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscoverer.java index 494efd0c..6b1d5090 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscoverer.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 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. @@ -24,32 +24,41 @@ import io.grpc.BindableService; import io.grpc.ServerServiceDefinition; +import io.grpc.ServiceDescriptor; /** - * The default {@link GrpcServiceDiscoverer} that finds all {@link BindableService} beans - * and configures and binds them. + * Default {@link GrpcServiceDiscoverer} implementation that finds all + * {@link BindableService} beans in the application context. * * @author Chris Bono */ public class DefaultGrpcServiceDiscoverer implements GrpcServiceDiscoverer { - private final GrpcServiceConfigurer serviceConfigurer; - private final ApplicationContext applicationContext; - public DefaultGrpcServiceDiscoverer(GrpcServiceConfigurer serviceConfigurer, - ApplicationContext applicationContext) { - this.serviceConfigurer = serviceConfigurer; + public DefaultGrpcServiceDiscoverer(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override - public List findServices() { + public List findServices() { return ApplicationContextBeanLookupUtils .getOrderedBeansWithAnnotation(this.applicationContext, BindableService.class, GrpcService.class) .entrySet() .stream() - .map((e) -> this.serviceConfigurer.configure(e.getKey(), this.serviceInfo(e.getValue()))) + .map((e) -> new ServerServiceDefinitionSpec(e.getKey(), this.serviceInfo(e.getValue()))) + .toList(); + } + + @Override + public List listServiceNames() { + return ApplicationContextBeanLookupUtils + .getOrderedBeansWithAnnotation(this.applicationContext, BindableService.class, GrpcService.class) + .keySet() + .stream() + .map(BindableService::bindService) + .map(ServerServiceDefinition::getServiceDescriptor) + .map(ServiceDescriptor::getName) .toList(); } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceConfigurer.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceConfigurer.java index 67721d97..f5391ad8 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceConfigurer.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 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,13 +16,12 @@ package org.springframework.grpc.server.service; -import org.springframework.lang.Nullable; - -import io.grpc.BindableService; import io.grpc.ServerServiceDefinition; /** - * Configures and binds a {@link BindableService gRPC Service}. + * Configures and binds a {@link ServerServiceDefinitionSpec service spec} into a + * {@link ServerServiceDefinition service definition} that can then be added to a gRPC + * server. * * @author Chris Bono */ @@ -30,11 +29,12 @@ public interface GrpcServiceConfigurer { /** - * Configure and bind a gRPC service. - * @param bindableService service to bind and configure - * @param serviceInfo optional additional service information - * @return configured service definition + * Configure and bind a gRPC service spec resulting in a service definition that can + * then be added to a gRPC server. + * @param serviceSpec the spec containing the info about the service + * @return bound and configured service definition that is ready to be added to the + * server */ - ServerServiceDefinition configure(BindableService bindableService, @Nullable GrpcServiceInfo serviceInfo); + ServerServiceDefinition configure(ServerServiceDefinitionSpec serviceSpec); } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceDiscoverer.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceDiscoverer.java index 47bed3b7..1e46591c 100644 --- a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceDiscoverer.java +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceDiscoverer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 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. @@ -18,44 +18,26 @@ import java.util.List; -import io.grpc.ServerServiceDefinition; - /** - * Discovers {@link ServerServiceDefinition gRPC services} to be provided by the server. + * Discovers gRPC services to be provided by the server. * * @author Michael (yidongnan@gmail.com) * @author Chris Bono */ -@FunctionalInterface public interface GrpcServiceDiscoverer { /** - * Find gRPC services for the server to provide. - * @return list of services to add to the server - empty when no services available + * Find the specs of the available gRPC services. The spec can then be passed into a + * {@link GrpcServiceConfigurer service configurer} to bind and configure an actual + * service definition. + * @return list of service specs - empty when no services available */ - List findServices(); + List findServices(); /** - * Find gRPC service names. + * Find the names of the available gRPC services. * @return list of service names - empty when no services available */ - default List listServiceNames() { - return findServices().stream() - .map(ServerServiceDefinition::getServiceDescriptor) - .map(descriptor -> descriptor.getName()) - .toList(); - } - - /** - * Find gRPC service. - * @param name the service name - * @return a service - null if no service has this name - */ - default ServerServiceDefinition findService(String name) { - return findServices().stream() - .filter(service -> service.getServiceDescriptor().getName().equals(name)) - .findFirst() - .orElse(null); - } + List listServiceNames(); } diff --git a/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/ServerServiceDefinitionSpec.java b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/ServerServiceDefinitionSpec.java new file mode 100644 index 00000000..8f250b21 --- /dev/null +++ b/spring-grpc-core/src/main/java/org/springframework/grpc/server/service/ServerServiceDefinitionSpec.java @@ -0,0 +1,37 @@ +/* + * Copyright 2024-2025 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 org.springframework.grpc.server.service; + +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import io.grpc.BindableService; +import io.grpc.ServerServiceDefinition; + +/** + * Encapsulates enough information to construct an actual {@link ServerServiceDefinition}. + * + * @param service the bindable service + * @param serviceInfo optional additional information about the service (e.g. + * interceptors) + * @author Chris Bono + */ +public record ServerServiceDefinitionSpec(BindableService service, @Nullable GrpcServiceInfo serviceInfo) { + public ServerServiceDefinitionSpec { + Assert.notNull(service, "service must not be null"); + } + +} diff --git a/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurerTests.java b/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurerTests.java index 13a4018c..432950aa 100644 --- a/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurerTests.java +++ b/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurerTests.java @@ -108,12 +108,13 @@ private void customizeContextAndRunServiceConfigurerWithServiceInfo( .run((context) -> { DefaultGrpcServiceConfigurer configurer = context.getBean(DefaultGrpcServiceConfigurer.class); if (expectedExceptionType != null) { - assertThatThrownBy(() -> configurer.configure(service, serviceInfo)) + assertThatThrownBy( + () -> configurer.configure(new ServerServiceDefinitionSpec(service, serviceInfo))) .isInstanceOf(expectedExceptionType); serverInterceptorsMocked.verifyNoInteractions(); } else { - configurer.configure(service, serviceInfo); + configurer.configure(new ServerServiceDefinitionSpec(service, serviceInfo)); serverInterceptorsMocked .verify(() -> ServerInterceptors.interceptForward(serviceDef, expectedInterceptors)); } diff --git a/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscovererTests.java b/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscovererTests.java index 9b43f1f1..9cbb6887 100644 --- a/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscovererTests.java +++ b/spring-grpc-core/src/test/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscovererTests.java @@ -17,11 +17,10 @@ package org.springframework.grpc.server.service; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.grpc.server.service.DefaultGrpcServiceDiscovererTests.DefaultGrpcServiceDiscovererTestsConfig.SERVICE_A; -import static org.springframework.grpc.server.service.DefaultGrpcServiceDiscovererTests.DefaultGrpcServiceDiscovererTestsConfig.SERVICE_B; - -import java.util.LinkedHashMap; -import java.util.Map; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.grpc.server.service.DefaultGrpcServiceDiscovererTests.DefaultGrpcServiceDiscovererTestsServiceConfig.SERVICE_A; +import static org.springframework.grpc.server.service.DefaultGrpcServiceDiscovererTests.DefaultGrpcServiceDiscovererTestsServiceConfig.SERVICE_B; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; @@ -35,6 +34,7 @@ import io.grpc.BindableService; import io.grpc.ServerServiceDefinition; +import io.grpc.ServiceDescriptor; /** * Tests for {@link DefaultGrpcServiceDiscoverer}. @@ -43,28 +43,55 @@ */ class DefaultGrpcServiceDiscovererTests { + @Test + void whenNoServicesRegisteredThenListServiceNamesReturnsEmptyList() { + new ApplicationContextRunner().withUserConfiguration(DefaultGrpcServiceDiscovererTestsBaseConfig.class) + .run((context) -> assertThat(context).getBean(DefaultGrpcServiceDiscoverer.class) + .extracting(DefaultGrpcServiceDiscoverer::listServiceNames, InstanceOfAssertFactories.LIST) + .isEmpty()); + } + + @Test + void whenServicesRegisteredThenListServiceNamesReturnsNames() { + new ApplicationContextRunner() + .withUserConfiguration(DefaultGrpcServiceDiscovererTestsBaseConfig.class, + DefaultGrpcServiceDiscovererTestsServiceConfig.class) + .run((context) -> assertThat(context).getBean(DefaultGrpcServiceDiscoverer.class) + .extracting(DefaultGrpcServiceDiscoverer::listServiceNames, InstanceOfAssertFactories.LIST) + .containsExactly("serviceB", "serviceA")); + + } + @Test void servicesAreFoundInProperOrderWithExpectedGrpcServiceAnnotations() { - new ApplicationContextRunner().withUserConfiguration(DefaultGrpcServiceDiscovererTestsConfig.class) - .run((context) -> { - assertThat(context).getBean(DefaultGrpcServiceDiscoverer.class) - .extracting(DefaultGrpcServiceDiscoverer::findServices, InstanceOfAssertFactories.LIST) - .containsExactly(DefaultGrpcServiceDiscovererTestsConfig.SERVICE_DEF_B, - DefaultGrpcServiceDiscovererTestsConfig.SERVICE_DEF_A); - TestServiceConfigurer configurer = context.getBean(TestServiceConfigurer.class); - assertThat(configurer.invocations).hasSize(2); - assertThat(configurer.invocations.keySet()).containsExactly(SERVICE_B, SERVICE_A); - assertThat(configurer.invocations).containsEntry(SERVICE_B, null); - assertThat(configurer.invocations).hasEntrySatisfying(SERVICE_A, (serviceInfo) -> { - assertThat(serviceInfo.interceptors()).isEmpty(); - assertThat(serviceInfo.interceptorNames()).isEmpty(); - assertThat(serviceInfo.blendWithGlobalInterceptors()).isFalse(); - }); - }); + new ApplicationContextRunner() + .withUserConfiguration(DefaultGrpcServiceDiscovererTestsBaseConfig.class, + DefaultGrpcServiceDiscovererTestsServiceConfig.class) + .run((context) -> assertThat(context).getBean(DefaultGrpcServiceDiscoverer.class) + .extracting(DefaultGrpcServiceDiscoverer::findServices, + InstanceOfAssertFactories.list(ServerServiceDefinitionSpec.class)) + .satisfies((serviceSpecs) -> { + assertThat(serviceSpecs).hasSize(2); + assertThat(serviceSpecs).element(0).isEqualTo(new ServerServiceDefinitionSpec(SERVICE_B, null)); + assertThat(serviceSpecs).element(1).satisfies((spec) -> { + assertThat(spec.service()).isEqualTo(SERVICE_A); + assertThat(spec.serviceInfo()).isNotNull(); + }); + })); } @Configuration(proxyBeanMethods = false) - static class DefaultGrpcServiceDiscovererTestsConfig { + static class DefaultGrpcServiceDiscovererTestsBaseConfig { + + @Bean + GrpcServiceDiscoverer grpcServiceDiscoverer(ApplicationContext applicationContext) { + return new DefaultGrpcServiceDiscoverer(applicationContext); + } + + } + + @Configuration(proxyBeanMethods = false) + static class DefaultGrpcServiceDiscovererTestsServiceConfig { static BindableService SERVICE_A = Mockito.mock(); @@ -74,44 +101,27 @@ static class DefaultGrpcServiceDiscovererTestsConfig { static ServerServiceDefinition SERVICE_DEF_B = Mockito.mock(); - @Bean - TestServiceConfigurer testServiceConfigurer() { - return new TestServiceConfigurer(); - } - - @Bean - GrpcServiceDiscoverer grpcServiceDiscoverer(GrpcServiceConfigurer grpcServiceConfigurer, - ApplicationContext applicationContext) { - return new DefaultGrpcServiceDiscoverer(grpcServiceConfigurer, applicationContext); - } - @GrpcService @Bean @Order(200) BindableService serviceA() { - Mockito.when(SERVICE_A.bindService()).thenReturn(SERVICE_DEF_A); + ServiceDescriptor descriptor = mock(); + when(descriptor.getName()).thenReturn("serviceA"); + when(SERVICE_DEF_A.getServiceDescriptor()).thenReturn(descriptor); + when(SERVICE_A.bindService()).thenReturn(SERVICE_DEF_A); return SERVICE_A; } @Bean @Order(100) BindableService serviceB() { - Mockito.when(SERVICE_B.bindService()).thenReturn(SERVICE_DEF_B); + ServiceDescriptor descriptor = mock(); + when(descriptor.getName()).thenReturn("serviceB"); + when(SERVICE_DEF_B.getServiceDescriptor()).thenReturn(descriptor); + when(SERVICE_B.bindService()).thenReturn(SERVICE_DEF_B); return SERVICE_B; } } - static class TestServiceConfigurer implements GrpcServiceConfigurer { - - Map invocations = new LinkedHashMap<>(); - - @Override - public ServerServiceDefinition configure(BindableService bindableService, GrpcServiceInfo serviceInfo) { - invocations.put(bindableService, serviceInfo); - return bindableService.bindService(); - } - - } - } diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java index 2f18114a..53c3d9bd 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java @@ -23,15 +23,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.grpc.autoconfigure.common.codec.GrpcCodecConfiguration; -import org.springframework.grpc.server.GrpcServerFactory; import org.springframework.grpc.server.ServerBuilderCustomizer; import org.springframework.grpc.server.exception.ReactiveStubBeanDefinitionRegistrar; -import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; import org.springframework.grpc.server.service.DefaultGrpcServiceConfigurer; import org.springframework.grpc.server.service.DefaultGrpcServiceDiscoverer; import org.springframework.grpc.server.service.GrpcServiceConfigurer; @@ -78,9 +75,8 @@ DefaultGrpcServiceConfigurer grpcServiceConfigurer(ApplicationContext applicatio @ConditionalOnMissingBean(GrpcServiceDiscoverer.class) @Bean - DefaultGrpcServiceDiscoverer grpcServiceDiscoverer(GrpcServiceConfigurer grpcServiceConfigurer, - ApplicationContext applicationContext) { - return new DefaultGrpcServiceDiscoverer(grpcServiceConfigurer, applicationContext); + DefaultGrpcServiceDiscoverer grpcServiceDiscoverer(ApplicationContext applicationContext) { + return new DefaultGrpcServiceDiscoverer(applicationContext); } @ConditionalOnBean(CompressorRegistry.class) diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java index a3a84c09..d228b7fb 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2024-2024 the original author or authors. + * Copyright 2024-2025 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. @@ -38,6 +38,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.Ordered; +import org.springframework.grpc.server.service.GrpcServiceConfigurer; import org.springframework.grpc.server.service.GrpcServiceDiscoverer; import org.springframework.util.unit.DataSize; @@ -83,16 +84,20 @@ public static class GrpcServletConfiguration { @Bean public ServletRegistrationBean grpcServlet(GrpcServerProperties properties, - GrpcServiceDiscoverer discoverer, ServerBuilderCustomizers serverBuilderCustomizers) { - List paths = discoverer.listServiceNames() - .stream() - .map(service -> "/" + service + "/*") - .collect(Collectors.toList()); + GrpcServiceDiscoverer serviceDiscoverer, GrpcServiceConfigurer serviceConfigurer, + ServerBuilderCustomizers serverBuilderCustomizers) { + List serviceNames = serviceDiscoverer.listServiceNames(); if (logger.isInfoEnabled()) { - discoverer.listServiceNames().forEach(service -> logger.info("Registering gRPC service: " + service)); + serviceNames.forEach(service -> logger.info("Registering gRPC service: " + service)); } + List paths = serviceNames.stream() + .map(service -> "/" + service + "/*") + .collect(Collectors.toList()); ServletServerBuilder servletServerBuilder = new ServletServerBuilder(); - discoverer.findServices().forEach(servletServerBuilder::addService); + serviceDiscoverer.findServices() + .stream() + .map(serviceConfigurer::configure) + .forEach(servletServerBuilder::addService); PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); mapper.from(properties.getMaxInboundMessageSize()) .asInt(DataSize::toBytes) diff --git a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryConfigurations.java b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryConfigurations.java index 97897434..edebcad5 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryConfigurations.java +++ b/spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryConfigurations.java @@ -37,6 +37,7 @@ import org.springframework.grpc.server.ServerBuilderCustomizer; import org.springframework.grpc.server.ShadedNettyGrpcServerFactory; import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; +import org.springframework.grpc.server.service.GrpcServiceConfigurer; import org.springframework.grpc.server.service.GrpcServiceDiscoverer; import io.grpc.inprocess.InProcessServerBuilder; @@ -60,8 +61,8 @@ static class ShadedNettyServerFactoryConfiguration { @Bean ShadedNettyGrpcServerFactory shadedNettyGrpcServerFactory(GrpcServerProperties properties, - GrpcServiceDiscoverer grpcServicesDiscoverer, ServerBuilderCustomizers serverBuilderCustomizers, - SslBundles bundles) { + GrpcServiceDiscoverer serviceDiscoverer, GrpcServiceConfigurer serviceConfigurer, + ServerBuilderCustomizers serverBuilderCustomizers, SslBundles bundles) { ShadedNettyServerFactoryPropertyMapper mapper = new ShadedNettyServerFactoryPropertyMapper(properties); List> builderCustomizers = List .of(mapper::customizeServerBuilder, serverBuilderCustomizers::customize); @@ -75,7 +76,7 @@ ShadedNettyGrpcServerFactory shadedNettyGrpcServerFactory(GrpcServerProperties p } ShadedNettyGrpcServerFactory factory = new ShadedNettyGrpcServerFactory(properties.getAddress(), builderCustomizers, keyManager, trustManager, properties.getSsl().getClientAuth()); - grpcServicesDiscoverer.findServices().forEach(factory::addService); + serviceDiscoverer.findServices().stream().map(serviceConfigurer::configure).forEach(factory::addService); return factory; } @@ -99,8 +100,8 @@ static class NettyServerFactoryConfiguration { @Bean NettyGrpcServerFactory nettyGrpcServerFactory(GrpcServerProperties properties, - GrpcServiceDiscoverer grpcServicesDiscoverer, ServerBuilderCustomizers serverBuilderCustomizers, - SslBundles bundles) { + GrpcServiceDiscoverer serviceDiscoverer, GrpcServiceConfigurer serviceConfigurer, + ServerBuilderCustomizers serverBuilderCustomizers, SslBundles bundles) { NettyServerFactoryPropertyMapper mapper = new NettyServerFactoryPropertyMapper(properties); List> builderCustomizers = List .of(mapper::customizeServerBuilder, serverBuilderCustomizers::customize); @@ -114,7 +115,7 @@ NettyGrpcServerFactory nettyGrpcServerFactory(GrpcServerProperties properties, } NettyGrpcServerFactory factory = new NettyGrpcServerFactory(properties.getAddress(), builderCustomizers, keyManager, trustManager, properties.getSsl().getClientAuth()); - grpcServicesDiscoverer.findServices().forEach(factory::addService); + serviceDiscoverer.findServices().stream().map(serviceConfigurer::configure).forEach(factory::addService); return factory; } @@ -137,13 +138,14 @@ static class InProcessServerFactoryConfiguration { @Bean InProcessGrpcServerFactory inProcessGrpcServerFactory(GrpcServerProperties properties, - GrpcServiceDiscoverer grpcServicesDiscoverer, ServerBuilderCustomizers serverBuilderCustomizers) { + GrpcServiceDiscoverer serviceDiscoverer, GrpcServiceConfigurer serviceConfigurer, + ServerBuilderCustomizers serverBuilderCustomizers) { var mapper = new InProcessServerFactoryPropertyMapper(properties); List> builderCustomizers = List .of(mapper::customizeServerBuilder, serverBuilderCustomizers::customize); InProcessGrpcServerFactory factory = new InProcessGrpcServerFactory(properties.getInprocess().getName(), builderCustomizers); - grpcServicesDiscoverer.findServices().forEach(factory::addService); + serviceDiscoverer.findServices().stream().map(serviceConfigurer::configure).forEach(factory::addService); return factory; } diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java index 4a687bb6..7249654c 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 the original author or authors. + * Copyright 2023-2025 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. @@ -51,6 +51,7 @@ import org.springframework.grpc.server.ShadedNettyGrpcServerFactory; import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; import org.springframework.grpc.server.service.DefaultGrpcServiceConfigurer; +import org.springframework.grpc.server.service.DefaultGrpcServiceDiscoverer; import org.springframework.grpc.server.service.GrpcServiceConfigurer; import org.springframework.grpc.server.service.GrpcServiceDiscoverer; @@ -148,9 +149,7 @@ void grpcServiceDiscovererAutoConfiguredAsExpected() { this.contextRunnerWithLifecyle() .withPropertyValues("spring.grpc.server.port=0") .run((context) -> assertThat(context).getBean(GrpcServiceDiscoverer.class) - .extracting(GrpcServiceDiscoverer::findServices, - InstanceOfAssertFactories.list(ServerServiceDefinition.class)) - .containsExactly(this.serviceDefinition)); + .isInstanceOf(DefaultGrpcServiceDiscoverer.class)); } @Test diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java index 91244fa8..ab7d9877 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/health/GrpcServerHealthAutoConfigurationTests.java @@ -39,6 +39,7 @@ import org.springframework.grpc.autoconfigure.server.ServerBuilderCustomizers; import org.springframework.grpc.autoconfigure.server.health.GrpcServerHealthAutoConfiguration.ActuatorHealthAdapterConfiguration; import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; +import org.springframework.grpc.server.service.GrpcServiceConfigurer; import org.springframework.grpc.server.service.GrpcServiceDiscoverer; import org.springframework.util.StringUtils; @@ -130,6 +131,7 @@ void healthIsAutoConfiguredBeforeGrpcServerFactory() { .withBean("noopServerLifecycle", GrpcServerLifecycle.class, Mockito::mock) .withBean("serverBuilderCustomizers", ServerBuilderCustomizers.class, Mockito::mock) .withBean("grpcServicesDiscoverer", GrpcServiceDiscoverer.class, Mockito::mock) + .withBean("grpcServiceConfigurer", GrpcServiceConfigurer.class, Mockito::mock) .withBean("sslBundles", SslBundles.class, Mockito::mock) .withPropertyValues("spring.grpc.server.port=0") .run((context) -> assertThatBeanDefinitionsContainInOrder(context, GrpcServerHealthAutoConfiguration.class, diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcReactiveRequestTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcReactiveRequestTests.java index 56fa3b24..9337aca2 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcReactiveRequestTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcReactiveRequestTests.java @@ -46,8 +46,7 @@ public void setup() { ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-service").build(); when(service.bindService()).thenReturn(serviceDefinition); this.context.registerBean(BindableService.class, () -> service); - this.context.registerBean(GrpcServiceDiscoverer.class, - () -> new DefaultGrpcServiceDiscoverer((input, info) -> input.bindService(), context)); + this.context.registerBean(GrpcServiceDiscoverer.class, () -> new DefaultGrpcServiceDiscoverer(context)); } @Test @@ -63,7 +62,7 @@ private MockExchange mockRequest(String path) { return request; } - static interface MockService extends BindableService { + interface MockService extends BindableService { } diff --git a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcServletRequestTests.java b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcServletRequestTests.java index 375932f8..750cc286 100644 --- a/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcServletRequestTests.java +++ b/spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/security/GrpcServletRequestTests.java @@ -42,8 +42,7 @@ public void setup() { ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-service").build(); when(service.bindService()).thenReturn(serviceDefinition); this.context.registerBean(BindableService.class, () -> service); - this.context.registerBean(GrpcServiceDiscoverer.class, - () -> new DefaultGrpcServiceDiscoverer((input, info) -> input.bindService(), context)); + this.context.registerBean(GrpcServiceDiscoverer.class, () -> new DefaultGrpcServiceDiscoverer(context)); } @Test @@ -51,28 +50,28 @@ void requestMatches() { GrpcServletRequestMatcher matcher = GrpcServletRequest.all(); MockHttpServletRequest request = mockRequest("/my-service/Method"); assertThat(matcher.matches(request)).isTrue(); - }; + } @Test void noMatch() { GrpcServletRequestMatcher matcher = GrpcServletRequest.all(); MockHttpServletRequest request = mockRequest("/other-service/Method"); assertThat(matcher.matches(request)).isFalse(); - }; + } @Test void requestMatcherExcludes() { GrpcServletRequestMatcher matcher = GrpcServletRequest.all().excluding("my-service"); MockHttpServletRequest request = mockRequest("/my-service/Method"); assertThat(matcher.matches(request)).isFalse(); - }; + } @Test void noServices() { GrpcServletRequestMatcher matcher = GrpcServletRequest.all(); MockHttpServletRequest request = mockRequestNoServices("/my-service/Method"); assertThat(matcher.matches(request)).isFalse(); - }; + } private MockHttpServletRequest mockRequestNoServices(String path) { MockServletContext servletContext = new MockServletContext(); @@ -91,7 +90,7 @@ private MockHttpServletRequest mockRequest(String path) { return request; } - static interface MockService extends BindableService { + interface MockService extends BindableService { } diff --git a/spring-grpc-test/src/main/java/org/springframework/grpc/test/InProcessTestAutoConfiguration.java b/spring-grpc-test/src/main/java/org/springframework/grpc/test/InProcessTestAutoConfiguration.java index 7ff3209a..359dd796 100644 --- a/spring-grpc-test/src/main/java/org/springframework/grpc/test/InProcessTestAutoConfiguration.java +++ b/spring-grpc-test/src/main/java/org/springframework/grpc/test/InProcessTestAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2024-2024 the original author or authors. + * Copyright 2024-2025 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. @@ -35,6 +35,7 @@ import org.springframework.grpc.server.InProcessGrpcServerFactory; import org.springframework.grpc.server.ServerBuilderCustomizer; import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle; +import org.springframework.grpc.server.service.GrpcServiceConfigurer; import org.springframework.grpc.server.service.GrpcServiceDiscoverer; import io.grpc.BindableService; @@ -53,10 +54,11 @@ public class InProcessTestAutoConfiguration { @Bean @ConditionalOnBean(BindableService.class) @Order(Ordered.HIGHEST_PRECEDENCE) - TestInProcessGrpcServerFactory testInProcessGrpcServerFactory(GrpcServiceDiscoverer grpcServicesDiscoverer, + TestInProcessGrpcServerFactory testInProcessGrpcServerFactory(GrpcServiceDiscoverer serviceDiscoverer, + GrpcServiceConfigurer serviceConfigurer, List> customizers) { var factory = new TestInProcessGrpcServerFactory(address, customizers); - grpcServicesDiscoverer.findServices().forEach(factory::addService); + serviceDiscoverer.findServices().stream().map(serviceConfigurer::configure).forEach(factory::addService); return factory; }