Skip to content

Commit

Permalink
gRPC - per-client interceptors
Browse files Browse the repository at this point in the history
- resolves quarkusio#21648
- make the RegisterInterceptor annotaion repeatable
- refactor server interceptors
  • Loading branch information
mkouba committed Dec 2, 2021
1 parent ce19ff7 commit d627156
Show file tree
Hide file tree
Showing 29 changed files with 819 additions and 386 deletions.
30 changes: 29 additions & 1 deletion docs/src/main/asciidoc/grpc-service-consumption.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,19 @@ public class MyService {

== Client Interceptors

You can implement a gRPC client interceptor by implementing an `@ApplicationScoped` bean implementing `io.grpc.ClientInterceptor`:
A gRPC client interceptor can be implemented by a CDI bean that also implements the `io.grpc.ClientInterceptor` interface.
If you want to apply the interceptor to any injected client then annotate the interceptor bean with `@io.quarkus.grpc.GlobalInterceptor`.
You can also register the interceptor per injected client with `@io.quarkus.grpc.RegisterClientInterceptor`.
This annotation is repetable.

.Global Client Interceptor Example
[source, java]
----
import io.quarkus.grpc.GlobalInterceptor;
import io.grpc.ClientInterceptor;
@GlobalInterceptor <1>
@ApplicationScoped
public class MyInterceptor implements ClientInterceptor {
Expand All @@ -309,9 +318,28 @@ public class MyInterceptor implements ClientInterceptor {
}
}
----
<1> This interceptor is applied to all injected gRPC clients.

TIP: Check the https://grpc.github.io/grpc-java/javadoc/io/grpc/ClientInterceptor.html[ClientInterceptor JavaDoc] to properly implement your interceptor.

.`@RegisterClientInterceptor` Example
[source, java]
----
import io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.RegisterClientInterceptor;
import hello.Greeter;
@ApplicationScoped
class MyBean {
@RegisterClinetInterceptor(MySpecialInterceptor.class) <1>
@GrpcClient("helloService")
Greeter greeter;
}
----
<1> Registers the `MySpecialInterceptor` for this particular client.

When you have multiple client interceptors, you can order them by implementing the `javax.enterprise.inject.spi.Prioritized` interface:

[source, java]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.jboss.jandex.DotName;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.grpc.deployment.GrpcClientBuildItem.ClientInfo;
import io.quarkus.grpc.deployment.GrpcClientBuildItem.ClientType;

public final class GrpcClientBuildItem extends MultiBuildItem {

Expand All @@ -23,7 +25,12 @@ public Set<ClientInfo> getClients() {
}

public void addClient(ClientInfo client) {
addClient(client, false);
}

public void addClient(ClientInfo client, boolean addChannel) {
clients.add(client);
clients.add(new ClientInfo(GrpcDotNames.CHANNEL, ClientType.CHANNEL, client.interceptors));
}

public String getClientName() {
Expand All @@ -35,20 +42,22 @@ public static final class ClientInfo {
public final DotName className;
public final ClientType type;
public final DotName implName;
public final Set<String> interceptors;

public ClientInfo(DotName className, ClientType type) {
this(className, type, null);
public ClientInfo(DotName className, ClientType type, Set<String> interceptors) {
this(className, type, null, interceptors);
}

public ClientInfo(DotName className, ClientType type, DotName implName) {
public ClientInfo(DotName className, ClientType type, DotName implName, Set<String> interceptors) {
this.className = className;
this.type = type;
this.implName = implName;
this.interceptors = interceptors;
}

@Override
public int hashCode() {
return Objects.hash(className);
return Objects.hash(className, interceptors);
}

@Override
Expand All @@ -63,13 +72,14 @@ public boolean equals(Object obj) {
return false;
}
ClientInfo other = (ClientInfo) obj;
return Objects.equals(className, other.className);
return Objects.equals(className, other.className) && Objects.equals(interceptors, other.interceptors);
}

}

public enum ClientType {

CHANNEL(null, false),
BLOCKING_STUB("newBlockingStub", true),
MUTINY_STUB("newMutinyStub", false),
MUTINY_CLIENT(null, false);
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package io.quarkus.grpc.deployment;

import java.util.Set;
import java.util.function.BiFunction;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.DotName;

import io.grpc.BindableService;
import io.grpc.Channel;
import io.grpc.ClientInterceptor;
import io.grpc.ServerInterceptor;
import io.grpc.stub.AbstractBlockingStub;
import io.grpc.stub.AbstractStub;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.grpc.GlobalInterceptor;
import io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.GrpcService;
import io.quarkus.grpc.RegisterClientInterceptor;
import io.quarkus.grpc.RegisterInterceptor;
import io.quarkus.grpc.RegisterInterceptors;
import io.quarkus.grpc.runtime.MutinyBean;
Expand Down Expand Up @@ -48,11 +51,15 @@ public class GrpcDotNames {
public static final DotName REGISTER_INTERCEPTOR = DotName.createSimple(RegisterInterceptor.class.getName());
public static final DotName REGISTER_INTERCEPTORS = DotName.createSimple(RegisterInterceptors.class.getName());
public static final DotName SERVER_INTERCEPTOR = DotName.createSimple(ServerInterceptor.class.getName());
public static final DotName REGISTER_CLIENT_INTERCEPTOR = DotName.createSimple(RegisterClientInterceptor.class.getName());
public static final DotName REGISTER_CLIENT_INTERCEPTOR_LIST = DotName
.createSimple(RegisterClientInterceptor.List.class.getName());
public static final DotName CLIENT_INTERCEPTOR = DotName.createSimple(ClientInterceptor.class.getName());

static final MethodDescriptor CREATE_CHANNEL_METHOD = MethodDescriptor.ofMethod(Channels.class, "createChannel",
Channel.class, String.class);
Channel.class, String.class, Set.class);
static final MethodDescriptor RETRIEVE_CHANNEL_METHOD = MethodDescriptor.ofMethod(Channels.class, "retrieveChannel",
Channel.class, String.class);
Channel.class, String.class, Set.class);

static final MethodDescriptor CONFIGURE_STUB = MethodDescriptor.ofMethod(GrpcClientConfigProvider.class,
"configureStub", AbstractStub.class, String.class, AbstractStub.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.grpc.deployment;

import static io.quarkus.grpc.deployment.GrpcDotNames.GLOBAL_INTERCEPTOR;

import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;

final class GrpcInterceptors {

final Set<String> globalInterceptors;
final Set<String> nonGlobalInterceptors;

GrpcInterceptors(Set<String> globalInterceptors, Set<String> nonGlobalInterceptors) {
this.globalInterceptors = globalInterceptors;
this.nonGlobalInterceptors = nonGlobalInterceptors;
}

static GrpcInterceptors gatherInterceptors(IndexView index, DotName interceptorInterface) {
Set<String> globalInterceptors = new HashSet<>();
Set<String> nonGlobalInterceptors = new HashSet<>();

Collection<ClassInfo> interceptorImplClasses = index.getAllKnownImplementors(interceptorInterface);
for (ClassInfo interceptorImplClass : interceptorImplClasses) {
if (Modifier.isAbstract(interceptorImplClass.flags())
|| Modifier.isInterface(interceptorImplClass.flags())) {
continue;
}
if (interceptorImplClass.classAnnotation(GLOBAL_INTERCEPTOR) == null) {
nonGlobalInterceptors.add(interceptorImplClass.name().toString());
} else {
globalInterceptors.add(interceptorImplClass.name().toString());
}
}
return new GrpcInterceptors(globalInterceptors, nonGlobalInterceptors);
}

}
Loading

0 comments on commit d627156

Please sign in to comment.