Skip to content

Commit 9cf0227

Browse files
onobcdsyer
authored andcommitted
Add GrpcServiceDiscoverer to prepare for interceptors
This commit re-introduces the service discoverer concept from the gRPC ecosystem. We previously removed it but realized that the abstraction is helpful to provide fully configured service definitions with features like server interceptors. The next commit will take advantage of this commit to add interceptors. See #4 Signed-off-by: Chris Bono <[email protected]>
1 parent 1397632 commit 9cf0227

File tree

5 files changed

+126
-12
lines changed

5 files changed

+126
-12
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2024-2024 The gRPC-Spring Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.grpc.server;
18+
19+
import java.util.List;
20+
21+
import io.grpc.ServerServiceDefinition;
22+
23+
/**
24+
* Discovers {@link ServerServiceDefinition gRPC services} to be provided by the server.
25+
*
26+
* @author Michael ([email protected])
27+
* @author Chris Bono
28+
*/
29+
@FunctionalInterface
30+
public interface GrpcServiceDiscoverer {
31+
32+
/**
33+
* Find gRPC services for the server to provide.
34+
* @return list of services to add to the server - empty when no services available
35+
*/
36+
List<ServerServiceDefinition> findServices();
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2016-2023 The gRPC-Spring Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.grpc.autoconfigure.server;
18+
19+
import java.util.List;
20+
21+
import io.grpc.BindableService;
22+
import io.grpc.ServerServiceDefinition;
23+
24+
import org.springframework.beans.factory.ObjectProvider;
25+
import org.springframework.grpc.server.GrpcServiceDiscoverer;
26+
27+
/**
28+
* The default {@link GrpcServiceDiscoverer} that finds all {@link BindableService} beans
29+
* and configures and binds them.
30+
*
31+
* @author Michael ([email protected])
32+
* @author Chris Bono
33+
*/
34+
class DefaultGrpcServiceDiscoverer implements GrpcServiceDiscoverer {
35+
36+
private final ObjectProvider<BindableService> grpcServicesProvider;
37+
38+
DefaultGrpcServiceDiscoverer(ObjectProvider<BindableService> grpcServicesProvider) {
39+
this.grpcServicesProvider = grpcServicesProvider;
40+
}
41+
42+
@Override
43+
public List<ServerServiceDefinition> findServices() {
44+
return grpcServicesProvider.orderedStream().map(BindableService::bindService).toList();
45+
}
46+
47+
}

spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfiguration.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.context.annotation.Import;
2929
import org.springframework.grpc.autoconfigure.common.codec.GrpcCodecConfiguration;
3030
import org.springframework.grpc.server.GrpcServerFactory;
31+
import org.springframework.grpc.server.GrpcServiceDiscoverer;
3132
import org.springframework.grpc.server.ServerBuilderCustomizer;
3233
import org.springframework.grpc.server.lifecycle.GrpcServerLifecycle;
3334

@@ -69,4 +70,10 @@ ServerBuilderCustomizers serverBuilderCustomizers(ObjectProvider<ServerBuilderCu
6970
return new ServerBuilderCustomizers(customizers.orderedStream().toList());
7071
}
7172

73+
@ConditionalOnMissingBean
74+
@Bean
75+
GrpcServiceDiscoverer grpcServiceDiscoverer(ObjectProvider<BindableService> bindableServicesProvider) {
76+
return new DefaultGrpcServiceDiscoverer(bindableServicesProvider);
77+
}
78+
7279
}

spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/server/GrpcServerFactoryConfigurations.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import javax.net.ssl.KeyManagerFactory;
2222

23-
import org.springframework.beans.factory.ObjectProvider;
2423
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2524
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2625
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -30,11 +29,11 @@
3029
import org.springframework.context.annotation.Bean;
3130
import org.springframework.context.annotation.Configuration;
3231
import org.springframework.grpc.server.GrpcServerFactory;
32+
import org.springframework.grpc.server.GrpcServiceDiscoverer;
3333
import org.springframework.grpc.server.NettyGrpcServerFactory;
3434
import org.springframework.grpc.server.ServerBuilderCustomizer;
3535
import org.springframework.grpc.server.ShadedNettyGrpcServerFactory;
3636

