Search code examples
javaapache-camelactivemq-classicdead-letter

Configuring Java/Camel/AMQ with route-specific DLQs


Java 8/Camel 2.19.x/AMQ 5.15.x here.

I have a Java app that uses Camel to consume messages off of AMQ queues, process those messages, and do stuff with them. Sometimes the output of a route is putting the processing result back up on another queue for further downstream processing, but not always/necessarily. Typical Java/Camel/AMQ setup.

Each of my routes (I'm using the Camel XML DSL) has a configured <onException> handler, which typically looks like this:

<onException useOriginalMessage="true">
  <exception>java.lang.Exception</exception>

  <redeliveryPolicy logStackTrace="true"/>

  <handled>
    <constant>true</constant>
  </handled>

  <log message="${exception.stacktrace}" loggingLevel="ERROR"/>

  <rollback markRollbackOnly="true"/>

</onException>

So pretty simple: log the exception and rollback.

What I would like to do is, as part of this <onException> handler, to place the original message (that failed and caused the exception to be thrown, not the exception!) on a route-specific DLQ (by "DLQ" I mean just a queue where failed messages can be sent for auditing/reporting/playback purposes)

Meaning, if my app has 30 routes that each consume from 30 different AMQ queues, I would have 30 different "DLQs" where each of their respective <onException> handlers would send the failing messages to.

Ideally I'd like this configuration to be on the AMQ side (again perhaps inside of activem.xml or similar) so that I don't need to make code changes or redeploys if the DLQ destinations need to change. But if its only possible to do from inside the Camel route/configs, that's fine too.

I suppose I could modify each route to contain its own customized destination DLQ for the original messages:

<onException useOriginalMessage="true">
  <exception>java.lang.Exception</exception>

  <redeliveryPolicy logStackTrace="true"/>

  <handled>
    <constant>true</constant>
  </handled>

  <log message="${exception.stacktrace}" loggingLevel="ERROR"/>

  <rollback markRollbackOnly="true"/>

  <to uri="activemq:fizzbuzz.dlq"/>

</onException>

But I'm hoping for something more elegant than this...

Any ideas how I can do this?


Solution

  • May be this way will be better for you:

                 DeadLetterChannelBuilder errorHandlerBuilder = deadLetterChannel("jms:dummy");
        errorHandlerBuilder.onPrepareFailure(exchange -> {
            exchange.getIn().setHeader("CamelJmsDestinationName",exchange.getIn().getHeader("JMSDestination",String.class).concat(".DLQ"));
        });
    
        from("jms:input1")
                .to("seda:process");
    
        from("jms:input2")
                .to("seda:process");
    
        from("jms:input3")
                .to("seda:process");
    
        from("seda:process").errorHandler(errorHandlerBuilder)
                .process(exchange -> {
                    throw new RuntimeException();
                });
    

    You can compute DLQ queue name in runtime. DeadLetterChannelBuilder can be configured like your onException as well.