From b3f84d1ee2b7e26c514ef43b6e108fe6ad530a71 Mon Sep 17 00:00:00 2001
From: ST-DDT
Date: Mon, 2 Nov 2020 21:59:52 +0100
Subject: [PATCH] Modify default channel factory to add support for 'null'
schemes (Fixes #419)
---
docs/en/client/configuration.md | 5 +
docs/en/client/testing.md | 26 +++++
.../GrpcClientAutoConfiguration.java | 17 ++-
.../channelfactory/GrpcChannelFactory.java | 9 +-
.../InProcessChannelFactory.java | 20 +++-
.../InProcessOrAlternativeChannelFactory.java | 103 ------------------
.../channelfactory/NullChannelFactory.java | 50 +++++++++
.../SchemaAwareChannelFactory.java | 96 ++++++++++++++++
.../inject/GrpcClientBeanPostProcessor.java | 2 +-
...a => AbstractGrpcClientInjectionTest.java} | 71 ++++++------
...faultInProcessGrpcClientInjectionTest.java | 42 +++++++
.../DefaultNettyGrpcClientInjectionTest.java | 40 +++++++
.../DefaultNullGrpcClientInjectionTest.java | 49 +++++++++
.../inject/InProcessClientInjectionTest.java | 41 +++++++
14 files changed, 417 insertions(+), 154 deletions(-)
delete mode 100644 grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessOrAlternativeChannelFactory.java
create mode 100644 grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/NullChannelFactory.java
create mode 100644 grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/SchemaAwareChannelFactory.java
rename tests/src/test/java/net/devh/boot/grpc/test/inject/{GrpcClientInjectionTest.java => AbstractGrpcClientInjectionTest.java} (68%)
create mode 100644 tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultInProcessGrpcClientInjectionTest.java
create mode 100644 tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNettyGrpcClientInjectionTest.java
create mode 100644 tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNullGrpcClientInjectionTest.java
create mode 100644 tests/src/test/java/net/devh/boot/grpc/test/inject/InProcessClientInjectionTest.java
diff --git a/docs/en/client/configuration.md b/docs/en/client/configuration.md
index daf7564a9..e379aeb79 100644
--- a/docs/en/client/configuration.md
+++ b/docs/en/client/configuration.md
@@ -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
diff --git a/docs/en/client/testing.md b/docs/en/client/testing.md
index a92e5f704..c50d9011c 100644
--- a/docs/en/client/testing.md
+++ b/docs/en/client/testing.md
@@ -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
@@ -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
- [Getting Started](getting-started.md)
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.java
index 64dbddbb2..ebcc318f0 100644
--- a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.java
+++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/autoconfigure/GrpcClientAutoConfiguration.java
@@ -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;
@@ -139,6 +140,7 @@ List 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"})
@@ -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
@@ -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
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/GrpcChannelFactory.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/GrpcChannelFactory.java
index 2958c3d38..55702ea2c 100644
--- a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/GrpcChannelFactory.java
+++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/GrpcChannelFactory.java
@@ -45,7 +45,8 @@ public interface GrpcChannelFactory extends AutoCloseable {
*
*
* @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());
@@ -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 interceptors) {
return createChannel(name, interceptors, false);
@@ -88,7 +90,8 @@ default Channel createChannel(final String name, final List 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 interceptors, boolean sortInterceptors);
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessChannelFactory.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessChannelFactory.java
index 63d27eec9..962c88653 100644
--- a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessChannelFactory.java
+++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessChannelFactory.java
@@ -17,6 +17,7 @@
package net.devh.boot.grpc.client.channelfactory;
+import java.net.URI;
import java.util.Collections;
import java.util.List;
@@ -37,14 +38,21 @@
@Slf4j
public class InProcessChannelFactory extends AbstractChannelFactory {
+ /**
+ * 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());
}
@@ -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 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
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessOrAlternativeChannelFactory.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessOrAlternativeChannelFactory.java
deleted file mode 100644
index 0feb7e225..000000000
--- a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/InProcessOrAlternativeChannelFactory.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2016-2020 Michael Zhang
- *
- * 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 static java.util.Objects.requireNonNull;
-
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-
-import com.google.common.collect.ImmutableMap;
-
-import io.grpc.Channel;
-import io.grpc.ClientInterceptor;
-import io.grpc.ConnectivityState;
-import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
-
-/**
- * This channel factory is a switch between the {@link InProcessChannelFactory} and an alternative implementation. All
- * channels that are configured with the {@code in-process} scheme will be handled by the in-process-channel-factory,
- * the other channels will be handled by the alternative implementation.
- *
- *
- * The following examples show how the configured address will be mapped to an actual channel:
- *
- *
- *
- * in-process:foobar
-> will use the foobar
in-process-channel.
- * in-process:foo/bar
-> will use the foo/bar
in-process-channel.
- * static://127.0.0.1
-> will be handled by the alternative grpc channel factory.
- *
- *
- *
- * Using this class does not incur any additional performance or resource costs, as the actual channels (in-process or
- * other) are only created on demand.
- *
- */
-public class InProcessOrAlternativeChannelFactory implements GrpcChannelFactory {
-
- private static final String IN_PROCESS_SCHEME = "in-process";
-
- private final GrpcChannelsProperties properties;
- private final InProcessChannelFactory inProcessChannelFactory;
- private final GrpcChannelFactory alternativeChannelFactory;
-
- /**
- * Creates a new InProcessOrAlternativeChannelFactory with the given properties and channel factories.
- *
- * @param properties The properties used to resolved the target scheme
- * @param inProcessChannelFactory The in process channel factory implementation to use.
- * @param alternativeChannelFactory The alternative channel factory implementation to use.
- */
- public InProcessOrAlternativeChannelFactory(final GrpcChannelsProperties properties,
- final InProcessChannelFactory inProcessChannelFactory, final GrpcChannelFactory alternativeChannelFactory) {
- this.properties = requireNonNull(properties, "properties");
- this.inProcessChannelFactory = requireNonNull(inProcessChannelFactory, "inProcessChannelFactory");
- this.alternativeChannelFactory = requireNonNull(alternativeChannelFactory, "alternativeChannelFactory");
- }
-
- @Override
- public Channel createChannel(final String name, final List interceptors,
- boolean sortInterceptors) {
- final URI address = this.properties.getChannel(name).getAddress();
- if (address != null && IN_PROCESS_SCHEME.equals(address.getScheme())) {
- return this.inProcessChannelFactory.createChannel(address.getSchemeSpecificPart(), interceptors,
- sortInterceptors);
- }
- return this.alternativeChannelFactory.createChannel(name, interceptors, sortInterceptors);
- }
-
- @Override
- public Map getConnectivityState() {
- return ImmutableMap.builder()
- .putAll(inProcessChannelFactory.getConnectivityState())
- .putAll(alternativeChannelFactory.getConnectivityState())
- .build();
- }
-
- @Override
- public void close() {
- try {
- this.inProcessChannelFactory.close();
- } finally {
- this.alternativeChannelFactory.close();
- }
- }
-
-}
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/NullChannelFactory.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/NullChannelFactory.java
new file mode 100644
index 000000000..69ca0acfe
--- /dev/null
+++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/NullChannelFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016-2020 Michael Zhang
+ *
+ * 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 interceptors,
+ final boolean sortInterceptors) {
+ return null;
+ }
+
+ @Override
+ public void close() {
+ // Nothing to do
+ }
+
+}
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/SchemaAwareChannelFactory.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/SchemaAwareChannelFactory.java
new file mode 100644
index 000000000..561c92f91
--- /dev/null
+++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/SchemaAwareChannelFactory.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016-2020 Michael Zhang
+ *
+ * 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.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.grpc.Channel;
+import io.grpc.ClientInterceptor;
+import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
+
+/**
+ * Creates a schema aware channel factory. The actual {@link GrpcChannelFactory} is chosen based on the scheme of the
+ * address associated to the requested channel name.
+ *
+ * @author Daniel Theuke (daniel.theuke@heuboe.de)
+ */
+public class SchemaAwareChannelFactory implements GrpcChannelFactory {
+
+ private final GrpcChannelsProperties properties;
+ private final Map factories;
+ private final GrpcChannelFactory fallback;
+
+ /**
+ * Creates a new SchemaAwareChannelFactory.
+ *
+ * @param properties The properties to lookup the addresses.
+ * @param fallback The fallback factory to use if the scheme does not match a specific factory.
+ */
+ public SchemaAwareChannelFactory(
+ final GrpcChannelsProperties properties,
+ final GrpcChannelFactory fallback) {
+
+ this(properties, new HashMap<>(), fallback);
+ }
+
+ /**
+ * Creates a new SchemaAwareChannelFactory.
+ *
+ * @param properties The properties to lookup the addresses.
+ * @param factories The factories by their associated scheme.
+ * @param fallback The fallback factory to use if the scheme does not match a specific factory.
+ */
+ public SchemaAwareChannelFactory(
+ final GrpcChannelsProperties properties,
+ final Map factories,
+ final GrpcChannelFactory fallback) {
+
+ this.properties = properties;
+ this.factories = new HashMap<>(factories);
+ this.fallback = fallback;
+ }
+
+ @Override
+ public Channel createChannel(
+ final String name,
+ final List interceptors,
+ final boolean sortInterceptors) {
+
+ final URI address = this.properties.getChannel(name).getAddress();
+ final String scheme = address == null ? null : address.getScheme();
+ return this.factories.getOrDefault(scheme, this.fallback)
+ .createChannel(name, interceptors, sortInterceptors);
+ }
+
+ public SchemaAwareChannelFactory put(final String scheme, final GrpcChannelFactory factory) {
+ this.factories.put(scheme, factory);
+ return this;
+ }
+
+ @Override
+ public void close() {
+ for (final GrpcChannelFactory factory : this.factories.values()) {
+ factory.close();
+ }
+ this.fallback.close();
+ }
+
+}
diff --git a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClientBeanPostProcessor.java b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClientBeanPostProcessor.java
index b1b1ad190..c1c12eef2 100644
--- a/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClientBeanPostProcessor.java
+++ b/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/inject/GrpcClientBeanPostProcessor.java
@@ -119,7 +119,7 @@ protected T processInjectionPoint(final Member injectionTarget, final Class<
try {
channel = getChannelFactory().createChannel(name, interceptors, annotation.sortInterceptors());
if (channel == null) {
- throw new IllegalStateException("Channel factory created a null channel for " + name);
+ return null;
}
} catch (final RuntimeException e) {
throw new IllegalStateException("Failed to create channel: " + name, e);
diff --git a/tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientInjectionTest.java b/tests/src/test/java/net/devh/boot/grpc/test/inject/AbstractGrpcClientInjectionTest.java
similarity index 68%
rename from tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientInjectionTest.java
rename to tests/src/test/java/net/devh/boot/grpc/test/inject/AbstractGrpcClientInjectionTest.java
index a4a587fbf..b0e7e45f5 100644
--- a/tests/src/test/java/net/devh/boot/grpc/test/inject/GrpcClientInjectionTest.java
+++ b/tests/src/test/java/net/devh/boot/grpc/test/inject/AbstractGrpcClientInjectionTest.java
@@ -22,24 +22,18 @@
import javax.annotation.PostConstruct;
import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import io.grpc.Channel;
import io.grpc.stub.AbstractStub;
import net.devh.boot.grpc.client.inject.GrpcClient;
import net.devh.boot.grpc.client.stubfactory.StandardJavaGrpcStubFactory;
import net.devh.boot.grpc.client.stubfactory.StubFactory;
-import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
-import net.devh.boot.grpc.test.config.InProcessConfiguration;
-import net.devh.boot.grpc.test.config.ServiceConfiguration;
import net.devh.boot.grpc.test.inject.CustomGrpc.ConstructorAccessibleStub;
import net.devh.boot.grpc.test.inject.CustomGrpc.CustomAccessibleStub;
import net.devh.boot.grpc.test.inject.CustomGrpc.FactoryMethodAccessibleStub;
-import net.devh.boot.grpc.test.inject.GrpcClientInjectionTest.TestConfig;
import net.devh.boot.grpc.test.proto.TestServiceGrpc.TestServiceBlockingStub;
import net.devh.boot.grpc.test.proto.TestServiceGrpc.TestServiceFutureStub;
import net.devh.boot.grpc.test.proto.TestServiceGrpc.TestServiceStub;
@@ -49,11 +43,8 @@
*
* @author Daniel Theuke (daniel.theuke@heuboe.de)
*/
-@SpringBootTest
-@SpringJUnitConfig(classes = {TestConfig.class, InProcessConfiguration.class, ServiceConfiguration.class,
- BaseAutoConfiguration.class})
@DirtiesContext
-class GrpcClientInjectionTest {
+abstract class AbstractGrpcClientInjectionTest {
@GrpcClient("test")
Channel channel;
@@ -81,75 +72,79 @@ class GrpcClientInjectionTest {
@PostConstruct
public void init() {
// Test injection
- assertNotNull(this.channel, "channel");
- assertNotNull(this.stub, "stub");
- assertNotNull(this.blockingStub, "blockingStub");
- assertNotNull(this.futureStub, "futureStub");
- assertNotNull(this.constructorStub, "constructorStub");
- assertNotNull(this.factoryMethodStub, "factoryMethodStub");
- assertNotNull(this.customStub, "customStub");
+ assertValid(this.channel, "channel");
+ assertValid(this.stub, "stub");
+ assertValid(this.blockingStub, "blockingStub");
+ assertValid(this.futureStub, "futureStub");
+ assertValid(this.constructorStub, "constructorStub");
+ assertValid(this.factoryMethodStub, "factoryMethodStub");
+ assertValid(this.customStub, "customStub");
}
@GrpcClient("test")
void inject(final Channel channel) {
- assertNotNull(channel, "channel");
+ assertValid(channel, "channel");
this.channelSetted = channel;
}
@GrpcClient("test")
void inject(final TestServiceStub stub) {
- assertNotNull(stub, "stub");
+ assertValid(stub, "stub");
this.stubSetted = stub;
}
@GrpcClient("test")
void inject(final TestServiceBlockingStub stub) {
- assertNotNull(stub, "stub");
+ assertValid(stub, "stub");
this.blockingStubSetted = stub;
}
@GrpcClient("test")
void inject(final TestServiceFutureStub stub) {
- assertNotNull(stub, "stub");
+ assertValid(stub, "stub");
this.futureStubSetted = stub;
}
@GrpcClient("test")
void inject(final ConstructorAccessibleStub stub) {
- assertNotNull(stub, "stub");
+ assertValid(stub, "stub");
this.constructorStubSetted = stub;
}
@GrpcClient("test")
void inject(final FactoryMethodAccessibleStub stub) {
- assertNotNull(stub, "stub");
+ assertValid(stub, "stub");
this.factoryMethodStubSetted = stub;
}
@GrpcClient("test")
void inject(final CustomAccessibleStub stub) {
- assertNotNull(stub, "stub");
+ assertValid(stub, "stub");
this.customStubSetted = stub;
}
@Test
void testAllSet() {
// Field injection
- assertNotNull(this.channel, "channel");
- assertNotNull(this.stub, "stub");
- assertNotNull(this.blockingStub, "blockingStub");
- assertNotNull(this.futureStub, "futureStub");
- assertNotNull(this.constructorStub, "constructorStub");
- assertNotNull(this.factoryMethodStub, "factoryMethodStub");
- assertNotNull(this.customStub, "customStub");
+ assertValid(this.channel, "channel");
+ assertValid(this.stub, "stub");
+ assertValid(this.blockingStub, "blockingStub");
+ assertValid(this.futureStub, "futureStub");
+ assertValid(this.constructorStub, "constructorStub");
+ assertValid(this.factoryMethodStub, "factoryMethodStub");
+ assertValid(this.customStub, "customStub");
// Setter injection
- assertNotNull(this.channelSetted, "channelSetted");
- assertNotNull(this.stubSetted, "stubSetted");
- assertNotNull(this.blockingStubSetted, "blockingStubSetted");
- assertNotNull(this.futureStubSetted, "futureStubSetted");
- assertNotNull(this.constructorStubSetted, "constructorStubSetted");
- assertNotNull(this.factoryMethodStubSetted, "factoryMethodStubSetted");
- assertNotNull(this.customStubSetted, "customStubSetted");
+ assertValid(this.channelSetted, "channelSetted");
+ assertValid(this.stubSetted, "stubSetted");
+ assertValid(this.blockingStubSetted, "blockingStubSetted");
+ assertValid(this.futureStubSetted, "futureStubSetted");
+ assertValid(this.constructorStubSetted, "constructorStubSetted");
+ assertValid(this.factoryMethodStubSetted, "factoryMethodStubSetted");
+ assertValid(this.customStubSetted, "customStubSetted");
+ }
+
+ protected void assertValid(final Object actual, final String message) {
+ assertNotNull(actual, message);
}
@TestConfiguration
diff --git a/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultInProcessGrpcClientInjectionTest.java b/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultInProcessGrpcClientInjectionTest.java
new file mode 100644
index 000000000..b4542ac99
--- /dev/null
+++ b/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultInProcessGrpcClientInjectionTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016-2020 Michael Zhang
+ *
+ * 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.test.inject;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
+import net.devh.boot.grpc.test.config.ServiceConfiguration;
+
+/**
+ * A test checking that the client injection works.
+ *
+ * @author Daniel Theuke (daniel.theuke@heuboe.de)
+ */
+@SpringBootTest(classes = {
+ DefaultInProcessGrpcClientInjectionTest.TestConfig.class,
+ ServiceConfiguration.class,
+ BaseAutoConfiguration.class,
+}, properties = {
+ "grpc.server.in-process-name=test",
+ "grpc.server.port=-1",
+ "grpc.client.test.address=in-process:test",
+})
+@DirtiesContext
+class DefaultInProcessGrpcClientInjectionTest extends AbstractGrpcClientInjectionTest {
+}
diff --git a/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNettyGrpcClientInjectionTest.java b/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNettyGrpcClientInjectionTest.java
new file mode 100644
index 000000000..a0b63e06b
--- /dev/null
+++ b/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNettyGrpcClientInjectionTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016-2020 Michael Zhang
+ *
+ * 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.test.inject;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
+import net.devh.boot.grpc.test.config.ServiceConfiguration;
+
+/**
+ * A test checking that the client injection works.
+ *
+ * @author Daniel Theuke (daniel.theuke@heuboe.de)
+ */
+@SpringBootTest(classes = {
+ DefaultNettyGrpcClientInjectionTest.TestConfig.class,
+ ServiceConfiguration.class,
+ BaseAutoConfiguration.class,
+}, properties = {
+ "grpc.client.test.address=dns:/localhost",
+})
+@DirtiesContext
+class DefaultNettyGrpcClientInjectionTest extends AbstractGrpcClientInjectionTest {
+}
diff --git a/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNullGrpcClientInjectionTest.java b/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNullGrpcClientInjectionTest.java
new file mode 100644
index 000000000..e46eb3258
--- /dev/null
+++ b/tests/src/test/java/net/devh/boot/grpc/test/inject/DefaultNullGrpcClientInjectionTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016-2020 Michael Zhang
+ *
+ * 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.test.inject;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
+import net.devh.boot.grpc.test.config.ServiceConfiguration;
+
+/**
+ * A test checking that the client injection works.
+ *
+ * @author Daniel Theuke (daniel.theuke@heuboe.de)
+ */
+@SpringBootTest(classes = {
+ DefaultNullGrpcClientInjectionTest.TestConfig.class,
+ ServiceConfiguration.class,
+ BaseAutoConfiguration.class,
+}, properties = {
+ "grpc.server.port=-1",
+ "grpc.client.test.address=null:/",
+})
+@DirtiesContext
+class DefaultNullGrpcClientInjectionTest extends AbstractGrpcClientInjectionTest {
+
+ @Override
+ protected void assertValid(final Object actual, final String message) {
+ assertNull(actual, message);
+ }
+
+}
diff --git a/tests/src/test/java/net/devh/boot/grpc/test/inject/InProcessClientInjectionTest.java b/tests/src/test/java/net/devh/boot/grpc/test/inject/InProcessClientInjectionTest.java
new file mode 100644
index 000000000..c5c3ea136
--- /dev/null
+++ b/tests/src/test/java/net/devh/boot/grpc/test/inject/InProcessClientInjectionTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016-2020 Michael Zhang
+ *
+ * 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.test.inject;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+
+import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
+import net.devh.boot.grpc.test.config.InProcessConfiguration;
+import net.devh.boot.grpc.test.config.ServiceConfiguration;
+
+/**
+ * A test checking that the client injection works.
+ *
+ * @author Daniel Theuke (daniel.theuke@heuboe.de)
+ */
+@SpringBootTest(classes = {
+ DefaultNettyGrpcClientInjectionTest.TestConfig.class,
+ InProcessConfiguration.class,
+ ServiceConfiguration.class,
+ BaseAutoConfiguration.class,
+})
+@DirtiesContext
+class InProcessClientInjectionTest extends AbstractGrpcClientInjectionTest {
+
+}