///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.

    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.

///////////////////////////////////////////////////////////////////////////////

:javadoc-base-url-api: {javadoc-base-url}?io/helidon/grpc/client
:pagename: grpc-server-client-implementation
:description: Helidon gRPC Client Implementation
:keywords: helidon, grpc, java

= Client Implementation

Helidon gRPC client framework allows you to write gRPC clients to access any gRPC
service implementation. The benefits of using Helidon gRPC Client Framework include:

* It provides a number of helper methods that make client implementation
  significantly simpler.

* It allows you to configure some of the Helidon value-added features, such
  as <<08_security.adoc, security>> and <<07_metrics.adoc, metrics collection and interceptors>>
  down to the method level.

* It allows you to easily specify custom marshaller for requests and
  responses if `protobuf` does not satisfy your needs.

== Client Implementation Basics

* The first step to create a Helidon gRPC client application is to describe the set of methods in the gRPC service. Helidon
gRPC Client Framework (simply called the "Client framework" in the remainder of the document) provides a class called
`ClientServiceDescriptor` to describe the set of methods of a service that the client may invoke.

There are three ways to build and initialize a `ClientServiceDescriptor`.
** The first option is to initialize `ClientServiceDescriptor` using `protoc` generated artifacts like
`BindableService` or `io.grpc.ServiceDescriptor`. This option is possible if the gRPC service
was built using `.proto` file. In this case the set of gRPC methods, their types and
the appropriate marshallers are detected automatically. This is certainly the easiest way to initialize
a `ClientServiceDescriptor`.
** The second option is to programmatically build the `ClientServiceDescriptor`. This option should be
taken if the service was *not* built from protobuf files or if the `protoc` generated artifacts are not
available to the client.
** The third option is to load the method descriptions from a configuration file. (Not yet implemented).

* The next step is to create a gRPC `Channel` to use to communicate with the server.

* Finally, you create an instance of `GrpcServiceClient` passing the `ClientMethodDescriptor` and the `Channel` instances.

== Creating gRPC clients from `protoc` generated artifacts

As mentioned above, the easiest way to create a `ClientServiceDescriptor` is to create it from an `io.grpc.ServiceDescriptor` or
from a `io.grpc.BindableService`. It is fairly trivial to obtain these from a service generated from artifacts generated
from protobuf IDL file.

For this section we will assume the following proto file:

[source, proto]
----
syntax = "proto3";
option java_package = "io.helidon.grpc.client.test";

service StringService {
  rpc Upper (StringMessage) returns (StringMessage) {}                  // (Unary)
  rpc Lower (StringMessage) returns (StringMessage) {}                  // (Unary)
  rpc Split (StringMessage) returns (stream StringMessage) {}           // (Server Streaming)
  rpc Join (stream StringMessage) returns (StringMessage) {}            // (Client Streaming)
  rpc Echo (stream StringMessage) returns (stream StringMessage) {}     // (Bidirectional Streaming)
}

message StringMessage {
  string text = 1;
}
----

If you run it through `protoc` it will generate a class (among other things) called `StringService`.
Assuming that the `StringService` server is running on port 1408, here is how you can create a Helidon gRPC
Client that uses the Client Framework to invoke various types of gRPC methods.

=== Creating and initializing a ClientServiceDescriptor for StringService (generated from `protoc`)

Lets build a class called `ProtoBasedStringServiceClient` that invokes the various types of
gRPC methods that our `StringService` offers.


[source,java]
----
public class ProtoBasedStringServiceClient {

    private GrpcServiceClient client;

    public ProtoBasedStringServiceClient() {
        ClientServiceDescriptor desc = ClientServiceDescriptor
                .builder(StringService.getServiceDescriptor())                  // (1)
                .build();

        Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408)   // (2)
                .usePlaintext().build();

        this.client = GrpcServiceClient.create(channel, desc);                  // (3)
    }

    /**
     * Many gRPC methods take a {@link io.grpc.StreamObserver} as an argument. Lets
     * build a helper class that can be used in our example.
     */
    public static class StringMessageStream<T>                                  // (4)
        implements StreamObserver<T> {

        @Override
        public void onNext(T value) {
            System.out.println("Received : " + value);
        }

        @Override
        public void onError(Throwable t) {
          t.printStracktrace();
        }

        @Override
        public void onCompleted() {
          System.out.println("DONE");
        }
    }
}
----

