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>*

Reply via email to