5.4. Error Handling and Rollbacks
Overview
How to roll back a transaction
Runtime exceptions as rollbacks
java.lang.RuntimeException. Java errors, of java.lang.Error type, also trigger transaction rollback. Checked exceptions, on the other hand, do not trigger rollback. Figure 5.3, “Errors and Exceptions that Trigger Rollback” summarises how Java errors and exceptions affect transactions, where the classes that trigger rollback are shaded gray.
Figure 5.3. Errors and Exceptions that Trigger Rollback

The rollback() DSL command
rollback() DSL command, which throws an org.apache.camel.RollbackExchangeException exception. In other words, the rollback() command uses the standard approach of throwing a runtime exception to trigger the rollback.
Example 5.2. Rolling Back an Exception with rollback()
from("file:src/data?noop=true")
.transacted()
.beanRef("accountService","credit")
.choice().when(xpath("/transaction/transfer[amount > 100]"))
.rollback()
.otherwise()
.to("direct:txsmall");
from("direct:txsmall")
.beanRef("accountService","debit")
.beanRef("accountService","dumpTable")
.to("file:target/messages/small");RollbackExchangeException exception thrown by rollback() propagates back to the file endpoint at the start of the route. The File component has a built-in reliability feature that causes it to resend any exchange for which an exception has been thrown. Upon resending, of course, the exchange just triggers another rollback, leading to an infinite loop.
The markRollbackOnly() DSL command
markRollbackOnly() DSL command enables you to force the current transaction to roll back, without throwing an exception. This can be useful in cases where (as in Example 5.2, “Rolling Back an Exception with rollback()”) throwing an exception has unwanted side effects.
rollback() with markRollbackOnly(). This version of the route solves the problem of the infinite loop. In this case, when the amount of the money transfer exceeds 100, the current transaction is rolled back, but no exception is thrown. Because the file endpoint does not receive an exception, it does not retry the exchange, and the failed transactions is quietly discarded.
Example 5.3. Rolling Back an Exception with markRollbackOnly()
from("file:src/data?noop=true")
.transacted()
.beanRef("accountService","credit")
.choice().when(xpath("/transaction/transfer[amount > 100]"))
.markRollbackOnly()
.otherwise()
.to("direct:txsmall");
...How to define a dead letter queue
onException() clause, which enables you to divert the relevant exchange object to a dead-letter queue. When used in the context of transactions, however, you need to be careful about how you define the onException() clause, because of potential interactions between exception handling and transaction handling. Example 5.4, “How to Define a Dead Letter Queue” shows the correct way to define an onException() clause, assuming that you need to suppress the rethrown exception.
Example 5.4. How to Define a Dead Letter Queue
// Java
import org.apache.camel.spring.SpringRouteBuilder;
public class MyRouteBuilder extends SpringRouteBuilder {
...
public void configure() {
onException(IllegalArgumentException.class)
.maximumRedeliveries(1)
.handled(true)
.to("file:target/messages?fileName=deadLetters.xml&fileExist=Append")
.markRollbackOnly(); // NB: Must come *after* the dead letter endpoint.
from("file:src/data?noop=true")
.transacted()
.beanRef("accountService","credit")
.beanRef("accountService","debit")
.beanRef("accountService","dumpTable")
.to("file:target/messages");
}
}onException() is configured to catch the IllegalArgumentException exception and send the offending exchange to a dead letter file, deadLetters.xml (of course, you can change this definition to catch whatever kind of exception arises in your application). The exception rethrow behavior and the transaction rollback behavior are controlled by the following special settings in the onException() clause:
handled(true)—suppress the rethrown exception. In this particular example, the rethrown exception is undesirable because it triggers an infinite loop when it propagates back to the file endpoint (see the section called “The markRollbackOnly() DSL command”). In some cases, however, it might be acceptable to rethrow the exception (for example, if the endpoint at the start of the route does not implement a retry feature).markRollbackOnly()—marks the current transaction for rollback without throwing an exception. Note that it is essential to insert this DSL command after theto()command that routes the exchange to the dead letter queue. Otherwise, the exchange would never reach the dead letter queue, becausemarkRollbackOnly()interrupts the chain of processing.
Catching exceptions around a transaction
onException(), a simple approach to handling exceptions in a transactional route is to use the doTry() and doCatch() clauses around the route. For example, Example 5.5, “Catching Exceptions with doTry() and doCatch()” shows how you can catch and handle the IllegalArgumentException in a transactional route, without the risk of getting trapped in an infinite loop.
Example 5.5. Catching Exceptions with doTry() and doCatch()
// Java
import org.apache.camel.spring.SpringRouteBuilder;
public class MyRouteBuilder extends SpringRouteBuilder {
...
public void configure() {
from("file:src/data?noop=true")
.doTry()
.to("direct:split")
.doCatch(IllegalArgumentException.class)
.to("file:target/messages?fileName=deadLetters.xml&fileExist=Append")
.end();
from("direct:split")
.transacted()
.beanRef("accountService","credit")
.beanRef("accountService","debit")
.beanRef("accountService","dumpTable")
.to("file:target/messages");
}
}file:src/data endpoint) receives the incoming exchanges and performs the exception handling using doTry() and doCatch(). The second segment (from the direct:split endpoint) does all of the transactional work. If an exception occurs within this transactional segment, it propagates first of all to the transacted() command, causing the current transaction to be rolled back, and it is then caught by the doCatch() clause in the first route segment. The doCatch() clause does not rethrow the exception, so the file endpoint does not do any retries and infinite looping is avoided.

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.