diff --git a/docs/src/main/asciidoc/mp/grpc/server.adoc b/docs/src/main/asciidoc/mp/grpc/server.adoc index de2bc0520a7..78d34bb4982 100644 --- a/docs/src/main/asciidoc/mp/grpc/server.adoc +++ b/docs/src/main/asciidoc/mp/grpc/server.adoc @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// - Copyright (c) 2019, 2024 Oracle and/or its affiliates. + Copyright (c) 2019, 2025 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. @@ -32,21 +32,21 @@ include::{rootdir}/includes/mp.adoc[] - <> - <> ** <> -** <> -** <> -- <> +** <> +- <> +- <> - <> - <> == Overview -The gRPC Microprofile APIs are an extension to xref:{rootdir}/mp/introduction.adoc[Helidon MP] to allow building -gRPC services that integrate with the Microprofile APIs. Using Helidon gRPC MP makes building gRPC services -an easier process compared to the traditional approach using Protobuf files and code generation. Services +The gRPC Microprofile API is an extension to xref:{rootdir}/mp/introduction.adoc[Helidon MP] to allow building +gRPC services that integrate with the Microprofile APIs. Using Helidon MP makes building gRPC services +easier when compared to the traditional approach. Services can be built using POJOs that are then discovered and deployed at runtime in the same way Helidon MP -discovers and deploys other web resources. +discovers and deploys other Web services. Building gRPC services using Helidon gRPC MP is simple and allows developers to concentrate on their -application logic without needing to write a lot of boilerplate gRPC code. +application logic without the need to write a lot of boilerplate code. include::{rootdir}/includes/dependencies.adoc[] @@ -62,28 +62,33 @@ include::{rootdir}/includes/dependencies.adoc[] All Helidon gRPC MP annotations are defined in the `Grpc` interface. The following annotations are used to implement Helidon MP gRPC services: -* `@Grpc.GrpcService` - an annotation used to mark a class as representing a gRPC service. -* `@Grpc.GrpcMarshaller` - an annotation used to annotate a type or method to specify the named marshaller -supplier to use for gRPC method calls. +* `@Grpc.GrpcService`: an annotation that marks a class as a gRPC service. + +* `@Grpc.GrpcMarshaller`: an annotation on a type or method that specifies a named marshaller +supplier. This annotation is required when not using Protobuf serialization. + +* `@Grpc.Proto`: an annotation for an optional method returning the Protobuf descriptor. For +more information see the <>. The following gRPC method types are supported: -* `@Grpc.Unary` - a method with at most a single request value and returning at most a single -response value. -*`@Grpc.ServerStreaming` - a method that takes at most a single request value but may return zero -or more response values. -* `@Grpc.ClientStreaming` - a method that takes one or more request values and returns at most -one response value. -* `@Grpc.Bidirectional` - A method that can take one or more request values and return zero or more -response values. +* `@Grpc.Unary`: a method that takes a single value (or void) and returns a single value +(or void). + +* `@Grpc.ServerStreaming`: a method that takes a single value (or void) and returns a +stream of values. + +* `@Grpc.ClientStreaming`: a method that takes a stream of values and returns a single value (or void). + +* `@Grpc.Bidirectional`: a method that takes a stream of values and returns a stream of values. == Usage === Defining a Service -The traditional approach to build Java gRPC services is to write Protobuf files describing the service, -use these files to generate service stubs, and then implement the service methods by extending the -generated stub classes. Using Helidon gRPC MP API, all you need to do is write an annotated service -implementation class that is just a normal POJO. +The traditional approach to building Java gRPC services is to write Protobuf files describing a +service, use these files to generate service stubs, and then implement the service methods +from stub classes. In Helidon MP, you have a simpler option of just writing normal POJOs that +refer to the Protobuf message types without the need to extend any generated stubs. For example: @@ -92,97 +97,121 @@ For example: include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_1, indent=0] ---- -The code above is a simple service with a single unary method that just converts a String to uppercase. -The important parts in the example are the `@ApplicationScoped`, `@Grpc.GrpcService` and `@Grpc.Unary` -annotations. These, along with other annotations discussed later, allow the gRPC MP APIs to discover, -configure and deploy the service. +Note that the message types in `Strings` are generated from a Protobuf file, but the class +itself is just a POJO that uses the Helidon MP annotations described above. In addition, +`@Grpc.Unary` overrides the Java method name to match that in the Protobuf file, +as shown next: -Of course Helidon gRPC MP does not preclude you from using the Protobuf files approach as traditional -gRPC Java services also work in a gRPC MP server. +[source,protobuf] +---- +syntax = "proto3"; -As already shown above, a Helidon gRPC MP service is just an annotated POJO. To make a class a service, -it requires two annotations. +service StringService { + rpc Upper (StringMessage) returns (StringMessage) {} +} + +message StringMessage { + string text = 1; +} +---- + +=== Using Custom Marshalers + +Even though it is recommended to use Protobuf message types, it is not mandatory. Traditional +Java types can be used as long as custom marshalers are provided. For instance, in the example +above we can use a `String` type instead of the generated type `StringMessage`, if we create +a marshaler for it. For example, [source,java] ---- include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_2, indent=0] ---- -<1> The `ApplicationScoped` annotation is what makes the service implementation a CDI bean and hence -discoverable. -<2> The `Grpc.GrpcService` annotation is what defines the class as a gRPC service so that when the -bean is discovered, it is then deployed by the gRPC MP server. - -=== Service Name -By default, when a class is annotated with `Grpc.GrpcService`, the class name will be used as the -gRPC service name. So in the example above, the service name will be `StringService`. This can be -changed by supplying a name to the annotation. +In this example, the marshaler is provided using the name `"string"` and its supplier must +be discoverable via CDI. The following is an example of a marshaler and its supplier for the +`String` type: [source,java] ---- +include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_3_5, indent=0] include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_3, indent=0] ---- -<1> The name of the deployed service will be `Strings`. -=== Defining Service Methods +Annotating the supplier with `@Dependent` ensures discoverability provided CDI is configured +to find all annotated beans in the corresponding `beans.xml` file. -- <> +== Implementing a gRPC Extension -Once a class is properly annotated to make it a gRPC MP service, it needs to have service methods that implement the -application business logic. In gRPC there are four different types of method: +When unable to annotate a service class —for example when the code is built by a third party— +another way to deploy non-CDI bean services is to implement a gRPC MP server extension. +Such an extension will be called when the MP server is starting and be given the chance to provide +additional services for deployment. +An extension must implement the `io.helidon.microprofile.grpc.server.spi.GrpcMpExtension` interface. -* `Unary` - a simple method with at most a single request value and returning at most a single response value. -* `Server Streaming` - a method that takes at most a single request value but may return zero or more response values. -* `Client Streaming` - a method that takes one or more request values and returns at most one response value. -* `Bi-directional Streaming` - a method that can take one or more request values and return zero or more response values. +For example, assuming that there is a gRPC service class called `StringService` that needs to +be deployed, a gRPC server extension class might look like this: -The Helidon gRPC MP API determines a method type by its annotation, which should be one of the following: [source,java] ---- -@Grpc.Unary -@Grpc.ServerStreaming -@Grpc.ClientStreaming -@Grpc.Bidirectional +include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_4, indent=0] ---- -==== Request and Response Types - -A gRPC service method typically takes a request parameter and returns a response value (`streaming` methods may take -or return multiple requests or responses). In traditional gRPC Java, the types used for the request and response -values must be Protobuf serializable classes but this is not the case with Helidon gRPC. Helidon supports -pluggable marshallers and, by default, will support Protobuf types. Any type that -can be marshalled by the built-in marshallers or custom supplied marshaller may be used as a request or response type. +<1> The `configure` method of the extension will be called to allow the extension to add extra configuration to +the server. +<2> In this example, an instance of the `StringService` is registered with the routing, as +described in the xref:{rootdir}/se/grpc/server.adoc#_grpc_server_routing[gRPC Server Routing] documentation. + +The `GrpcMpExtension` instances are discovered and loaded using the service loader, so for +this example above to work, a file +`META-INF/services/io.helidon.microprofile.grpc.server.spi.GrpcMpExtension` +would need to be created with the name of the extension shown above. + +== gRPC Reflection Service + +When a gRPC client interacts with a server, it needs to have access to the +Protobuf file to learn about the available services, methods and +message types in use. For many applications, this information is simply +_common knowledge_ between the two parties. However, in some cases, especially +while developing a new service, it is convenient to use tools such as `grpcurl` +or `Postman` to test a service. + +Helidon includes a gRPC reflection service that can be queried by client tools +to learn about the available services —similar to +OpenAPI for REST services. The reflection service is implemented as a +_feature_ and can be enabled programmatically by adding the feature, or +via config as follows: + +[source,yaml] +---- + features: + grpc-reflection: + enabled: true +---- -== Implement a GrpcMpExtension +For more information see xref:{rootdir}/se/grpc/server.adoc#_configuration[gRPC +Server Configuration]. -If it is not possible to annotate the service class (for example the code is built by a third party), another way to -deploy non-CDI bean services is to implement a gRPC MP server extension. -The extension will then be called when the MP server is starting and be given the chance to add additional -services for deployment. -An extension should implement the `io.helidon.microprofile.grpc.server.spi.GrpcMpExtension` interface. -For example, assuming that there was a gRPC service class called `StringService` that needed to be deployed, an -extension class might look like this: +In Helidon MP, annotated services must provide access to the underlying +Protobuf description to use the reflection service. Here is a modified +version of `StringService` that adds an annotated method returning the +descriptor: [source,java] ---- -include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_4, indent=0] +include::{sourcedir}/mp/grpc/GrpcSnippets.java[tag=snippet_1_5, indent=0] ---- -<1> The `configure` method of the extension will be called to allow the extension to add extra configuration to -the server. -<2> In this example, an instance of the `StringService` is registered with the routing (as described in -the xref:{rootdir}/se/grpc/server.adoc#_grpc_server_routing[gRPC server routing] documentation). - -The `GrpcMpExtension` instances are discovered and loaded using the service loader, so for the example above to -work, a file `META-INF/services/io.helidon.microprofile.grpc.server.spi.GrpcMpExtension` would need to be -created that contained the names of the service implementations. +A method annotated by `@Grpc.Proto` must return type `Descriptors.FileDescriptor`; +such a descriptor is available from the Protobuf generated code, in this example, +the `Strings.getDescription()` method. The reflection service shall call this +method to provide service introspection to any clients that support the protocol. == Configuration -Unlike in previous versions of Helidon, gRPC services in Helidon 4 run alongside other services on the -default webserver port. All gRPC services communicate with their corresponding clients using the HTTP/2 -protocol. +At the time of writing, there is no configuration that is specific to +Helidon MP. For more information about gRPC configuration in SE, see +xref:{rootdir}/se/grpc/server.adoc#_configuration[gRPC Server Configuration]. == Examples diff --git a/docs/src/main/asciidoc/se/grpc/client.adoc b/docs/src/main/asciidoc/se/grpc/client.adoc index c2c286f42dc..e024e89575f 100644 --- a/docs/src/main/asciidoc/se/grpc/client.adoc +++ b/docs/src/main/asciidoc/se/grpc/client.adoc @@ -34,6 +34,7 @@ include::{rootdir}/includes/se.adoc[] ** <> ** <> ** <> +** <> - <> == Overview @@ -244,10 +245,45 @@ NOTE: Metrics are only available for gRPC clients running in a server environmen For more information see xref:{rootdir}/se/metrics/metrics.adoc[Helidon Metrics]. +== Tracing + +Tracing in the gRPC client is implemented as a so-called gRPC client service. +To enable tracing support, you need to list tracing as an available service +either programmatically or via config, and +include the following dependency in your project: + +[source,xml] +---- + + io.helidon.webclient + helidon-webclient-grpc-tracing + +---- + +Tracing support is loaded via the gRPC client service SPI and made available to +clients. Using config, we can list it as an available gRPC service as follows: + +[source,yaml] +---- +grpc-client: + grpc-services: + tracing: +---- + +At the time of writing, no additional configuration is necessary under the +`tracing:` section. +Finally, a gRPC client instance can be created that is configured with +tracing support enabled as follows: + +[source,java] +---- +include::{sourcedir}/se/grpc/ClientSnippets.java[tag=snippet_12, indent=0] +---- + == Configuration TLS can be configured externally, just like it is done when using the -WebClient to access an HTTP endpoint. For more information see +WebClient to access an HTTP endpoint. For more information, see https://helidon.io/docs/v4/se/webclient#_configuring_the_webclient[Configuring the WebClient]. There are a few configuration options (see table below) that are specific to a `GrpcClient` and diff --git a/docs/src/main/asciidoc/se/grpc/server.adoc b/docs/src/main/asciidoc/se/grpc/server.adoc index 0c376c58c17..9891709475b 100644 --- a/docs/src/main/asciidoc/se/grpc/server.adoc +++ b/docs/src/main/asciidoc/se/grpc/server.adoc @@ -36,6 +36,7 @@ include::{rootdir}/includes/se.adoc[] - <> ** <> ** <> +** <> - <> == Overview @@ -245,7 +246,9 @@ server: The configuration above shall enable metrics on the Webserver's default port 8080. For more information see xref:{rootdir}/se/metrics/metrics.adoc[Helidon Metrics]. + == Configuration + Configure the gRPC server using the Helidon configuration framework, either programmatically or via a configuration file. @@ -303,6 +306,26 @@ _disabled by default_; if enabled, it is recommended to disable the feature for production to avoid any unwanted requests. For more information about gRPC reflection, see https://grpc.io/docs/guides/reflection/[gRPC Reflection]. +=== Configuring Compression + +gRPC compression is normally driven by client requests, and it can be asymmetric, whereby +the server may use a different type of compression than the client. In some scenarios, such +as when debugging or measuring performance, it may be beneficial to disable compression on +the server side. As always, this can be done programmatically or via config. + +[source,yaml] +---- +server: + port: 8080 + host: 0.0.0.0 + protocols: + grpc: + enable-compression: false +---- + +Compression is always _enabled_ by default in Helidon, but can be disabled +as shown above. + == Examples The following gRPC examples for Helidon SE are available: 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 6e83cb4fa25..49fd9ce52a8 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Oracle and/or its affiliates. + * Copyright (c) 2024, 2025 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. @@ -15,13 +15,20 @@ */ package io.helidon.docs.mp.grpc; +import java.lang.String; +import java.io.InputStream; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Named; import io.grpc.Channel; import io.grpc.stub.StreamObserver; +import io.grpc.MethodDescriptor; import io.helidon.grpc.api.Grpc; +import io.helidon.grpc.core.MarshallerSupplier; import io.helidon.webserver.grpc.GrpcService; import io.helidon.webserver.grpc.GrpcService.Routing; import io.helidon.microprofile.grpc.server.spi.GrpcMpExtension; @@ -39,21 +46,50 @@ class Snippet1 { @Grpc.GrpcService public class StringService { - @Grpc.Unary - public String upper(String s) { - return s == null ? null : s.toUpperCase(); + @Grpc.Unary("Upper") + public Strings.StringMessage upper(Strings.StringMessage request) { + String text = request.getText().toUpperCase(); + return Strings.StringMessage.newBuilder().setText(text).build(); } } // end::snippet_1[] + + + } + + class Snnippet1_5 { + + // tag::snippet_1_5[] + @ApplicationScoped + @Grpc.GrpcService + public class StringService { + + @Grpc.Proto + public Descriptors.FileDescriptor proto() { + return Strings.getDescriptor(); + } + + @Grpc.Unary("Upper") + public Strings.StringMessage upper(Strings.StringMessage request) { + String text = request.getText().toUpperCase(); + return Strings.StringMessage.newBuilder().setText(text).build(); + } + } + // end::snippet_1_5[] } class Snippet2 { // tag::snippet_2[] - @ApplicationScoped // <1> - @Grpc.GrpcService // <2> + @ApplicationScoped + @Grpc.GrpcService + @Grpc.GrpcMarshaller("string") public class StringService { - /* code is omitted */ + + @Grpc.Unary("Upper") + public String upper(String request) { + return request.toLowerCase(); + } } // end::snippet_2[] } @@ -61,12 +97,38 @@ public class StringService { class Snippet3 { // tag::snippet_3[] - @ApplicationScoped - @Grpc.GrpcService("Strings") // <1> - public class StringService { - /* code is omitted */ + public class StringMarshaller + implements MethodDescriptor.Marshaller { + + @Override + public InputStream stream(String obj) { + InputStream stream = null; + // convert to stream + return stream; + } + + @Override + @SuppressWarnings("unchecked") + public String parse(InputStream in) { + String string = null; + // parse from stream + return string; + } + } // end::snippet_3[] + + // tag::snippet_3_5[] + @Dependent + @Named("string") + public class StringSupplier implements MarshallerSupplier { + @Override + public MethodDescriptor.Marshaller get(Class clazz) { + return new StringMarshaller<>(); + } + } + + // end::snippet_3_5[] } class Snippet4 { @@ -85,9 +147,8 @@ public void update(Routing routing) { public class MyExtension implements GrpcMpExtension { @Override - public void configure(GrpcMpContext context) { // <1> - context.routing() - .service(new StringService()); // <2> + public void configure(GrpcMpContext context) { // <1> + context.routing().service(new StringService()); // <2> } } // end::snippet_4[] diff --git a/docs/src/main/java/io/helidon/docs/mp/grpc/Strings.java b/docs/src/main/java/io/helidon/docs/mp/grpc/Strings.java new file mode 100644 index 00000000000..afa3521a7ef --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/grpc/Strings.java @@ -0,0 +1,622 @@ +/* + * Copyright (c) 2025 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. + */ + +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: strings.proto + +// Protobuf Java Version: 3.25.5 +package io.helidon.docs.mp.grpc; + +public final class Strings { + private Strings() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface StringMessageOrBuilder extends + // @@protoc_insertion_point(interface_extends:StringMessage) + com.google.protobuf.MessageOrBuilder { + + /** + * string text = 1; + * @return The text. + */ + java.lang.String getText(); + /** + * string text = 1; + * @return The bytes for text. + */ + com.google.protobuf.ByteString + getTextBytes(); + } + /** + * Protobuf type {@code StringMessage} + */ + public static final class StringMessage extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:StringMessage) + StringMessageOrBuilder { + private static final long serialVersionUID = 0L; + // Use StringMessage.newBuilder() to construct. + private StringMessage(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private StringMessage() { + text_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new StringMessage(); + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.helidon.docs.mp.grpc.Strings.internal_static_StringMessage_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.helidon.docs.mp.grpc.Strings.internal_static_StringMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.helidon.docs.mp.grpc.Strings.StringMessage.class, io.helidon.docs.mp.grpc.Strings.StringMessage.Builder.class); + } + + public static final int TEXT_FIELD_NUMBER = 1; + @SuppressWarnings("serial") + private volatile java.lang.Object text_ = ""; + /** + * string text = 1; + * @return The text. + */ + @java.lang.Override + public java.lang.String getText() { + java.lang.Object ref = text_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + text_ = s; + return s; + } + } + /** + * string text = 1; + * @return The bytes for text. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getTextBytes() { + java.lang.Object ref = text_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + text_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(text_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, text_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(text_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, text_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof io.helidon.docs.mp.grpc.Strings.StringMessage)) { + return super.equals(obj); + } + io.helidon.docs.mp.grpc.Strings.StringMessage other = (io.helidon.docs.mp.grpc.Strings.StringMessage) obj; + + if (!getText() + .equals(other.getText())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + TEXT_FIELD_NUMBER; + hash = (53 * hash) + getText().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static io.helidon.docs.mp.grpc.Strings.StringMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(io.helidon.docs.mp.grpc.Strings.StringMessage prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code StringMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:StringMessage) + io.helidon.docs.mp.grpc.Strings.StringMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return io.helidon.docs.mp.grpc.Strings.internal_static_StringMessage_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return io.helidon.docs.mp.grpc.Strings.internal_static_StringMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + io.helidon.docs.mp.grpc.Strings.StringMessage.class, io.helidon.docs.mp.grpc.Strings.StringMessage.Builder.class); + } + + // Construct using io.helidon.docs.mp.grpc.Strings.StringMessage.newBuilder() + private Builder() { + + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + + } + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + text_ = ""; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return io.helidon.docs.mp.grpc.Strings.internal_static_StringMessage_descriptor; + } + + @java.lang.Override + public io.helidon.docs.mp.grpc.Strings.StringMessage getDefaultInstanceForType() { + return io.helidon.docs.mp.grpc.Strings.StringMessage.getDefaultInstance(); + } + + @java.lang.Override + public io.helidon.docs.mp.grpc.Strings.StringMessage build() { + io.helidon.docs.mp.grpc.Strings.StringMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public io.helidon.docs.mp.grpc.Strings.StringMessage buildPartial() { + io.helidon.docs.mp.grpc.Strings.StringMessage result = new io.helidon.docs.mp.grpc.Strings.StringMessage(this); + if (bitField0_ != 0) { buildPartial0(result); } + onBuilt(); + return result; + } + + private void buildPartial0(io.helidon.docs.mp.grpc.Strings.StringMessage result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.text_ = text_; + } + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof io.helidon.docs.mp.grpc.Strings.StringMessage) { + return mergeFrom((io.helidon.docs.mp.grpc.Strings.StringMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(io.helidon.docs.mp.grpc.Strings.StringMessage other) { + if (other == io.helidon.docs.mp.grpc.Strings.StringMessage.getDefaultInstance()) return this; + if (!other.getText().isEmpty()) { + text_ = other.text_; + bitField0_ |= 0x00000001; + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + text_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + default: { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + private int bitField0_; + + private java.lang.Object text_ = ""; + /** + * string text = 1; + * @return The text. + */ + public java.lang.String getText() { + java.lang.Object ref = text_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + text_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string text = 1; + * @return The bytes for text. + */ + public com.google.protobuf.ByteString + getTextBytes() { + java.lang.Object ref = text_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + text_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string text = 1; + * @param value The text to set. + * @return This builder for chaining. + */ + public Builder setText( + java.lang.String value) { + if (value == null) { throw new NullPointerException(); } + text_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * string text = 1; + * @return This builder for chaining. + */ + public Builder clearText() { + text_ = getDefaultInstance().getText(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * string text = 1; + * @param value The bytes for text to set. + * @return This builder for chaining. + */ + public Builder setTextBytes( + com.google.protobuf.ByteString value) { + if (value == null) { throw new NullPointerException(); } + checkByteStringIsUtf8(value); + text_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:StringMessage) + } + + // @@protoc_insertion_point(class_scope:StringMessage) + private static final io.helidon.docs.mp.grpc.Strings.StringMessage DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new io.helidon.docs.mp.grpc.Strings.StringMessage(); + } + + public static io.helidon.docs.mp.grpc.Strings.StringMessage getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public StringMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public io.helidon.docs.mp.grpc.Strings.StringMessage getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_StringMessage_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_StringMessage_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\rstrings.proto\"\035\n\rStringMessage\022\014\n\004text" + + "\030\001 \001(\t2\276\001\n\rStringService\022)\n\005Upper\022\016.Stri" + + "ngMessage\032\016.StringMessage\"\000\022)\n\005Lower\022\016.S" + + "tringMessage\032\016.StringMessage\"\000\022+\n\005Split\022" + + "\016.StringMessage\032\016.StringMessage\"\0000\001\022*\n\004J" + + "oin\022\016.StringMessage\032\016.StringMessage\"\000(\001B" + + "\'\n%io.helidon.docs.mp.grpc" + + "b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_StringMessage_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_StringMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_StringMessage_descriptor, + new java.lang.String[] { "Text", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/docs/src/main/java/io/helidon/docs/se/grpc/ClientSnippets.java b/docs/src/main/java/io/helidon/docs/se/grpc/ClientSnippets.java index 3bcb663fdc5..f85db7466fb 100644 --- a/docs/src/main/java/io/helidon/docs/se/grpc/ClientSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/grpc/ClientSnippets.java @@ -203,5 +203,25 @@ void snippets3() { .enableMetrics(true) // enables metrics .build(); // end::snippet_11[] + + } + + void snippets4() { + Tls clientTls = Tls.builder() + .trust(trust -> trust + .keystore(store -> store + .passphrase("password") + .trustStore(true) + .keystore(Resource.create("client.p12")))) + .build(); + + // tag::snippet_12[] + Config config = Config.create().get("grpc-client"); + GrpcClient grpcClient = GrpcClient.builder() + .config(config) // with tracing + .tls(clientTls) + .baseUri("https://localhost:8080") + .build(); + // end::snippet_12[] } }