Skip to content

Commit

Permalink
Merge pull request #34348 from nahguam/global-interceptor-producer-me…
Browse files Browse the repository at this point in the history
…thod

Add support for @GlobalInterceptor on producer methods
  • Loading branch information
cescoffier authored Jun 28, 2023
2 parents e59f6d4 + e431054 commit d25ebbc
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 110 deletions.
17 changes: 17 additions & 0 deletions docs/src/main/asciidoc/grpc-service-consumption.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,23 @@ public class MyInterceptor implements ClientInterceptor {
----
<1> This interceptor is applied to all injected gRPC clients.

It's also possible to annotate a producer method as a global interceptor:

[source, java]
----
import io.quarkus.grpc.GlobalInterceptor;
import jakarta.enterprise.inject.Produces;
public class MyProducer {
@GlobalInterceptor
@Produces
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
}
----

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

.`@RegisterClientInterceptor` Example
Expand Down
17 changes: 17 additions & 0 deletions docs/src/main/asciidoc/grpc-service-implementation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,23 @@ public class MyInterceptor implements ServerInterceptor {
}
----

It's also possible to annotate a producer method as a global interceptor:

[source, java]
----
import io.quarkus.grpc.GlobalInterceptor;
import jakarta.enterprise.inject.Produces;
public class MyProducer {
@GlobalInterceptor
@Produces
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
}
----

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

To apply an interceptor to all exposed services, annotate it with `@io.quarkus.grpc.GlobalInterceptor`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.grpc;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
Expand All @@ -15,7 +16,7 @@
* @see RegisterInterceptor
* @see RegisterClientInterceptor
*/
@Target({ FIELD, PARAMETER, TYPE })
@Target({ FIELD, PARAMETER, TYPE, METHOD })
@Retention(RUNTIME)
public @interface GlobalInterceptor {
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package io.quarkus.grpc.deployment;

import static io.quarkus.grpc.deployment.GrpcDotNames.GLOBAL_INTERCEPTOR;
import static org.jboss.jandex.AnnotationTarget.Kind.CLASS;
import static org.jboss.jandex.AnnotationTarget.Kind.METHOD;

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

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
Expand All @@ -27,6 +31,7 @@ final class GrpcInterceptors {
}

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

Expand All @@ -36,13 +41,46 @@ static GrpcInterceptors gatherInterceptors(IndexView index, DotName interceptorI
|| Modifier.isInterface(interceptorImplClass.flags())) {
continue;
}
if (interceptorImplClass.declaredAnnotation(GLOBAL_INTERCEPTOR) == null) {
nonGlobalInterceptors.add(interceptorImplClass.name().toString());
} else {
if (allGlobalInterceptors.contains(interceptorImplClass.name())) {
globalInterceptors.add(interceptorImplClass.name().toString());
} else {
nonGlobalInterceptors.add(interceptorImplClass.name().toString());
}
}
return new GrpcInterceptors(globalInterceptors, nonGlobalInterceptors);
}

private static Set<DotName> allGlobalInterceptors(IndexView index, DotName interceptorInterface) {
Set<DotName> result = new HashSet<>();
for (AnnotationInstance instance : index.getAnnotations(GLOBAL_INTERCEPTOR)) {
ClassInfo classInfo = classInfo(index, instance.target());
if (isAssignableFrom(index, classInfo, interceptorInterface)) {
result.add(classInfo.name());
}
}
return result;
}

private static ClassInfo classInfo(IndexView index, AnnotationTarget target) {
if (target.kind() == CLASS) {
return target.asClass();
} else if (target.kind() == METHOD) {
return index.getClassByName(target.asMethod().returnType().name());
}
return null;
}

private static boolean isAssignableFrom(IndexView index, ClassInfo classInfo, DotName interceptorInterface) {
if (classInfo == null) {
return false;
}
if (classInfo.interfaceNames().contains(interceptorInterface)) {
return true;
}
if (classInfo.superName() == null) {
return false;
}
return isAssignableFrom(index, index.getClassByName(classInfo.superName()), interceptorInterface);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.grpc.examples.interceptors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.MethodDescriptor;
import io.quarkus.grpc.GlobalInterceptor;

class ClientInterceptors {
@GlobalInterceptor
@ApplicationScoped
static class TypeTarget extends Base {
}

static class MethodTarget extends Base {
}

static class Producer {
@GlobalInterceptor
@Produces
MethodTarget methodTarget() {
return new MethodTarget();
}
}

abstract static class Base implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions options,
Channel next) {
HelloWorldEndpoint.invoked.add(getClass().getName());
return next.newCall(method, options);
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.grpc.examples.interceptors;

import java.util.HashSet;
import java.util.Set;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
Expand All @@ -14,7 +17,7 @@

@Path("/hello")
public class HelloWorldEndpoint {

static Set<String> invoked = new HashSet<>();
@GrpcClient("hello")
GreeterGrpc.GreeterBlockingStub blockingHelloService;

Expand All @@ -24,10 +27,12 @@ public class HelloWorldEndpoint {
@GET
@Path("/blocking/{name}")
public Response helloBlocking(@PathParam("name") String name) {
HeaderClientInterceptor.invoked = false;
invoked.clear();
HelloReply helloReply = blockingHelloService.sayHello(HelloRequest.newBuilder().setName(name).build());

return Response.ok(helloReply.getMessage()).header("intercepted", HeaderClientInterceptor.invoked).build();
return Response.ok(helloReply.getMessage())
.header("interceptors", String.join(",", invoked))
.build();
}

@GET
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.grpc.examples.interceptors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.quarkus.grpc.GlobalInterceptor;

class ServerInterceptors {
@GlobalInterceptor
@ApplicationScoped
static class TypeTarget extends Base {
}

static class MethodTarget extends Base {
}

static class Producer {
@GlobalInterceptor
@Produces
MethodTarget methodTarget() {
return new MethodTarget();
}
}

abstract static class Base implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata metadata,
ServerCallHandler<ReqT, RespT> next) {
HelloWorldEndpoint.invoked.add(getClass().getName());
return next.startCall(call, metadata);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static io.restassured.RestAssured.get;
import static org.assertj.core.api.Assertions.assertThat;

import java.util.Set;

import org.junit.jupiter.api.Test;

import io.restassured.response.Response;
Expand All @@ -12,10 +14,15 @@ class HelloWorldEndpointTestBase {
@Test
public void testHelloWorldServiceUsingBlockingStub() {
Response response = get("/hello/blocking/neo");
String intercepted = response.getHeader("intercepted");
String responseMsg = response.asString();
assertThat(responseMsg).isEqualTo("Hello neo");
assertThat(intercepted).isEqualTo("true");

Set<String> invoked = Set.of(response.getHeader("interceptors").split(","));
assertThat(invoked).containsExactlyInAnyOrder(
"io.quarkus.grpc.examples.interceptors.ClientInterceptors$TypeTarget",
"io.quarkus.grpc.examples.interceptors.ClientInterceptors$MethodTarget",
"io.quarkus.grpc.examples.interceptors.ServerInterceptors$TypeTarget",
"io.quarkus.grpc.examples.interceptors.ServerInterceptors$MethodTarget");

ensureThatMetricsAreProduced();
}
Expand Down

0 comments on commit d25ebbc

Please sign in to comment.