Skip to content

Commit 5dc1d77

Browse files
committed
Modify default channel factory to add support for 'null' schemes
Fixes #419
1 parent 496971b commit 5dc1d77

14 files changed

+556
-116
lines changed

docs/en/client/configuration.md

+5
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ There are a number of supported schemes, that you can use to determine the targe
7676
This is a special scheme that will bypass the normal channel factory and will use the `InProcessChannelFactory`
7777
instead. Use it to connect to the [`InProcessServer`](../server/configuration.md#enabling-the-inprocessserver). \
7878
Example: `in-process:foobar`
79+
- `null`: \
80+
This is a special scheme that will bypass the normal channel factory and will use the `NullChannelFactory`
81+
instead. Use it to use `null` for a GrpcClient annotated field. \
82+
Useful for testing. \
83+
Example: `null:/`
7984
- *custom*: \
8085
You can define custom
8186
[`NameResolverProvider`s](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/NameResolverProvider.html) those

docs/en/client/testing.md

+26
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This section describes how you write tests for components that use the `@GrpcCli
1111
- [Useful Dependencies](#useful-dependencies)
1212
- [Using a Mocked Stub](#using-a-mocked-stub)
1313
- [Running a Dummy Server](#running-a-dummy-server)
14+
- [Skipping injection](#skipping-injection)
1415

1516
## Additional Topics <!-- omit in toc -->
1617

@@ -254,6 +255,31 @@ public class ChatServiceImplForMyComponentIntegrationTest extends ChatServiceGrp
254255
}
255256
````
256257

258+
## Skipping injection
259+
260+
If you don't need a specific `@GrpcClient` in a test, then you can configure it to be skipped using the `null` scheme.
261+
(In that case it will be injected with `null`)
262+
263+
````java
264+
@SpringBootTest(properties = {
265+
"grpc.client.test.address=null:/",
266+
}, ...)
267+
class MyTest {
268+
269+
@GrpcClient("test")
270+
Channel channel;
271+
272+
@Test()
273+
void test() {
274+
assertNull(channel);
275+
}
276+
277+
}
278+
````
279+
280+
> **Note:** Due to configuration limitations you cannot use just `null` or `null:` as address,
281+
> you have to specify a scheme specific part e.g.: `null:/` or `null:null`.
282+
257283
## Additional Topics <!-- omit in toc -->
258284

259285
- [Getting Started](getting-started.md)

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@
3939
import net.devh.boot.grpc.client.channelfactory.GrpcChannelConfigurer;
4040
import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
4141
import net.devh.boot.grpc.client.channelfactory.InProcessChannelFactory;
42-
import net.devh.boot.grpc.client.channelfactory.InProcessOrAlternativeChannelFactory;
4342
import net.devh.boot.grpc.client.channelfactory.NettyChannelFactory;
43+
import net.devh.boot.grpc.client.channelfactory.NullChannelFactory;
44+
import net.devh.boot.grpc.client.channelfactory.SchemaAwareChannelFactory;
4445
import net.devh.boot.grpc.client.channelfactory.ShadedNettyChannelFactory;
4546
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
4647
import net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor;
@@ -139,6 +140,7 @@ List<GrpcChannelConfigurer> defaultChannelConfigurers() {
139140
}
140141

141142
// First try the shaded netty channel factory
143+
@SuppressWarnings("resource")
142144
@ConditionalOnMissingBean(GrpcChannelFactory.class)
143145
@ConditionalOnClass(name = {"io.grpc.netty.shaded.io.netty.channel.Channel",
144146
"io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder"})
@@ -152,12 +154,15 @@ GrpcChannelFactory shadedNettyGrpcChannelFactory(
152154
log.info("Detected grpc-netty-shaded: Creating ShadedNettyChannelFactory + InProcessChannelFactory");
153155
final ShadedNettyChannelFactory channelFactory =
154156
new ShadedNettyChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
155-
final InProcessChannelFactory inProcessChannelFactory =
157+
final InProcessChannelFactory inProcess =
156158
new InProcessChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
157-
return new InProcessOrAlternativeChannelFactory(properties, inProcessChannelFactory, channelFactory);
159+
return new SchemaAwareChannelFactory(properties, channelFactory)
160+
.put(InProcessChannelFactory.SCHEME, inProcess)
161+
.put(NullChannelFactory.SCHEME, new NullChannelFactory());
158162
}
159163

160164
// Then try the normal netty channel factory
165+
@SuppressWarnings("resource")
161166
@ConditionalOnMissingBean(GrpcChannelFactory.class)
162167
@ConditionalOnClass(name = {"io.netty.channel.Channel", "io.grpc.netty.NettyChannelBuilder"})
163168
@Bean
@@ -170,9 +175,11 @@ GrpcChannelFactory nettyGrpcChannelFactory(
170175
log.info("Detected grpc-netty: Creating NettyChannelFactory + InProcessChannelFactory");
171176
final NettyChannelFactory channelFactory =
172177
new NettyChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
173-
final InProcessChannelFactory inProcessChannelFactory =
178+
final InProcessChannelFactory inProcess =
174179
new InProcessChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
175-
return new InProcessOrAlternativeChannelFactory(properties, inProcessChannelFactory, channelFactory);
180+
return new SchemaAwareChannelFactory(properties, channelFactory)
181+
.put(InProcessChannelFactory.SCHEME, inProcess)
182+
.put(NullChannelFactory.SCHEME, new NullChannelFactory());
176183
}
177184

178185
// Finally try the in process channel factory

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/GrpcChannelFactory.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public interface GrpcChannelFactory extends AutoCloseable {
4545
* </p>
4646
*
4747
* @param name The name of the service.
48-
* @return The newly created channel for the given service.
48+
* @return The newly created channel for the given service. Might return null if the channel was intentionally
49+
* skipped.
4950
*/
5051
default Channel createChannel(final String name) {
5152
return createChannel(name, Collections.emptyList());
@@ -66,7 +67,8 @@ default Channel createChannel(final String name) {
6667
*
6768
* @param name The name of the service.
6869
* @param interceptors A list of additional client interceptors that should be added to the channel.
69-
* @return The newly created channel for the given service.
70+
* @return The newly created channel for the given service. Might return null if the channel was intentionally
71+
* skipped.
7072
*/
7173
default Channel createChannel(final String name, final List<ClientInterceptor> interceptors) {
7274
return createChannel(name, interceptors, false);
@@ -88,7 +90,8 @@ default Channel createChannel(final String name, final List<ClientInterceptor> i
8890
* @param name The name of the service.
8991
* @param interceptors A list of additional client interceptors that should be added to the channel.
9092
* @param sortInterceptors Whether the interceptors (both global and custom) should be sorted before being applied.
91-
* @return The newly created channel for the given service.
93+
* @return The newly created channel for the given service. Might return null if the channel was intentionally
94+
* skipped.
9295
*/
9396
Channel createChannel(String name, List<ClientInterceptor> interceptors, boolean sortInterceptors);
9497

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessChannelFactory.java

+16-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package net.devh.boot.grpc.client.channelfactory;
1919

20+
import java.net.URI;
2021
import java.util.Collections;
2122
import java.util.List;
2223

@@ -37,14 +38,21 @@
3738
@Slf4j
3839
public class InProcessChannelFactory extends AbstractChannelFactory<InProcessChannelBuilder> {
3940

41+
/**
42+
* The scheme of this factory: {@value #SCHEME}.
43+
*/
44+
public static final String SCHEME = "in-process";
45+
4046
/**
4147
* Creates a new InProcessChannelFactory with the given properties.
4248
*
4349
* @param properties The properties for the channels to create.
4450
* @param globalClientInterceptorRegistry The interceptor registry to use.
4551
*/
46-
public InProcessChannelFactory(final GrpcChannelsProperties properties,
52+
public InProcessChannelFactory(
53+
final GrpcChannelsProperties properties,
4754
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry) {
55+
4856
this(properties, globalClientInterceptorRegistry, Collections.emptyList());
4957
}
5058

@@ -55,16 +63,20 @@ public InProcessChannelFactory(final GrpcChannelsProperties properties,
5563
* @param globalClientInterceptorRegistry The interceptor registry to use.
5664
* @param channelConfigurers The channel configurers to use. Can be empty.
5765
*/
58-
public InProcessChannelFactory(final GrpcChannelsProperties properties,
66+
public InProcessChannelFactory(
67+
final GrpcChannelsProperties properties,
5968
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry,
6069
final List<GrpcChannelConfigurer> channelConfigurers) {
70+
6171
super(properties, globalClientInterceptorRegistry, channelConfigurers);
6272
}
6373

6474
@Override
6575
protected InProcessChannelBuilder newChannelBuilder(final String name) {
66-
log.debug("Creating new channel: {}", name);
67-
return InProcessChannelBuilder.forName(name);
76+
final URI address = getPropertiesFor(name).getAddress();
77+
final String target = address == null ? name : address.getSchemeSpecificPart();
78+
log.debug("Creating new channel: {}", target);
79+
return InProcessChannelBuilder.forName(target);
6880
}
6981

7082
@Override

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessOrAlternativeChannelFactory.java

-103
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2016-2021 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.client.channelfactory;
19+
20+
import java.util.List;
21+
22+
import io.grpc.Channel;
23+
import io.grpc.ClientInterceptor;
24+
25+
/**
26+
* An dummy channel factory that always returns null. Useful for skipping specific channels during tests/profiles.
27+
*
28+
* @author Daniel Theuke ([email protected])
29+
*/
30+
public final class NullChannelFactory implements GrpcChannelFactory {
31+
32+
/**
33+
* The scheme of this factory: {@value #SCHEME}.
34+
*/
35+
public static final String SCHEME = "null";
36+
37+
@Override
38+
public Channel createChannel(
39+
final String name,
40+
final List<ClientInterceptor> interceptors,
41+
final boolean sortInterceptors) {
42+
return null;
43+
}
44+
45+
@Override
46+
public void close() {
47+
// Nothing to do
48+
}
49+
50+
}

0 commit comments

Comments
 (0)