1
votes

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?

2

2 Answers

1
votes

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.

0
votes

If you want to configure in the ActiveMQ itself, you can also add a specific policyEntry and use wildcards in the deadLetterStrategy like in this example:

Create a dedicated DLQ for all INBOUND queues:

<policyEntry queue="*.INBOUND.>">
    <deadLetterStrategy>
        <individualDeadLetterStrategy processExpired="false" queuePrefix="" queueSuffix=".DLQ" useQueueForQueueMessages="true"/>
    </deadLetterStrategy>
</policyEntry>

In that case, I catch the error of the consumer in all my inbound queues:

XXX.INBOUND.AAA        -> XXX.INBOUND.AAA.DLQ
YYY.INBOUND            -> YYY.INBOUND.DLQ
ZZZ.INBOUND.BBB.CCC    -> ZZZ.INBOUND.BBB.CCC.DLQ

But

NNN.MMM.INBOUND        -> ActiveMQ.DLQ 

as the pattern * does not match the dot . character in NNN.MMM.

You can adjust your pattern to your use-case.

This is very useful for managing consumers not catching properly the exceptions and this is done at ActiveMQ level.