Skip to content

Instantly share code, notes, and snippets.

@rponte
Last active January 26, 2023 11:21
Show Gist options
  • Save rponte/8f23557d7e3079c9e644e620df293420 to your computer and use it in GitHub Desktop.
Save rponte/8f23557d7e3079c9e644e620df293420 to your computer and use it in GitHub Desktop.
Micronaut: Implementing a Micronaut AOP Interceptor for exception handling in gRPC endpoints
package br.com.zup.edu.shared.grpc
import io.grpc.BindableService
import io.grpc.stub.StreamObserver
import io.micronaut.aop.MethodInterceptor
import io.micronaut.aop.MethodInvocationContext
import org.slf4j.LoggerFactory
import javax.inject.Inject
import javax.inject.Singleton
/**
* Class responsible for intercepting gRPC Endpoints and handling any exception thrown by their methods
*/
@Singleton
class ExceptionHandlerInterceptor(@Inject private val resolver: ExceptionHandlerResolver) : MethodInterceptor<BindableService, Any?> {
private val logger = LoggerFactory.getLogger(this::class.java)
override fun intercept(context: MethodInvocationContext<BindableService, Any?>): Any? {
try {
return context.proceed()
} catch (e: Exception) {
logger.error("Handling the exception '${e.javaClass.name}' while processing the call: ${context.targetMethod}", e)
val handler = resolver.resolve(e)
val status = handler.handle(e)
GrpcEndpointArguments(context).response()
.onError(status.asRuntimeException())
return null
}
}
/**
* Represents the endpoint method arguments
*/
private class GrpcEndpointArguments(val context : MethodInvocationContext<BindableService, Any?>) {
fun response(): StreamObserver<*> {
return context.parameterValues[1] as StreamObserver<*>
}
}
}
package br.com.zup.edu.shared.grpc
import io.micronaut.aop.Around
import io.micronaut.context.annotation.Type
import kotlin.annotation.AnnotationTarget.*
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(CLASS, FILE, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Around
@Type(ExceptionHandlerInterceptor::class)
annotation class ErrorHandler()
@ErrorHandler // enables the interceptor for all methods
@Singleton
class KeyManagerEndpoint() : KeymanagerGrpcServiceGrpc.KeymanagerGrpcServiceImplBase() {
override fun registra(
request: RegistraChavePixRequest,
responseObserver: StreamObserver<RegistraChavePixResponse>
) {
throw IllegalArgumentException("Invalid request parameters"); // it will be handled as Status.INVALID_ARGUMENT by default
}
}
@rponte
Copy link
Author

rponte commented Feb 21, 2021

The rest of the code can be seen with this alternative approach of intercepting calls using a gRPC Server Interceptor.

@rponte
Copy link
Author

rponte commented Mar 10, 2021

Micronaut v2.4 changed the way it configures interceptors. Now, the interceptor class is responsible for setting which annotation(s) it works with via @InterceptorBean

From 2.4.x onwards the recommending way to define AOP advise is to use the @InterceptorBinding annotation on the annotation you wish to trigger AOP advise

@Singleton
@InterceptorBean(NotNull.class) // now this interceptor can handle many annotations
public class NotNullInterceptor implements MethodInterceptor<Object, Object> {

     // ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment