0
votes

I have a fairly simple axon application that Im trying to apply some generic "catch all" exception handling logic to.

If I have a command that goes into an aggregate that throws some kind of exception e.g.

class UserAggregate {
    //... 
        
    @CommandHandler()
    public void on(CreateUserCommand cmd) {
       Validate.notNull(cmd.getEmail(), "Email cannot be null");

       //other processing
   }
}

Then when I invoke this command from the Rest Controller, then the exception is far away from what I would expect

org.axonframework.commandhandling.CommandExecutionException: Email cannot be null at org.axonframework.axonserver.connector.ErrorCode.lambda$static$10(ErrorCode.java:88) at org.axonframework.axonserver.connector.ErrorCode.convert(ErrorCode.java:182) at org.axonframework.axonserver.connector.command.CommandSerializer.deserialize(CommandSerializer.java:157) at org.axonframework.axonserver.connector.command.AxonServerCommandBus$1.onNext(AxonServerCommandBus.java:313) at org.axonframework.axonserver.connector.command.AxonServerCommandBus$1.onNext(AxonServerCommandBus.java:306) at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onMessage(ClientCalls.java:429) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInternal(ClientCallImpl.java:596) at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:581)

Granted the message is useful, however, this is not always a given. This can be mitigated when implementing an ExceptionHandler like so

@ExceptionHandler
public void handle(Exception exception) {
   log.info("Caught Exception - {}", exception.getMessage(), exception);
}

This now gives me a stack trace pinpointing where the issue actually came from, however, this comes at the cost of having to write such an ExceptionHandler everywhere I would like to invoke this command.

Is there a more generic way to log these exceptions without having to impose the ExceptionHandler on every class issueing commands?

1

1 Answers

3
votes

I would point you to the code-samples repo, where it shows a way of handling it using a MessageHandlerInterceptor.

Quoting from the repo itself:

One of the most common ways to indicate that a logical error has occurred and that Command handling failed is to throw an exception from a Command handler. However, if the exception is directly serialized there is no guarantee that the command sending side can properly deserialize the exception in question. That is why by default Axon Framework will wrap any exception thrown inside a Command handler into a CommandExecutionException.

In short, you are going to define a MessageHandlerInterceptor responsible to try-catch the command execution logic. There you would have your centralized way of handling the CommandExecutionException.