Skip to content

Commit

Permalink
Modify default channel factory to add support for 'null' schemes (Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ST-DDT committed Nov 2, 2020
1 parent aa286ab commit b3f84d1
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 154 deletions.
5 changes: 5 additions & 0 deletions docs/en/client/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ There are a number of supported schemes, that you can use to determine the targe
This is a special scheme that will bypass the normal channel factory and will use the `InProcessChannelFactory`
instead. Use it to connect to the [`InProcessServer`](../server/configuration.md#enabling-the-inprocessserver). \
Example: `in-process:foobar`
- `null`: \
This is a special scheme that will bypass the normal channel factory and will use the `NullChannelFactory`
instead. Use it to use `null` for a GrpcClient annotated field. \
Useful for testing. \
Example: `null:/`
- *custom*: \
You can define custom
[`NameResolverProvider`s](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/NameResolverProvider.html) those
Expand Down
26 changes: 26 additions & 0 deletions docs/en/client/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This section describes how you write tests for components that use the `@GrpcCli
- [Useful Dependencies](#useful-dependencies)
- [Using a Mocked Stub](#using-a-mocked-stub)
- [Running a Dummy Server](#running-a-dummy-server)
- [Skipping injection](#skipping-injection)

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

Expand Down Expand Up @@ -254,6 +255,31 @@ public class ChatServiceImplForMyComponentIntegrationTest extends ChatServiceGrp
}
````

## Skipping injection

If you don't need a specific `@GrpcClient` in a test, then you can configure it to be skipped using the `null` scheme.
(In that case it will be injected with `null`)

````java
@SpringBootTest(properties = {
"grpc.client.test.address=null:/",
}, ...)
class MyTest {

@GrpcClient("test")
Channel channel;

@Test()
void test() {
assertNull(channel);
}

}
````

> **Note:** Due to configuration limitations you cannot use just `null` or `null:` as address,
> you have to specify a scheme specific part e.g.: `null:/` or `null:null`.
## Additional Topics <!-- omit in toc -->

- [Getting Started](getting-started.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
import net.devh.boot.grpc.client.channelfactory.GrpcChannelConfigurer;
import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory;
import net.devh.boot.grpc.client.channelfactory.InProcessChannelFactory;
import net.devh.boot.grpc.client.channelfactory.InProcessOrAlternativeChannelFactory;
import net.devh.boot.grpc.client.channelfactory.NettyChannelFactory;
import net.devh.boot.grpc.client.channelfactory.NullChannelFactory;
import net.devh.boot.grpc.client.channelfactory.SchemaAwareChannelFactory;
import net.devh.boot.grpc.client.channelfactory.ShadedNettyChannelFactory;
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
import net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor;
Expand Down Expand Up @@ -139,6 +140,7 @@ List<GrpcChannelConfigurer> defaultChannelConfigurers() {
}

// First try the shaded netty channel factory
@SuppressWarnings("resource")
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.grpc.netty.shaded.io.netty.channel.Channel",
"io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder"})
Expand All @@ -152,12 +154,15 @@ GrpcChannelFactory shadedNettyGrpcChannelFactory(
log.info("Detected grpc-netty-shaded: Creating ShadedNettyChannelFactory + InProcessChannelFactory");
final ShadedNettyChannelFactory channelFactory =
new ShadedNettyChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
final InProcessChannelFactory inProcessChannelFactory =
final InProcessChannelFactory inProcess =
new InProcessChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
return new InProcessOrAlternativeChannelFactory(properties, inProcessChannelFactory, channelFactory);
return new SchemaAwareChannelFactory(properties, channelFactory)
.put(InProcessChannelFactory.SCHEME, inProcess)
.put(NullChannelFactory.SCHEME, new NullChannelFactory());
}

// Then try the normal netty channel factory
@SuppressWarnings("resource")
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.netty.channel.Channel", "io.grpc.netty.NettyChannelBuilder"})
@Bean
Expand All @@ -170,9 +175,11 @@ GrpcChannelFactory nettyGrpcChannelFactory(
log.info("Detected grpc-netty: Creating NettyChannelFactory + InProcessChannelFactory");
final NettyChannelFactory channelFactory =
new NettyChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
final InProcessChannelFactory inProcessChannelFactory =
final InProcessChannelFactory inProcess =
new InProcessChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
return new InProcessOrAlternativeChannelFactory(properties, inProcessChannelFactory, channelFactory);
return new SchemaAwareChannelFactory(properties, channelFactory)
.put(InProcessChannelFactory.SCHEME, inProcess)
.put(NullChannelFactory.SCHEME, new NullChannelFactory());
}

// Finally try the in process channel factory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public interface GrpcChannelFactory extends AutoCloseable {
* </p>
*
* @param name The name of the service.
* @return The newly created channel for the given service.
* @return The newly created channel for the given service. Might return null if the channel was intentionally
* skipped.
*/
default Channel createChannel(final String name) {
return createChannel(name, Collections.emptyList());
Expand All @@ -66,7 +67,8 @@ default Channel createChannel(final String name) {
*
* @param name The name of the service.
* @param interceptors A list of additional client interceptors that should be added to the channel.
* @return The newly created channel for the given service.
* @return The newly created channel for the given service. Might return null if the channel was intentionally
* skipped.
*/
default Channel createChannel(final String name, final List<ClientInterceptor> interceptors) {
return createChannel(name, interceptors, false);
Expand All @@ -88,7 +90,8 @@ default Channel createChannel(final String name, final List<ClientInterceptor> i
* @param name The name of the service.
* @param interceptors A list of additional client interceptors that should be added to the channel.
* @param sortInterceptors Whether the interceptors (both global and custom) should be sorted before being applied.
* @return The newly created channel for the given service.
* @return The newly created channel for the given service. Might return null if the channel was intentionally
* skipped.
*/
Channel createChannel(String name, List<ClientInterceptor> interceptors, boolean sortInterceptors);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

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

import java.net.URI;
import java.util.Collections;
import java.util.List;

Expand All @@ -37,14 +38,21 @@
@Slf4j
public class InProcessChannelFactory extends AbstractChannelFactory<InProcessChannelBuilder> {

/**
* The scheme of this factory: {@value #SCHEME}.
*/
public static final String SCHEME = "in-process";

/**
* Creates a new InProcessChannelFactory with the given properties.
*
* @param properties The properties for the channels to create.
* @param globalClientInterceptorRegistry The interceptor registry to use.
*/
public InProcessChannelFactory(final GrpcChannelsProperties properties,
public InProcessChannelFactory(
final GrpcChannelsProperties properties,
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry) {

this(properties, globalClientInterceptorRegistry, Collections.emptyList());
}

Expand All @@ -55,16 +63,20 @@ public InProcessChannelFactory(final GrpcChannelsProperties properties,
* @param globalClientInterceptorRegistry The interceptor registry to use.
* @param channelConfigurers The channel configurers to use. Can be empty.
*/
public InProcessChannelFactory(final GrpcChannelsProperties properties,
public InProcessChannelFactory(
final GrpcChannelsProperties properties,
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry,
final List<GrpcChannelConfigurer> channelConfigurers) {

super(properties, globalClientInterceptorRegistry, channelConfigurers);
}

@Override
protected InProcessChannelBuilder newChannelBuilder(final String name) {
log.debug("Creating new channel: {}", name);
return InProcessChannelBuilder.forName(name);
final URI address = getPropertiesFor(name).getAddress();
final String target = address == null ? name : address.getSchemeSpecificPart();
log.debug("Creating new channel: {}", target);
return InProcessChannelBuilder.forName(target);
}

@Override
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

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

import java.util.List;

import io.grpc.Channel;
import io.grpc.ClientInterceptor;

/**
* An dummy channel factory that always returns null. Useful for skipping specific channels during tests/profiles.
*
* @author Daniel Theuke (daniel.theuke@heuboe.de)
*/
public final class NullChannelFactory implements GrpcChannelFactory {

/**
* The scheme of this factory: {@value #SCHEME}.
*/
public static final String SCHEME = "null";

@Override
public Channel createChannel(
final String name,
final List<ClientInterceptor> interceptors,
final boolean sortInterceptors) {
return null;
}

@Override
public void close() {
// Nothing to do
}

}

0 comments on commit b3f84d1

Please sign in to comment.