Search code examples
javaerror-handlingapache-cameltransactional

Camel exception/error handling transacted route without causing client exception


I am working with a command-event driven system using JMS and Apache Camel for for routing. In the following situation:

  • I send the system a request-reply command "X".

  • The system receives "X" via a transacted camel route.

  • While processing "X" the system sends out several events, "Y" & "Z", but as its part of the transacted route these should not be flushed until the transaction has completed.

  • An runtime exception occurs - which should cause the transaction to rollback.

I want to be able to intercept the exception and reply with a genuine message (rather than exception) to the client. As such I started to implement an error handler:

    onException(RuntimeException.class)
        .handled(true)
        .markRollbackOnly()
        .filter(header(Header.REPLY_TO.getName()).isNotNull())
        .to(DESTINATION_FOR_EXCEPTION_HANDLING)
        .to(DESTINATION_FOR_REPLIES);

Where:

  • DESTINATION_FOR_EXCEPTION_HANDLING is a bean that takes the exception and returns a message object
  • DESTINATION_FOR_REPLIES is a bean that sets the out body to the message object

The problem I have is that if I include "markRollbackOnly()" it:

  • Prevents "Y" & "Z" from being flushed - GOOD
  • Causes an exchange exception on the client making the requestReply - BAD

And If i don't include it, then:

  • "Y" & "Z" get flushed - BAD
  • I receive the genuine message object on the client - GOOD

How can I configure camel to both prevent the flushing of messages in the transaction, and yet be able to convert the exception to a handled error message?


Solution

  • I tried Claus approach but couldn't get it to work for some reason, I must have misunderstood or setup something incorrectly.

    Eventually I solved this by propagating a second transaction internally, one the error handler I could then "markRollbackOnlyLast" the second transaction, but respond with a "good" message on the primary transaction:

        TransactionTemplate newTransactionTemplate = new TransactionTemplate(platformTransactionManager);
        newTransactionTemplate.setPropagationBehavior(PROPAGATION_REQUIRES_NEW);
        Policy requireNewTransaction = new SpringTransactionPolicy(newTransactionTemplate);
    
        onException(RuntimeException.class)
            .onWhen(header(Header.REPLY_TO.getName()).isNotNull())
                .log(LoggingLevel.ERROR, EXCEPTION_STACKTRACE)
                .to(PROCESSOR_FOR_EXCEPTION_HANDLING)
                .to(PROCESSOR_FOR_REPLY)
                .handled(true)
                .markRollbackOnlyLast();
    
        from(FROM)
            .policy(requireNewTransaction)...