Skip to content

Commit c430b6d

Browse files
committed
Throw exception if service is registered twice (Implements #365)
1 parent 1043e47 commit c430b6d

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/serverfactory/AbstractGrpcServerFactory.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,22 @@
1919

2020
import static java.util.Objects.requireNonNull;
2121

22+
import java.util.LinkedHashSet;
2223
import java.util.List;
24+
import java.util.Set;
2325

2426
import org.springframework.beans.factory.annotation.Autowired;
2527
import org.springframework.util.unit.DataSize;
2628

29+
import com.google.common.annotations.VisibleForTesting;
2730
import com.google.common.collect.Lists;
2831

2932
import io.grpc.Server;
3033
import io.grpc.ServerBuilder;
3134
import io.grpc.health.v1.HealthCheckResponse;
35+
import io.grpc.health.v1.HealthGrpc;
3236
import io.grpc.protobuf.services.ProtoReflectionService;
37+
import io.grpc.reflection.v1alpha.ServerReflectionGrpc;
3338
import io.grpc.services.HealthStatusManager;
3439
import lombok.extern.slf4j.Slf4j;
3540
import net.devh.boot.grpc.server.config.GrpcServerProperties;
@@ -52,7 +57,8 @@ public abstract class AbstractGrpcServerFactory<T extends ServerBuilder<T>> impl
5257
protected final List<GrpcServerConfigurer> serverConfigurers;
5358

5459
@Autowired
55-
private HealthStatusManager healthStatusManager;
60+
@VisibleForTesting
61+
HealthStatusManager healthStatusManager;
5662

5763
/**
5864
* Creates a new server factory with the given properties.
@@ -102,16 +108,24 @@ protected void configure(final T builder) {
102108
* @param builder The server builder to configure.
103109
*/
104110
protected void configureServices(final T builder) {
111+
final Set<String> serviceNames = new LinkedHashSet<>();
112+
105113
// support health check
106114
if (this.properties.isHealthServiceEnabled()) {
107115
builder.addService(this.healthStatusManager.getHealthService());
116+
serviceNames.add(HealthGrpc.getServiceDescriptor().getName());
108117
}
118+
// gRPC reflection
109119
if (this.properties.isReflectionServiceEnabled()) {
110120
builder.addService(ProtoReflectionService.newInstance());
121+
serviceNames.add(ServerReflectionGrpc.getServiceDescriptor().getName());
111122
}
112123

113124
for (final GrpcServiceDefinition service : this.serviceList) {
114125
final String serviceName = service.getDefinition().getServiceDescriptor().getName();
126+
if (!serviceNames.add(serviceName)) {
127+
throw new IllegalStateException("Found duplicate service implementation: " + serviceName);
128+
}
115129
log.info("Registered gRPC service: " + serviceName + ", bean: " + service.getBeanName() + ", class: "
116130
+ service.getBeanClazz().getName());
117131
builder.addService(service.getDefinition());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2016-2020 Michael Zhang <[email protected]>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10+
* Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16+
*/
17+
18+
package net.devh.boot.grpc.server.serverfactory;
19+
20+
import static java.util.Collections.emptyList;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import io.grpc.ServerBuilder;
27+
import io.grpc.netty.NettyServerBuilder;
28+
import io.grpc.protobuf.services.ProtoReflectionService;
29+
import io.grpc.reflection.v1alpha.ServerReflectionGrpc;
30+
import io.grpc.services.HealthStatusManager;
31+
import net.devh.boot.grpc.server.config.GrpcServerProperties;
32+
import net.devh.boot.grpc.server.service.GrpcServiceDefinition;
33+
34+
/**
35+
* Tests for {@link AbstractGrpcServerFactory}.
36+
*/
37+
class AbstractGrpcServerFactoryTest {
38+
39+
/**
40+
* Tests {@link AbstractGrpcServerFactory#configureServices(ServerBuilder)}.
41+
*/
42+
@Test
43+
void testConfigureServices() {
44+
final GrpcServerProperties properties = new GrpcServerProperties();
45+
properties.setReflectionServiceEnabled(false);
46+
47+
final NettyGrpcServerFactory serverFactory = new NettyGrpcServerFactory(properties, emptyList());
48+
serverFactory.healthStatusManager = new HealthStatusManager();
49+
50+
serverFactory.addService(new GrpcServiceDefinition("test1", ProtoReflectionService.class,
51+
ProtoReflectionService.newInstance().bindService()));
52+
serverFactory.addService(new GrpcServiceDefinition("test2", ProtoReflectionService.class,
53+
ProtoReflectionService.newInstance().bindService()));
54+
55+
final NettyServerBuilder serverBuilder = serverFactory.newServerBuilder();
56+
57+
assertEquals("Found duplicate service implementation: " + ServerReflectionGrpc.SERVICE_NAME,
58+
assertThrows(IllegalStateException.class, () -> serverFactory.configureServices(serverBuilder))
59+
.getMessage());
60+
}
61+
62+
}

0 commit comments

Comments
 (0)