37-
import io.grpc.BindableService;
3837
import io.grpc.CompressorRegistry;
3938
import io.grpc.DecompressorRegistry;
4039
import io.grpc.netty.NettyServerBuilder;
@@ -54,7 +53,7 @@ static class ShadedNettyServerFactoryConfiguration {
5453

5554
@Bean
5655
ShadedNettyGrpcServerFactory shadedNettyGrpcServerFactory(GrpcServerProperties properties,
57-
ObjectProvider<BindableService> grpcServicesProvider, ServerBuilderCustomizers serverBuilderCustomizers,
56+
GrpcServiceDiscoverer grpcServicesDiscoverer, ServerBuilderCustomizers serverBuilderCustomizers,
5857
SslBundles bundles) {
5958
ShadedNettyServerFactoryPropertyMapper mapper = new ShadedNettyServerFactoryPropertyMapper(properties);
6059
List<ServerBuilderCustomizer<io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder>> builderCustomizers = List
@@ -66,7 +65,7 @@ ShadedNettyGrpcServerFactory shadedNettyGrpcServerFactory(GrpcServerProperties p
6665
}
6766
ShadedNettyGrpcServerFactory factory = new ShadedNettyGrpcServerFactory(properties.getAddress(), keyManager,
6867
builderCustomizers);
69-
grpcServicesProvider.orderedStream().map(BindableService::bindService).forEach(factory::addService);
68+
grpcServicesDiscoverer.findServices().forEach(factory::addService);
7069
return factory;
7170
}
7271

@@ -94,7 +93,7 @@ static class NettyServerFactoryConfiguration {
9493

9594
@Bean
9695
NettyGrpcServerFactory nettyGrpcServerFactory(GrpcServerProperties properties,
97-
ObjectProvider<BindableService> grpcServicesProvider, ServerBuilderCustomizers serverBuilderCustomizers,
96+
GrpcServiceDiscoverer grpcServicesDiscoverer, ServerBuilderCustomizers serverBuilderCustomizers,
9897
SslBundles bundles) {
9998
NettyServerFactoryPropertyMapper mapper = new NettyServerFactoryPropertyMapper(properties);
10099
List<ServerBuilderCustomizer<NettyServerBuilder>> builderCustomizers = List
@@ -106,7 +105,7 @@ NettyGrpcServerFactory nettyGrpcServerFactory(GrpcServerProperties properties,
106105
}
107106
NettyGrpcServerFactory factory = new NettyGrpcServerFactory(properties.getAddress(), keyManager,
108107
builderCustomizers);
109-
grpcServicesProvider.orderedStream().map(BindableService::bindService).forEach(factory::addService);
108+
grpcServicesDiscoverer.findServices().forEach(factory::addService);
110109
return factory;
111110
}
112111

spring-grpc-spring-boot-autoconfigure/src/test/java/org/springframework/grpc/autoconfigure/server/GrpcServerAutoConfigurationTests.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.concurrent.TimeUnit;
2929

3030
import org.assertj.core.api.InstanceOfAssertFactories;
31+
import org.junit.jupiter.api.BeforeEach;
3132
import org.junit.jupiter.api.Test;
3233
import org.mockito.InOrder;
3334
import org.mockito.MockedStatic;
@@ -41,6 +42,7 @@
4142
import org.springframework.context.annotation.Configuration;
4243
import org.springframework.core.annotation.Order;
4344
import org.springframework.grpc.server.GrpcServerFactory;
45+
import org.springframework.grpc.server.GrpcServiceDiscoverer;
4446
import org.springframework.grpc.server.NettyGrpcServerFactory;
4547
import org.springframework.grpc.server.ServerBuilderCustomizer;
4648
import org.springframework.grpc.server.ShadedNettyGrpcServerFactory;
@@ -60,10 +62,16 @@
6062
*/
6163
class GrpcServerAutoConfigurationTests {
6264

63-
private ApplicationContextRunner contextRunner() {
64-
BindableService service = mock();
65-
ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-service").build();
65+
private final BindableService service = mock();
66+
67+
private final ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-service").build();
68+
69+
@BeforeEach
70+
void prepareForTest() {
6671
when(service.bindService()).thenReturn(serviceDefinition);
72+
}
73+
74+
private ApplicationContextRunner contextRunner() {
6775
// NOTE: we use noop server lifecycle to avoid startup
6876
return new ApplicationContextRunner()
6977
.withConfiguration(AutoConfigurations.of(GrpcServerAutoConfiguration.class,
@@ -73,9 +81,6 @@ private ApplicationContextRunner contextRunner() {
7381
}
7482

7583
private ApplicationContextRunner contextRunnerWithLifecyle() {
76-
BindableService service = mock();
77-
ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-service").build();
78-
when(service.bindService()).thenReturn(serviceDefinition);
7984
// NOTE: we use noop server lifecycle to avoid startup
8085
return new ApplicationContextRunner()
8186
.withConfiguration(AutoConfigurations.of(GrpcServerAutoConfiguration.class,
@@ -111,6 +116,24 @@ void serverLifecycleAutoConfiguredAsExpected() {
111116
.hasFieldOrPropertyWithValue("factory", context.getBean(GrpcServerFactory.class)));
112117
}
113118

119+
@Test
120+
void whenHasUserDefinedGrpcServiceDiscovererDoesNotAutoConfigureBean() {
121+
GrpcServiceDiscoverer customGrpcServiceDiscoverer = mock(GrpcServiceDiscoverer.class);
122+
this.contextRunnerWithLifecyle()
123+
.withBean("customGrpcServiceDiscoverer", GrpcServiceDiscoverer.class, () -> customGrpcServiceDiscoverer)
124+
.run((context) -> assertThat(context).getBean(GrpcServiceDiscoverer.class)
125+
.isSameAs(customGrpcServiceDiscoverer));
126+
}
127+
128+
@Test
129+
void grpcServiceDiscovererAutoConfiguredAsExpected() {
130+
this.contextRunnerWithLifecyle()
131+
.run((context) -> assertThat(context).getBean(GrpcServiceDiscoverer.class)
132+
.extracting(GrpcServiceDiscoverer::findServices,
133+
InstanceOfAssertFactories.list(ServerServiceDefinition.class))
134+
.containsExactly(this.serviceDefinition));
135+
}
136+
114137
@Test
115138
void whenHasUserDefinedServerBuilderCustomizersDoesNotAutoConfigureBean() {
116139
ServerBuilderCustomizers customCustomizers = mock(ServerBuilderCustomizers.class);

0 commit comments

Comments
 (0)