Skip to content

Commit 192085c

Browse files
ch200203dsyer
authored andcommitted
feat: Make default stub factory configurable via properties (#250)
Signed-off-by: cheolhwan.ihn <ch200203@gmail.com>
1 parent 4ae8b93 commit 192085c

File tree

3 files changed

+195
-3
lines changed

3 files changed

+195
-3
lines changed

spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/ClientScanConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.grpc.autoconfigure.client.ClientScanConfiguration.DefaultGrpcClientRegistrations;
3333
import org.springframework.grpc.autoconfigure.client.GrpcClientProperties.ChannelConfig;
3434
import org.springframework.grpc.client.AbstractGrpcClientRegistrar;
35-
import org.springframework.grpc.client.BlockingStubFactory;
3635
import org.springframework.grpc.client.GrpcClientFactory;
3736
import org.springframework.grpc.client.GrpcClientFactory.GrpcClientRegistrationSpec;
3837

@@ -68,9 +67,11 @@ protected GrpcClientRegistrationSpec[] collect(AnnotationMetadata meta) {
6867
if (AutoConfigurationPackages.has(this.beanFactory)) {
6968
packages.addAll(AutoConfigurationPackages.get(this.beanFactory));
7069
}
71-
// TODO: change global default factory type in properties maybe?
70+
GrpcClientProperties props = binder.bind("spring.grpc.client", GrpcClientProperties.class)
71+
.orElseGet(GrpcClientProperties::new);
72+
7273
return new GrpcClientRegistrationSpec[] { GrpcClientRegistrationSpec.of("default")
73-
.factory(BlockingStubFactory.class)
74+
.factory(props.getDefaultStubFactory())
7475
.packages(packages.toArray(new String[0])) };
7576
}
7677
return new GrpcClientRegistrationSpec[0];

spring-grpc-spring-boot-autoconfigure/src/main/java/org/springframework/grpc/autoconfigure/client/GrpcClientProperties.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import org.springframework.context.EnvironmentAware;
2727
import org.springframework.core.env.Environment;
2828
import org.springframework.core.env.StandardEnvironment;
29+
import org.springframework.grpc.client.BlockingStubFactory;
2930
import org.springframework.grpc.client.NegotiationType;
31+
import org.springframework.grpc.client.StubFactory;
3032
import org.springframework.grpc.client.VirtualTargets;
3133
import org.springframework.util.unit.DataSize;
3234

@@ -45,6 +47,11 @@ public class GrpcClientProperties implements EnvironmentAware, VirtualTargets {
4547
*/
4648
private final Map<String, ChannelConfig> channels = new HashMap<>();
4749

50+
/**
51+
* Default stub factory to use for all channels.
52+
*/
53+
private Class<? extends StubFactory<?>> defaultStubFactory = BlockingStubFactory.class;
54+
4855
private Environment environment;
4956

5057
GrpcClientProperties() {
@@ -60,6 +67,14 @@ public Map<String, ChannelConfig> getChannels() {
6067
return this.channels;
6168
}
6269

70+
public Class<? extends StubFactory<?>> getDefaultStubFactory() {
71+
return this.defaultStubFactory;
72+
}
73+
74+
public void setDefaultStubFactory(Class<? extends StubFactory<?>> defaultStubFactory) {
75+
this.defaultStubFactory = defaultStubFactory;
76+
}
77+
6378
@Override
6479
public void setEnvironment(Environment environment) {
6580
this.environment = environment;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright 2025-present 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.autoconfigure.client;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
21+
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
import org.junit.jupiter.api.Nested;
26+
import org.junit.jupiter.api.Test;
27+
28+
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
30+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
31+
import org.springframework.boot.context.properties.bind.BindException;
32+
import org.springframework.boot.context.properties.bind.Binder;
33+
import org.springframework.boot.test.context.SpringBootTest;
34+
import org.springframework.boot.test.context.TestConfiguration;
35+
import org.springframework.context.ApplicationContext;
36+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
37+
import org.springframework.context.annotation.Configuration;
38+
import org.springframework.core.env.Environment;
39+
import org.springframework.core.env.MapPropertySource;
40+
import org.springframework.grpc.client.BlockingStubFactory;
41+
import org.springframework.grpc.client.CoroutineStubFactory;
42+
import org.springframework.grpc.client.GrpcClientFactory.GrpcClientRegistrationSpec;
43+
import org.springframework.grpc.client.ReactorStubFactory;
44+
import org.springframework.mock.env.MockEnvironment;
45+
46+
/**
47+
* Tests for the {@link ClientScanConfiguration}.
48+
*
49+
* @author CheolHwan Ihn
50+
*/
51+
class ClientScanConfigurationTests {
52+
53+
@Test
54+
void testReactorStubFactory() {
55+
Map<String, Object> properties = new HashMap<>();
56+
properties.put("spring.grpc.client.default-stub-factory", ReactorStubFactory.class.getName());
57+
properties.put("spring.grpc.client.default-channel.address", "static://localhost:9090");
58+
59+
MockEnvironment environment = new MockEnvironment();
60+
environment.getPropertySources().addFirst(new MapPropertySource("test", properties));
61+
62+
ClientScanConfiguration.DefaultGrpcClientRegistrations registrations = new ClientScanConfiguration.DefaultGrpcClientRegistrations();
63+
registrations.setEnvironment(environment);
64+
65+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
66+
context.setEnvironment(environment);
67+
AutoConfigurationPackages.register(context, "org.springframework.grpc.autoconfigure.client");
68+
registrations.setBeanFactory(context.getBeanFactory());
69+
70+
GrpcClientRegistrationSpec[] specs = registrations.collect(null);
71+
72+
assertThat(specs).hasSize(1);
73+
assertThat(specs[0].factory()).isEqualTo(ReactorStubFactory.class);
74+
}
75+
76+
@Test
77+
void testDefaultStubFactory() {
78+
Map<String, Object> properties = new HashMap<>();
79+
properties.put("spring.grpc.client.default-channel.address", "static://localhost:9090");
80+
81+
MockEnvironment environment = new MockEnvironment();
82+
environment.getPropertySources().addFirst(new MapPropertySource("test", properties));
83+
84+
ClientScanConfiguration.DefaultGrpcClientRegistrations registrations = new ClientScanConfiguration.DefaultGrpcClientRegistrations();
85+
registrations.setEnvironment(environment);
86+
87+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
88+
context.setEnvironment(environment);
89+
AutoConfigurationPackages.register(context, "org.springframework.grpc.autoconfigure.client");
90+
registrations.setBeanFactory(context.getBeanFactory());
91+
92+
GrpcClientRegistrationSpec[] specs = registrations.collect(null);
93+
94+
assertThat(specs).hasSize(1);
95+
assertThat(specs[0].factory()).isEqualTo(BlockingStubFactory.class);
96+
}
97+
98+
@Test
99+
void testCoroutineStubFactory() {
100+
Map<String, Object> properties = new HashMap<>();
101+
properties.put("spring.grpc.client.default-stub-factory", CoroutineStubFactory.class.getName());
102+
properties.put("spring.grpc.client.default-channel.address", "static://localhost:9090");
103+
104+
MockEnvironment env = new MockEnvironment();
105+
env.getPropertySources().addFirst(new MapPropertySource("test", properties));
106+
107+
var regs = new ClientScanConfiguration.DefaultGrpcClientRegistrations();
108+
regs.setEnvironment(env);
109+
110+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
111+
context.setEnvironment(env);
112+
AutoConfigurationPackages.register(context, "org.springframework.grpc.autoconfigure.client");
113+
regs.setBeanFactory(context.getBeanFactory());
114+
115+
GrpcClientRegistrationSpec[] specs = regs.collect(null);
116+
assertThat(specs).hasSize(1);
117+
assertThat(specs[0].factory()).isEqualTo(CoroutineStubFactory.class);
118+
}
119+
}
120+
121+
@Test
122+
void testInvalidStubFactoryValueThrowsBindException() {
123+
Map<String, Object> properties = new HashMap<>();
124+
properties.put("spring.grpc.client.default-stub-factory", "com.example.InvalidStubFactory");
125+
properties.put("spring.grpc.client.default-channel.address", "static://localhost:9090");
126+
127+
MockEnvironment env = new MockEnvironment();
128+
env.getPropertySources().addFirst(new MapPropertySource("test", properties));
129+
130+
var regs = new ClientScanConfiguration.DefaultGrpcClientRegistrations();
131+
regs.setEnvironment(env);
132+
133+
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
134+
context.setEnvironment(env);
135+
AutoConfigurationPackages.register(context, "org.springframework.grpc.autoconfigure.client");
136+
regs.setBeanFactory(context.getBeanFactory());
137+
138+
assertThatThrownBy(() -> regs.collect(null)).isInstanceOf(BindException.class)
139+
.hasMessageContaining("spring.grpc.client.default-stub-factory");
140+
}
141+
}
142+
143+
@Nested
144+
@TestConfiguration(proxyBeanMethods = false)
145+
@SpringBootTest(classes = { ClientScanConfiguration.class, ClientScanConfigurationSpringBootTest.TestConfig.class },
146+
properties = { "spring.grpc.client.default-channel.address=static://localhost:9090",
147+
"spring.grpc.client.default-stub-factory=org.springframework.grpc.client.ReactorStubFactory" })
148+
class ClientScanConfigurationSpringBootTest {
149+
150+
@Autowired
151+
private ApplicationContext context;
152+
153+
@Autowired
154+
private GrpcClientProperties props;
155+
156+
@Autowired
157+
private Environment env;
158+
159+
@Test
160+
void propertyIsBoundAsBeanAndUsable() {
161+
assertThat(props.getDefaultStubFactory()).isEqualTo(ReactorStubFactory.class);
162+
163+
GrpcClientProperties rebound = Binder.get(env).bind("spring.grpc.client", GrpcClientProperties.class).get();
164+
165+
assertThat(rebound.getDefaultStubFactory()).isEqualTo(ReactorStubFactory.class);
166+
}
167+
168+
@Configuration
169+
@EnableConfigurationProperties(GrpcClientProperties.class)
170+
static class TestConfig {
171+
172+
}
173+
174+
}
175+
176+
}

0 commit comments

Comments
 (0)