So, in the ongoing perfect transaction configuration we have an interesting use case: Consider the following route in a test:
@Override protected RouteBuilder createRouteBuilder() { System.out.println("createRouteBuilder"); return new RouteBuilder(this.context) { @Override public void configure() { getContext().setTracing(true); from(DIRECT_FEED_INBOX).routeId(ROUTE_ID_FEED) .transacted(TxnHelper.KEY_TXNPOLICY_REQUIRED) .split(body()).marshal(dfCaseRecord).to(endpointAMQ(QUEUE_INBOX)); from(endpointAMQ(QUEUE_OUTBOX)).routeId(ROUTE_ID_RESULTS) .transacted(TxnHelper.KEY_TXNPOLICY_REQUIRED) .unmarshal(dfCaseRecord).to(MOCK_END); from(endpointAMQ(QUEUE_DEAD)).routeId(ROUTE_ID_DEAD) .transacted(TxnHelper.KEY_TXNPOLICY_REQUIRED) .unmarshal(dfCaseRecord).to(MOCK_DEAD); from(endpointAMQ(QUEUE_INBOX)).routeId(ROUTE_ID_TEST_ROUTE) .onException(RuntimeException.class).handled(true).useOriginalMessage().to(endpointAMQ(QUEUE_DEAD)).end() .transacted(TxnHelper.KEY_TXNPOLICY_REQUIRED) .unmarshal(dfCaseRecord) .to(MOCK_BEFORE_TO_QUEUE) .marshal(dfCaseRecord) .to(endpointAMQ(QUEUE_OUTBOX)) .unmarshal(dfCaseRecord) .to(MOCK_AFTER_TO_QUEUE); } }; } Note that the transacted(TxnHelper.KEY_TXNPOLICY_REQUIRED) simply looks up the transaction policy by name as it is just a string key for our JNDI registry. Our test case looks like the following. @Test public void testRollbackAfterEnqueue() throws Exception { adviceRoute(); startCamelContext(); initMocks(); final RecordList cases = casesA(); mockEnd.expectedMessageCount(2); mockEnd.expectedBodiesReceived(cases.get(1), cases.get(2)); mockDead.expectedMessageCount(1); mockDead.expectedBodiesReceived(cases.get(0)); mockBeforeToQueue.expectedMessageCount(3); mockBeforeToQueue.expectedBodiesReceivedInAnyOrder(cases); mockOutbox.expectedMessageCount(3); mockOutbox.expectedBodiesReceivedInAnyOrder(toListOfJsonStrings(mapper, cases)); mockAfterToQueue.expectedMessageCount(3); mockAfterToQueue.expectedBodiesReceivedInAnyOrder(cases); mockAfterToQueue.whenExchangeReceived(1, EXCEPTION_PROCESSOR); template.sendBody(DIRECT_FEED_INBOX, cases); mockBeforeToQueue.assertIsSatisfied(); mockAfterToQueue.assertIsSatisfied(); mockEnd.assertIsSatisfied(); mockDead.assertIsSatisfied(); mockOutbox.assertIsSatisfied(); } In this route the goal is that if any exceptions are thrown even after the message is enqueued, it should be rolled back, the message that excepted should go to the DLQ and the messages in the outbox should be rolled back but the message from the inbox should not be put back on the queue. This is proving to be a bit of a juggling act. I tried putting markRollBackOnly() in the exception handler after the to(dead) but that rolled back the dead letter queue and outbox and then redelivered the inbox message. Removing the markRollbackOnly() means that the message arrives at dead and is off the inbox but it doesn't get removed from the outbox. So I am looking for the right combination of calls to accomplish what I want to do. Any suggestions? Thanks in advance. *Robert Simmons Jr. MSc. - Lead Java Architect @ EA* *Author of: Hardcore Java (2003) and Maintainable Java (2012)* *LinkedIn: **http://www.linkedin.com/pub/robert-simmons/40/852/a39 <http://www.linkedin.com/pub/robert-simmons/40/852/a39>*