diff --git a/docs/src/main/asciidoc/mp/grpc/client.adoc b/docs/src/main/asciidoc/mp/grpc/client.adoc index 2ce1e87b7e2..bf2b196de1d 100644 --- a/docs/src/main/asciidoc/mp/grpc/client.adoc +++ b/docs/src/main/asciidoc/mp/grpc/client.adoc @@ -35,6 +35,7 @@ include::{rootdir}/includes/mp.adoc[] - <> ** <> ** <> +** <> - <> == Overview @@ -133,7 +134,7 @@ include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_5, indent=0] service when it differs from the interface name, as it is the case in this example. <2> The `@Grpc.GrpcChannel` annotation is the qualifier that supplies the channel name. This is the same name as used in the channel configuration in the examples provided in -the <>. +the <>. There is no need to write any code to implement the client. The Helidon MP gRPC API will create a dynamic proxy for the interface using the information from the annotations and @@ -162,14 +163,33 @@ include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_7, indent=0] <1> The `@Inject` annotation tells CDI to inject the client implementation. <2> The `@Grpc.GrpcProxy` annotation is used by the CDI container to match the injection point to -the gRPC MP APIs provider. +the gRPC MP API provider. When the CDI container instantiates `MyAppBean`, it will inject a dynamic proxy into the `stringServiceClient` field, and then provide the necessary logic for the proxy methods to convert a method call into a gRPC call. In the example above, there is no need to use a channel directly. The correct channel is added to -the dynamic client proxy internally by the Helidon MP gRPC APIs. +the dynamic client proxy internally by the Helidon MP gRPC API. + +=== Injecting Channels + +Channels can also be directly injected into application bean instances. +The Helidon gRPC client API has CDI producers to inject `io.grpc.Channel` instances. + +For example, a class might have an injectable `io.grpc.Channel` field as follows: + +[source,java] +---- +include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_8, indent=4] +---- + +<1> The `@Inject` annotation tells CDI to inject the channel. +<2> The `@Grpc.GrpcChannel` annotation supplies the channel name. +This is the same name as used in the channel configuration in the examples provided in +the <>. + +An injected channel can be used, for example, when directly instantiating `protoc` generated stubs. == Examples diff --git a/docs/src/main/java/io/helidon/docs/mp/grpc/GrpcSnippets.java b/docs/src/main/java/io/helidon/docs/mp/grpc/GrpcSnippets.java index 54f15767343..6e83cb4fa25 100644 --- a/docs/src/main/java/io/helidon/docs/mp/grpc/GrpcSnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/grpc/GrpcSnippets.java @@ -136,4 +136,13 @@ public class MyAppBean { } // end::snippet_7[] } + + class Snippet8 { + + // tag::snippet_8[] + @Inject // <1> + @Grpc.GrpcChannel("string-channel") // <2> + private Channel channel; + // end::snippet_8[] + } } diff --git a/grpc/api/src/main/java/io/helidon/grpc/api/Grpc.java b/grpc/api/src/main/java/io/helidon/grpc/api/Grpc.java index 4f5a50c2e70..3a4f3585cfe 100644 --- a/grpc/api/src/main/java/io/helidon/grpc/api/Grpc.java +++ b/grpc/api/src/main/java/io/helidon/grpc/api/Grpc.java @@ -275,7 +275,7 @@ public interface Grpc { } /** - * An annotation to indicate the response type of a gRPC method. + * An annotation to indicate the response type of gRPC method. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @@ -293,7 +293,7 @@ public interface Grpc { /** * An annotation that can be used to specify the name of a configured gRPC channel. */ - @Target({ElementType.TYPE, ElementType.METHOD}) + @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @interface GrpcChannel { diff --git a/microprofile/grpc/client/src/main/java/io/helidon/microprofile/grpc/client/ChannelProducer.java b/microprofile/grpc/client/src/main/java/io/helidon/microprofile/grpc/client/ChannelProducer.java index 9a1cb1f3c5f..521965b2ed8 100644 --- a/microprofile/grpc/client/src/main/java/io/helidon/microprofile/grpc/client/ChannelProducer.java +++ b/microprofile/grpc/client/src/main/java/io/helidon/microprofile/grpc/client/ChannelProducer.java @@ -57,9 +57,8 @@ public class ChannelProducer { * @return a gRPC {@link io.grpc.Channel} */ @Produces - @Grpc.GrpcChannel(GrpcChannelsProvider.DEFAULT_CHANNEL_NAME) public Channel get(InjectionPoint injectionPoint) { - Grpc.GrpcChannel qualifier = injectionPoint.getQualifiers() + Grpc.GrpcChannel qualifier = injectionPoint.getAnnotated().getAnnotations() .stream() .filter(q -> q.annotationType().equals(Grpc.GrpcChannel.class)) .map(q -> (Grpc.GrpcChannel) q) @@ -70,16 +69,6 @@ public Channel get(InjectionPoint injectionPoint) { return findChannel(name); } - /** - * Produces the default gRPC {@link io.grpc.Channel}. - * - * @return the default gRPC {@link io.grpc.Channel} - */ - @Produces - public Channel getDefaultChannel() { - return findChannel(GrpcChannelsProvider.DEFAULT_CHANNEL_NAME); - } - /** * Obtain the named {@link io.grpc.Channel}. * diff --git a/microprofile/grpc/client/src/test/java/io/helidon/microprofile/grpc/client/ChannelInjectionTest.java b/microprofile/grpc/client/src/test/java/io/helidon/microprofile/grpc/client/ChannelInjectionTest.java new file mode 100644 index 00000000000..5c17ff64123 --- /dev/null +++ b/microprofile/grpc/client/src/test/java/io/helidon/microprofile/grpc/client/ChannelInjectionTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.helidon.microprofile.grpc.client; + +import io.helidon.grpc.api.Grpc; +import io.helidon.microprofile.testing.junit5.HelidonTest; + +import io.grpc.Channel; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +@HelidonTest +class ChannelInjectionTest { + + @Inject + @Grpc.GrpcChannel("echo-channel") + private Channel echoChannel1; + + private final Channel echoChannel2; + + @Inject + ChannelInjectionTest(@Grpc.GrpcChannel("echo-channel") Channel echoChannel2) { + this.echoChannel2 = echoChannel2; + } + + @Test + void testInjection() { + assertThat(echoChannel1, notNullValue()); + assertThat(echoChannel2, notNullValue()); + assertThat(echoChannel1.getClass(), equalTo(echoChannel2.getClass())); + assertThat(echoChannel1.authority(), equalTo(echoChannel2.authority())); + } +}