1. Initialize the builder by specifying the `StringService`'s proto `ServiceDescriptor`. From
the `ServiceDescriptor` the builder detects the service name, the set of method names, and for
each method its type (like Unary, ServerStreaming etc.), the request and response types (and
hence their corresponding Marshallers) etc.

2. We create a `Channel` to the service that is running on `localhost:1408`.

3. Finally, we create our `GrpcServiceClient` by using the above mentioned `ClientServiceDescriptor`
and `Channel`. This `client` reference will be used to invoke various gRPC methods in our
`StringService`

4. We define a static inner class that implements the `io.grpc.StreamObserver` interface. An instance
of this class can be used whereever a `io.grpc.StreamObserver` is required (like server streaming,
bi-directional streaming methods).

=== Invoking a unary method on the StringService

The Client Framework provides many helper methods to invoke gRPC unary methods.

[source,java]
----
public class ProtoBasedStringServiceClient {

    private GrpcServiceClient client;

    public ProtoBasedStringServiceClient() { /* code omitted */ }

    public void invokeUnaryMethod() throws Exception {
        StringMessage input = StringMessage.newBuilder().setText("ABC").build();

        CompletableFuture<String> result = client.unary("Lower", input);        // (1)

        String lcase = client.blockingUnary("Lower", input);                    // (2)

        StringMessageStream stream = new StringMessageStream<StringMessage>();
        client.blockingUnary("Lower", input);                                   // (3)
    }

    public static class StringMessageStream<T> { /* code omitted */ }
}
----

1. This variant of the `unary` API takes the method name and a request object and returns
a `CompletableFuture<Response>` where `<Response>` is the response type. Here we invoke the
`Lower` method passing the input `StringMessage`. This method returns a `CompletableFuture<StringMessage>`
as response thus allowing the client to obtain the result asynchronously.

2. This is simply a wrapper around the above method. This method blocks till the result is available.

3. Here we create invoke the `unary` method by passing the `StringMessageStream` whose `onNext` method
will be called (once) when the result is available.

=== Invoking a client streaming method on the StringService

Lets invoke the `Join` method which causes the server to return a single result *after* the client
has streamed the request values to the server. gRPC API expects the client application to provide
an instance of `io.grpc.StreamObserver` as an argument during the invocation of the client
streaming method.

In order to simplify the task of invoking Client Streaming methods, Helidon Client Framework provides
a couple of methods to invoke gRPC Client Streaming methods. The first variant takes an `Iterable` as
argument which in turn is converted into a `io.grpc.StreamObserver`. The second variant takes a
`io.grpc.StreamObserver` as argument. The first variant can be used if the number of values to be
streamed in small and known a priori.

[source,java]
----
public class ProtoBasedStringServiceClient {

    private GrpcServiceClient client;

    public ProtoBasedStringServiceClient() { /* code omitted */ }

    public void invokeClientStreamingWithIterable() throws Exception {

        String sentence = "A simple invocation of a client streaming method";
        Collection<StringMessage> input = Arrays.stream(sentence.split(" "))        // (1)
                  .map(w -> StringMessage.newBuilder().setText(w).build())
                  .collect(Collectors.toList());

        CompletableFuture<StringMessage> result =
                  grpcClient.clientStreaming("Join", input);                        // (2)
    }

    public void invokeClientStreaming() throws Exception {
        String sentence = "A simple invocation of a client streaming method";
        StringMessageStream responseStream = new StringMessageStream<StringMessage>();
        StreamObserver<StringMessage> clientStream =
                  grpcClient.clientStreaming("Join", responseStream);               // (3)

        for (String word : sentence.split(" ")) {
            clientStream.onNext(StringMessage.newBuilder().setText(word).build());  // (4)
        }
        clientStream.onCompleted();                                                 // (5)
    }

    public static class StringMessageStream<T> { /* code imitted */ }

}
----

1. We prepare the collection that contains the values to be streamed.

2. We call the first variant of the `clientStreaming()` method that takes the
method name and the collection of values to be streamed from the client.
Note: The above helper method is useful if the values to be streamed is fixed and small in number.

