| [[ErrorhandlinginCamel-ErrorhandlinginCamel]] |
| = Error handling in Camel |
| |
| Error handling in Camel can roughly be separated into two distinct |
| types: |
| |
| * non transactional |
| * transactional |
| |
| Where non transactional is the most common type that is enabled |
| out-of-the-box and handled by Camel itself. The transaction type is |
| handled by a backing system such as a J2EE application server. |
| |
| [TIP] |
| ==== |
| **Using try ... catch ... finally** |
| |
| Related to error handling is the xref:try-catch-finally.adoc[Try Catch |
| Finally] feature in Camel. |
| ==== |
| |
| [[ErrorhandlinginCamel-Whendoesanerrorhappen]] |
| == When does an error happen |
| |
| An error happens when |
| |
| * any uncaught exception is thrown during routing and processing of |
| messages within Camel |
| |
| TIP: So think of this as a big exception interceptor that catches all |
| exceptions and handles what to do. |
| |
| [[ErrorhandlinginCamel-Nontransactional]] |
| == Non transactional |
| |
| By default Camel uses the non transaction type and orchestrates the |
| error handling during processing and routing. |
| |
| As there isn't a single error handling configuration that suites all |
| uses cases, you should consider altering the default configurations to |
| better suit you needs. |
| |
| [[ErrorhandlinginCamel-Camel1.xdefaulterrorhandler]] |
| == Camel 1.x default error handler |
| |
| In Camel 1.x a global xref:{eip-vc}:eips:dead-letter-channel.adoc[Dead Letter Channel] |
| is setup as the xref:error-handler.adoc[Error Handler] by default. It's |
| configured as: |
| |
| * redeliver up to 6 times |
| * pause 1 second between each redelivery attempt |
| * if all redelivery attempts failed then move exchange into the dead |
| letter queue |
| * the default dead letter queue is a logger that logs the exchange at |
| ERROR level :star: |
| |
| [WARNING] |
| ==== |
| **Dead Letter Queue (*)** |
| |
| A dead letter queue is like a black hole, it will consume the |
| xref:exchange.adoc[Exchange] and the xref:exchange.adoc[Exchange] |
| routing is ended with no indication that it failed. + |
| This works great in the xref:components::jms-component.adoc[JMS] Messaging world where we |
| don't want a bad message to cause endless retries and causing the system |
| to exhaust. The message is said to be poison and thus we want to move it |
| to a dead letter queue so the system can continue to operate and work |
| with the next message. |
| |
| This default does not go well with other transports using in a |
| request/reply messaging style. If the xref:exchange.adoc[Exchange] |
| failed then the original caller will still want a response after the |
| failure. |
| |
| So the bottom line is that you *must* configure and setup the error |
| handling strategies that suits your business needs. |
| ==== |
| |
| [[ErrorhandlinginCamel-Camel2.0onwardsdefaulterrorhandler]] |
| == Camel 2.0 onwards default error handler |
| |
| In Camel 2.0 onwards a global |
| xref:defaulterrorhandler.adoc[DefaultErrorHandler] is set up as the |
| xref:error-handler.adoc[Error Handler] by default. It's configured as: |
| |
| * no redeliveries |
| * no dead letter queue |
| * if the exchange failed an exception is thrown and propagated back to |
| the original caller wrapped in a `RuntimeCamelException`. |
| |
| [[ErrorhandlinginCamel-Scopes]] |
| == Scopes |
| |
| Camel supports 2 scopes that is determined by the DSL in use: |
| |
| [width="100%",cols="25%,25%,25%,25%",options="header",] |
| |======================================================================= |
| |DSL |Scope 1 |Scope 2 |Note |
| |XML DSL |CamelContext |route |Scope 2 takes precedence over scope 1 |
| |
| |Java DSL |RouteBuilder |route |Scope 2 takes precedence over |
| scope 1 |
| |======================================================================= |
| |
| When using XML DSL then scope 1 applies for all routes. Where as when |
| using Java DSL then route 1 only applies for the given RouteBuilder |
| instance. So if you have multiple RouteBuilder's then each route builder |
| has its own scope 1. |
| |
| [TIP] |
| ==== |
| If you want to share scope among RouteBuilder's you can use class |
| inheritance and create a base class, and then extend this class for your |
| RouteBuilder's and invoke the super.configure() method. |
| ==== |
| |
| Mind that there was a bug in Camel that affected the scopes when using |
| multiple RouteBuilder classes. See more details at |
| https://issues.apache.org/jira/browse/CAMEL-5456[CAMEL-5456]. |
| |
| //NOTE: normal attribute replacement doesn't seem to work in xrefs in section headers. |
| [[ErrorhandlinginCamel-Howdoestheerrorhandlerwork]] |
| == How does the xref:latest@components:eips:dead-letter-channel.adoc[Dead Letter Channel] error handler work |
| |
| When Camel is started it will inspect the routes and weave in the error |
| handling into the routing. With up to 3 supported scopes, the error |
| handling can be quite complex. And on top of that you have inherited |
| error handling and you can even configure |
| xref:exception-clause.adoc[Exception Clause]s to handle specific |
| exception types differently. So yes it's advanced but very powerful when |
| you get the grip of it. |
| |
| To keep things simple we first look at the basic concept how Camel |
| orchestrates the redelivery attempt. At any given node in the route |
| graph Camel intercepts the current Exchange being routed and wraps it |
| with the xref:error-handler.adoc[Error Handler]. This ensures that the |
| xref:error-handler.adoc[Error Handler] can kick in, just as the AOP |
| around concept. If the exchange can be routed without any problems then |
| it's forwarded to the next node in the route graph, *But* if there was |
| an exception thrown, then the xref:error-handler.adoc[Error Handler] |
| kicks in and decides what to do. |
| |
| An example illustrating this: |
| |
| [source,java] |
| ---- |
| errorHandler(deadLetterChannel("jms:queue:dead")); |
| |
| from("seda:newOrder") |
| .to("bean:validateOrder") |
| .to("bean:storeOrder") |
| .to("bean:confirmOrder"); |
| ---- |
| |
| In this route we have 3 nodes (the dots) where the |
| xref:error-handler.adoc[Error Handler] is watching us (The AOP around |
| stuff). So when an order arrives on the seda queue we consume it and |
| send it to the validateOrder bean. In case the validation bean processed |
| ok, we move on to the next node. In case the storeOrder bean failed and |
| throws an exception it's caught by the |
| xref:{eip-vc}:eips:dead-letter-channel.adoc[Dead Letter Channel] that decides what to |
| do next. Either it does a: |
| |
| * redeliver |
| * or move it to dead letter queue |
| |
| It will continue to do redeliveries based on the policy configured. By |
| default xref:{eip-vc}:eips:dead-letter-channel.adoc[Dead Letter Channel] will attempt |
| at most 6 redeliveries with 1 second delay. So if the storeOrder bean |
| did succeed at the 3rd attempt the routing will continue to the next |
| node the confirmOrder bean. In case all redeliveries failed the Exchange |
| is regarded as failed and is moved to the dead letter queue and the |
| processing of this exchange stops. By default the dead letter queue is |
| just a ERROR logger. |
| |
| [NOTE] |
| ==== |
| This applies to all kind of xref:component.adoc[Components] in Camel. |
| The sample above only uses xref:components::bean-component.adoc[Bean] but it's the same for |
| xref:components::file-component.adoc[File], xref:components::mail-component.adoc[Mail], |
| xref:components::velocity-component.adoc[Velocity] or whatever component you use. |
| ==== |
| |
| [[ErrorhandlinginCamel-Transactional]] |
| == Transactional |
| |
| Camel leverages Spring transactions. Usually you can only use this with |
| a limited number of transport types such as JMS or JDBC based, that yet |
| again requires a transaction manager such as a Spring transaction, a |
| J2EE server or a Message Broker. |
| |
| [[ErrorhandlinginCamel-Howdoesitwork]] |
| == How does it work |
| |
| *Camel 1.x* |
| |
| Camel does the same weaving as for the non-transactional type. The |
| difference is that for transactional exchanges the |
| xref:error-handler.adoc[Error Handler] does *not* kick in. You can say |
| the AOP around does not apply. Camel relies solely on the backing system |
| to orchestrate the error handling. And as such the when the backing |
| system does redeliver it will start all over again. For instance if the |
| exchange was started by a JMS consumer then it's started again as the |
| JMS message is rolled back on the JMS queue and Camel will re consume |
| the JMS message again. |
| |
| *Camel 2.0* |
| |
| In Camel 2.0 we have empowered the |
| xref:transactionerrorhandler.adoc[TransactionErrorHandler] to build on |
| top of the same base that |
| xref:defaulterrorhandler.adoc[DefaultErrorHandler] does. This allows you |
| to use Camel redelivery with transactional routes as well. The Spring |
| transaction manager is still in charge and have the last say. But you |
| can use Camel to do some local redelivery, for instance to upload a file |
| to a FTP server, in which Camel can do local redelivery. So this gives |
| you the power from both worlds. In case Camel cannot redeliver the |
| exchange will be failed and rolled back. By default the |
| xref:transactionerrorhandler.adoc[TransactionErrorHandler] does *not* |
| attempt any local redeliveries. You have to configure it to do so, for |
| instance to set a maximum redelivers to a number > 0. |
| |
| See xref:{eip-vc}:eips:transactional-client.adoc[Transactional Client] |
| for more. |
| |