Skip to content

Commit 9b53805

Browse files
committed
Add support for server interceptor filtering
This commit adds support for filtering the server-side global interceptors. The server factory is now passed down through the service discoverer and configurer to allow the filter to take into account the server factory in use. The service discoverer contract has also been minimized to remove the unused `findService(String)` method. Resolves #208 Signed-off-by: Chris Bono <chris.bono@gmail.com>
1 parent cf15279 commit 9b53805

File tree

15 files changed

+386
-118
lines changed

15 files changed

+386
-118
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2023-2025 the original author or 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+
* https://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+
package org.springframework.grpc.server;
17+
18+
import io.grpc.BindableService;
19+
import io.grpc.ServerInterceptor;
20+
21+
/**
22+
* Strategy to determine whether a global {@link ServerInterceptor} should be included for
23+
* a given {@link BindableService service} and {@link GrpcServerFactory server factory}.
24+
*
25+
* @author Chris Bono
26+
*/
27+
@FunctionalInterface
28+
public interface ServerInterceptorFilter {
29+
30+
/**
31+
* Determine whether the given {@link ServerInterceptor} should be included for the
32+
* provided {@link BindableService service} and {@link GrpcServerFactory server
33+
* factory}.
34+
* @param interceptor the server interceptor under consideration.
35+
* @param serverFactory the server factory in use.
36+
* @param service the service being added.
37+
* @return {@code true} if the interceptor should be included; {@code false}
38+
* otherwise.
39+
*/
40+
boolean filter(ServerInterceptor interceptor, GrpcServerFactory serverFactory, BindableService service);
41+
42+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2024-2025 the original author or 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+
* https://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 io.grpc.Server;
20+
import io.grpc.ServerServiceDefinition;
21+
22+
/**
23+
* Marker interface for {@link GrpcServerFactory} that is to be handled by the servlet
24+
* container.
25+
*
26+
* @author Chris Bono
27+
*/
28+
public class ServletGrpcServerFactory implements GrpcServerFactory {
29+
30+
/**
31+
* Default instance of marker interface.
32+
*/
33+
public static ServletGrpcServerFactory INSTANCE = new ServletGrpcServerFactory();
34+
35+
@Override
36+
public Server createServer() {
37+
throw new UnsupportedOperationException("Marker interface only");
38+
}
39+
40+
@Override
41+
public void addService(ServerServiceDefinition service) {
42+
throw new UnsupportedOperationException("Marker interface only");
43+
}
44+
45+
}

spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceConfigurer.java

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,10 +20,16 @@
2020
import java.util.List;
2121

2222
import org.springframework.beans.factory.InitializingBean;
23+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
24+
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
2325
import org.springframework.context.ApplicationContext;
26+
import org.springframework.core.log.LogAccessor;
2427
import org.springframework.grpc.internal.ApplicationContextBeanLookupUtils;
2528
import org.springframework.grpc.server.GlobalServerInterceptor;
29+
import org.springframework.grpc.server.GrpcServerFactory;
30+
import org.springframework.grpc.server.ServerInterceptorFilter;
2631
import org.springframework.lang.Nullable;
32+
import org.springframework.util.Assert;
2733

2834
import io.grpc.BindableService;
2935
import io.grpc.ServerInterceptor;
@@ -38,37 +44,49 @@
3844
*/
3945
public class DefaultGrpcServiceConfigurer implements GrpcServiceConfigurer, InitializingBean {
4046

47+
private final LogAccessor log = new LogAccessor(getClass());
48+
4149
private final ApplicationContext applicationContext;
4250

4351
private List<ServerInterceptor> globalInterceptors;
4452

53+
private ServerInterceptorFilter interceptorFilter;
54+
4555
public DefaultGrpcServiceConfigurer(ApplicationContext applicationContext) {
4656
this.applicationContext = applicationContext;
4757
}
4858

4959
@Override
5060
public void afterPropertiesSet() {
5161
this.globalInterceptors = findGlobalInterceptors();
62+
this.interceptorFilter = findInterceptorFilter();
5263
}
5364

5465
@Override
55-
public ServerServiceDefinition configure(BindableService bindableService, @Nullable GrpcServiceInfo serviceInfo) {
56-
return bindInterceptors(bindableService, serviceInfo);
66+
public ServerServiceDefinition configure(GrpcServerFactory serverFactory, BindableService bindableService,
67+
@Nullable GrpcServiceInfo serviceInfo) {
68+
Assert.notNull(serverFactory, () -> "serverFactory must not be null");
69+
Assert.notNull(bindableService, () -> "bindableService must not be null");
70+
return bindInterceptors(serverFactory, bindableService, serviceInfo);
5771
}
5872

5973
private List<ServerInterceptor> findGlobalInterceptors() {
6074
return ApplicationContextBeanLookupUtils.getBeansWithAnnotation(this.applicationContext,
6175
ServerInterceptor.class, GlobalServerInterceptor.class);
6276
}
6377

64-
private ServerServiceDefinition bindInterceptors(BindableService bindableService,
78+
private ServerServiceDefinition bindInterceptors(GrpcServerFactory serverFactory, BindableService bindableService,
6579
@Nullable GrpcServiceInfo serviceInfo) {
6680
var serviceDef = bindableService.bindService();
6781
if (serviceInfo == null) {
6882
return ServerInterceptors.interceptForward(serviceDef, this.globalInterceptors);
6983
}
70-
// Add global interceptors first
84+
// Add and filter global interceptors first
7185
List<ServerInterceptor> allInterceptors = new ArrayList<>(this.globalInterceptors);
86+
if (this.interceptorFilter != null) {
87+
allInterceptors
88+
.removeIf(interceptor -> !this.interceptorFilter.filter(interceptor, serverFactory, bindableService));
89+
}
7290
// Add interceptors by type
7391
Arrays.stream(serviceInfo.interceptors())
7492
.forEachOrdered(
@@ -84,4 +102,20 @@ private ServerServiceDefinition bindInterceptors(BindableService bindableService
84102
return ServerInterceptors.interceptForward(serviceDef, allInterceptors);
85103
}
86104

105+
private ServerInterceptorFilter findInterceptorFilter() {
106+
try {
107+
return this.applicationContext.getBean(ServerInterceptorFilter.class);
108+
}
109+
catch (NoUniqueBeanDefinitionException noUniqueBeanEx) {
110+
this.log.warn(noUniqueBeanEx,
111+
() -> "No unique ServerInterceptorFilter bean found. Consider defining a single bean or marking one as @Primary");
112+
return null;
113+
}
114+
catch (NoSuchBeanDefinitionException ignored) {
115+
this.log.debug(
116+
() -> "No ServerInterceptorFilter bean found - filtering will not be applied to server interceptors.");
117+
return null;
118+
}
119+
}
120+
87121
}

spring-grpc-core/src/main/java/org/springframework/grpc/server/service/DefaultGrpcServiceDiscoverer.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,10 +20,13 @@
2020

2121
import org.springframework.context.ApplicationContext;
2222
import org.springframework.grpc.internal.ApplicationContextBeanLookupUtils;
23+
import org.springframework.grpc.server.GrpcServerFactory;
2324
import org.springframework.lang.Nullable;
25+
import org.springframework.util.Assert;
2426

2527
import io.grpc.BindableService;
2628
import io.grpc.ServerServiceDefinition;
29+
import io.grpc.ServiceDescriptor;
2730

2831
/**
2932
* The default {@link GrpcServiceDiscoverer} that finds all {@link BindableService} beans
@@ -44,12 +47,25 @@ public DefaultGrpcServiceDiscoverer(GrpcServiceConfigurer serviceConfigurer,
4447
}
4548

4649
@Override
47-
public List<ServerServiceDefinition> findServices() {
50+
public List<ServerServiceDefinition> findServices(GrpcServerFactory serverFactory) {
51+
Assert.notNull(serverFactory, () -> "serverFactory must not be null");
4852
return ApplicationContextBeanLookupUtils
4953
.getOrderedBeansWithAnnotation(this.applicationContext, BindableService.class, GrpcService.class)
5054
.entrySet()
5155
.stream()
52-
.map((e) -> this.serviceConfigurer.configure(e.getKey(), this.serviceInfo(e.getValue())))
56+
.map((e) -> this.serviceConfigurer.configure(serverFactory, e.getKey(), this.serviceInfo(e.getValue())))
57+
.toList();
58+
}
59+
60+
@Override
61+
public List<String> listServiceNames() {
62+
return ApplicationContextBeanLookupUtils
63+
.getOrderedBeansWithAnnotation(this.applicationContext, BindableService.class, GrpcService.class)
64+
.keySet()
65+
.stream()
66+
.map(BindableService::bindService)
67+
.map(ServerServiceDefinition::getServiceDescriptor)
68+
.map(ServiceDescriptor::getName)
5369
.toList();
5470
}
5571

spring-grpc-core/src/main/java/org/springframework/grpc/server/service/GrpcServiceConfigurer.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.grpc.server.service;
1818

19+
import org.springframework.grpc.server.GrpcServerFactory;
1920
import org.springframework.lang.Nullable;
2021

2122
import io.grpc.BindableService;
@@ -31,10 +32,13 @@ public interface GrpcServiceConfigurer {
3132

3233
/**
3334
* Configure and bind a gRPC service.
35+
* @param serverFactory the factory that provides the server the service will be added
36+
* to
3437
* @param bindableService service to bind and configure
3538
* @param serviceInfo optional additional service information
3639
* @return configured service definition
3740
*/
38-
ServerServiceDefinition configure(BindableService bindableService, @Nullable GrpcServiceInfo serviceInfo);
41+
ServerServiceDefinition configure(GrpcServerFactory serverFactory, BindableService bindableService,
42+
@Nullable GrpcServiceInfo serviceInfo);
3943

4044
}
Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
1818

1919
import java.util.List;
2020

21+
import org.springframework.grpc.server.GrpcServerFactory;
22+
2123
import io.grpc.ServerServiceDefinition;
2224

2325
/**
@@ -26,36 +28,20 @@
2628
* @author Michael (yidongnan@gmail.com)
2729
* @author Chris Bono
2830
*/
29-
@FunctionalInterface
3031
public interface GrpcServiceDiscoverer {
3132

3233
/**
33-
* Find gRPC services for the server to provide.
34+
* Find all gRPC services.
35+
* @param serverFactory the factory that provides the server that the services are to
36+
* be added to
3437
* @return list of services to add to the server - empty when no services available
3538
*/
36-
List<ServerServiceDefinition> findServices();
39+
List<ServerServiceDefinition> findServices(GrpcServerFactory serverFactory);
3740

3841
/**
39-
* Find gRPC service names.
42+
* Find names of all gRPC services.
4043
* @return list of service names - empty when no services available
4144
*/
42-
default List<String> listServiceNames() {
43-
return findServices().stream()
44-
.map(ServerServiceDefinition::getServiceDescriptor)
45-
.map(descriptor -> descriptor.getName())
46-
.toList();
47-
}
48-
49-
/**
50-
* Find gRPC service.
51-
* @param name the service name
52-
* @return a service - null if no service has this name
53-
*/
54-
default ServerServiceDefinition findService(String name) {
55-
return findServices().stream()
56-
.filter(service -> service.getServiceDescriptor().getName().equals(name))
57-
.findFirst()
58-
.orElse(null);
59-
}
45+
List<String> listServiceNames();
6046

6147
}

0 commit comments

Comments
 (0)