3. If the number of values to be streamed is large (or unknown), then it is better to use this
variant of the `clientStreaming()` method that takes a `io.grpc.StreamObserver` as an argument. This
method returns a client stream through which the client can stream (potentially a large number of)
value to the server.

4. Once the client stream is obtained, the client streams the values using the `onNext()` method on the
stream.

5. When all values have been stream, the client invokes the `onCompleted()` method signal that all values
have been streamed from the client.

=== Invoking a server streaming method on the StringService (generated from `protoc`)

Lets invoke the "Split" method which causes the server to stream the results back.

[source,java]
----
public class ProtoBasedStringServiceClient {

    private GrpcServiceClient client;

    public ProtoBasedStringServiceClient() { /* code omitted */ }

    public void invokeServerStreaming() throws Exception {
        String sentence = "This sentence will be split into words and sent back to client";
        StringMessage input = StringMessage.newBuilder().setText(sentence).build();   // (1)

        StringMessageStream<StringMessage> observer = new StringMessageStream<>();    // (2)
        grpcClient.serverStreaming("Split", input, observer);                         // (3)
    }

    public static class StringMessageStream<T> { /* code imitted */ }

}
----

1. We prepare the input `StringMessage` that needs to be  split.

2. We create a `StringMessageStream` which will receive the results streamed from the server.

3. We call the `serverStreaming()` passing the input and the `StringMessageStream` as arguments.
The server sends a stream of words by calling the `onNext()` method on the `StringMessageStream` for
each word.

=== Invoking a bi-directional streaming method on the StringService (generated from `protoc`)

Now lets invoke the `Echo` method in which both the client and the server have to stream
the request and response.

[source,java]
----
public class ProtoBasedStringServiceClient {

    private GrpcServiceClient client;

    public ProtoBasedStringServiceClient() { /* code omitted */ }

    public void invokeBidiStreaming() throws Exception {

        StringMessageStream<StringMessage> observer = new StringMessageStream<>();      // (1)
        StringMessageStream<StringMessage> clientStream = grpcClient
                                .bidiStreaming("Echo", observer);                       // (2)

        String sentence = "Each word will be echoed back to the client by the server";
        for (String word : sentence.split(" ")) {
            clientStream.onNext(StringMessage.newBuilder().setText(word).build());      // (3)
        }
        clientStream.onCompleted();                                                     // (4)
    }

    public static class StringMessageStream<T> { /* code imitted */ }

}
----

1. We create a `StringMessageStream` which will receive the results streamed from the server.

2. We call the `bidiStreaming()` passing the `observer` as argument. The server will
send its results through this stream (basically by calling the `onNext()` on the `observer`).
The method returns a (client) stream which should be used by the client to stream values to the
server.

3. We stream each word in our sentence to the server by calling the `onNext()` method on the
`clientStream`.

4. We call the `onCompleted()` method on the `clientStream` to signal that the client has
streamed all its values.

== Programmatically creating ClientServiceDescriptor for StringService

Assuming that the service is still running on port 1408, lets see how to create our Client
without using the `StringService` 's proto `ServiceDescriptor`.

Since we are *not* going to use the `StringService` 's proto `ServiceDescriptor`, we need to
describe the methods that the client need to invoke. The Helidon client framework provides a
bunch of APIs to easily describe gRPC methods.

For example, to register a unary method, we need to use the `unary` method and configure it to
specify the request and response types.

Other than describing the methods that our client will invoke, the rest of the following
code should be very similar (or same) as the previous section!!

[source,java]
----
public class StringServiceClient {

