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

    Copyright (c) 2019, 2020 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.

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

= gRPC Interceptors
:pagename: grpc-server-interceptors
:description: Helidon gRPC Service Interceptors
:keywords: helidon, grpc, java

== Interceptors

Helidon gRPC allows you to configure standard `io.grpc.ServerInterceptor`s.

For example, you could implement an interceptor that logs each RPC call:

[source,java]
----
class LoggingInterceptor implements ServerInterceptor {   // <1>

    private static final Logger LOG = Logger.getLogger(LoggingInterceptor.class.getName());

    @Override
    public <ReqT, ResT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, ResT> call,
                                                                 Metadata metadata,
                                                                 ServerCallHandler<ReqT, ResT> handler) {

        LOG.info(() -> "CALL: " + call.getMethodDescriptor());  // <2>
        return handler.startCall(call, metadata);               // <3>
    }
}
----

<1> Implement `io.grpc.ServerInterceptor`
<2> Implement the logging logic
<3> Start intercepted call

== Registering Interceptors

You can register interceptors globally, in which case they will be applied to all
methods of all services, by simply adding them to the `GrpcRouting` instance:

[source,java]
----
private static GrpcRouting createRouting(Config config) {
    return GrpcRouting.builder()
            .intercept(new LoggingInterceptor())  // <1>
            .register(new GreetService(config))
            .register(new EchoService())
            .build();
}
----

<1> Adds `LoggingInterceptor` to all methods of `GreetService` and `EchoService`

You can also register an interceptor for a specific service, either by implementing
`GrpcService.update` method:

[source,java]
----
public class MyService implements GrpcService {

    @Override
    public void update(ServiceDescriptor.Rules rules) {
        rules.intercept(new LoggingInterceptor())   // <1>
                .unary("MyMethod", this::myMethod);
    }

    private <ReqT, ResT> void myMethod(ReqT request, StreamObserver<ResT> observer) {
        // do something
    }
}
----

<1> Adds `LoggingInterceptor` to all methods of `MyService`

Or by configuring `ServiceDescriptor` externally, when creating `GrpcRouting`, which
allows you to add interceptors to plain `io.grpc.BindableService` services as well:

[source,java]
----
private static GrpcRouting createRouting(Config config) {
    return GrpcRouting.builder()
            .register(new GreetService(config), cfg -> cfg.intercept(new LoggingInterceptor()))  // <1>
            .register(new EchoService())
            .build();
}
----

<1> Adds `LoggingInterceptor` to all methods of `GreetService` only

Finally, you can also register an interceptor at the method level:

[source,java]
----
public class MyService implements GrpcService {

    @Override
    public void update(ServiceDescriptor.Rules rules) {
        rules.unary("MyMethod",
                     this::myMethod,
                     cfg -> cfg.intercept(new LoggingInterceptor()));  // <1>
    }

    private <ReqT, ResT> void myMethod(ReqT request, StreamObserver<ResT> observer) {
        // do something
    }
}
----

<1> Adds `LoggingInterceptor` to `MyService::MyMethod` only
