Last active
January 26, 2023 11:21
-
-
Save rponte/8f23557d7e3079c9e644e620df293420 to your computer and use it in GitHub Desktop.
Micronaut: Implementing a Micronaut AOP Interceptor for exception handling in gRPC endpoints
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<*> | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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 | |
} | |
} |
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
The rest of the code can be seen with this alternative approach of intercepting calls using a gRPC Server Interceptor.