    public static void main(String[] args) {
        ClientMethodDescriptor lower = ClientMethodDescriptor
                    .unary("StringService", "Lower")                            // (1)
                    .requestType(StringMessage.class)                           // (2)
                    .responseType(StringMessage.class)                          // (3)
                    .build();                                                   // (4)

        ClientMethodDescriptor join = ClientMethodDescriptor
                    .clientStreaming("StringService", "Join")                   // (5)
                    .requestType(StringMessage.class)
                    .responseType(StringMessage.class)
                    .build();

        ClientMethodDescriptor split = ClientMethodDescriptor
                    .serverStreaming("StringService", "Split")                  // (6)
                    .requestType(StringMessage.class)
                    .responseType(StringMessage.class)
                    .build();

        ClientMethodDescriptor echo = ClientMethodDescriptor
                    .bidirectional("StringService", "Echo")                     // (7)
                    .requestType(StringMessage.class)
                    .responseType(StringMessage.class)
                    .build();

        ClientServiceDescriptor serviceDesc = ClientServiceDescriptor           // (8)
                    .builder(StringService.class)
                    .unary(lower)
                    .clientStreaming(join)
                    .serverStreaming(split)
                    .bidirectional(echo)
                    .build();


        Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408)           // (9)
                .usePlaintext().build();

        GrpcServiceClient client = GrpcServiceClient.create(channel, serviceDesc);   // (10)

    }

}
----

1. Use the `unary()` method on `ClientMethodDescriptor` to create a builder for a gRPC unary method.
The service name and the method name ("Lower") are specified.

2. Set the request type of the method to be `StringMessage` (since the `Lower` method takes `StringMessage` as a parameter).

3. Set the response type of the method to be `StringMessage` (since the `Lower` method returns a `StringMessage` as a parameter).

4. Build the `ClientMethodDescriptor`. Note that the return value is a `ClientMethodDescriptor` that contains
the correct Marshallers for the request & response types.

5. Use the `clientStreaming()` method on `ClientMethodDescriptor` to create a builder for a gRPC client streaming method.
The service name and the method name ("Join") are specified.

6. Use the `serverStreaming()` method on `ClientMethodDescriptor` to create a builder for a gRPC server streaming method.
The service name and the method name ("Split") are specified.

7. Use the `bidirectional()` method on `ClientMethodDescriptor` to create a builder for a gRPC Bidi streaming method.
The service name and the method name ("Echo") are specified.

8. Create a `ClientServiceDescriptor` for service named `StringService` and add all our `ClientMethodDescriptor` s.

9. We create a `Channel` to the service that is running on `localhost:1408`.

10. Finally, we create our `GrpcServiceClient` by using the above mentioned `ClientServiceDescriptor`
and `Channel`.

At this point the `client` object can be used to invoke any of the four types of methods we have seen in the
earlier sections!!


== Creating gRPC clients for non protobuf services

If your service is *not* using protobuf for serialization, then the Client framework allows
you to programmatically initialize `ClientMethodDescriptor` and create clients to invoke
methods on the service.

All you have to do is create the set of `ClientMethodDescriptor` s and the `ClientServiceDescriptor` as
described in the previous section, but with one change. Just *do not* to set the request and response types
in the `ClientMethodDescriptor`. That's all!! In fact, there is an API in the `ClientServiceDescriptor`
that makes this even simpler. You can simply pass the method name. For example, to create a client streaming
method called "JoinString" that uses java serialization simply call the `clientStreamin("JoinString")`.

Lets see an example of creating a client for a service that uses Java serialization.

[source,java]
----
public static void main(String[] args) throws Exception {
    ClientServiceDescriptor descriptor = ClientServiceDescriptor.builder(HelloService.class)    // (1)
                                                                .clientStreaming("JoinString")  // (2)
                                                                .build();

    Channel channel = ManagedChannelBuilder.forAddress("localhost", 1408)
                                           .usePlaintext()
                                           .build();

    GrpcServiceClient client = GrpcServiceClient.create(channel, descriptor);

    String sentence = "A simple invocation of a client streaming method";
    Collection<StringMessage> input = Arrays.stream(sentence.split(" "))
                                        .map(w -> StringMessage.newBuilder().setText(w).build())
                                        .collect(Collectors.toList());

  CompletableFuture<StringMessage> result = grpcClient.clientStreaming("Join", input);
}
----

1. Create a `ClientServiceDescriptor` for the `HelloService`.
2. Add the "JoinString" client streaming method to the `ClientServiceDescriptor`. Since  we didn't set
the request or response type (like we did in the previous sections), Java serialization will be used for
Marshalling and Unmarshalling the request and response values.

Note that whether a `ClientServiceDescriptor` is built using protobuf artifacts or is built programmatically,
the same set of APIs provided by the Client Framework can be used to invoke gRPC methods.


