Skip to content

GH-3024 Move Error Handling Docs to the top level #3037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/reference/asciidoc/changes-4.3-5.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ See <<./endpoint.adoc#content-type-conversion,Content Type Conversion>> for more

We added `ErrorMessagePublisher` and the `ErrorMessageStrategy` for creating `ErrorMessage` instances.

See <<./configuration.adoc#namespace-errorhandler,Error Handling>> for more information.
See <<./error-handling.adoc#error-handling,Error Handling>> for more information.

===== JDBC Metadata Store

Expand Down
2 changes: 1 addition & 1 deletion src/reference/asciidoc/channel.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ If namespace support is enabled, two special channels are defined within the app
The 'nullChannel' acts like `/dev/null`, logging any message sent to it at the `DEBUG` level and returning immediately.
Any time you face channel resolution errors for a reply that you do not care about, you can set the affected component's `output-channel` attribute to 'nullChannel' (the name, 'nullChannel', is reserved within the application context).
The 'errorChannel' is used internally for sending error messages and may be overridden with a custom configuration.
This is discussed in greater detail in <<./configuration.adoc#namespace-errorhandler,Error Handling>>.
This is discussed in greater detail in <<./error-handling.adoc#error-handling,Error Handling>>.


See also <<./dsl.adoc#java-dsl-channels,Message Channels>> in the Java DSL chapter for more information about message channel and interceptors.
75 changes: 0 additions & 75 deletions src/reference/asciidoc/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -149,81 +149,6 @@ To do so, define a bean with the appropriate JNDI name for your environment, as

The next section describes what happens if exceptions occur within the asynchronous invocations.

[[namespace-errorhandler]]
=== Error Handling

As described in the <<./overview.adoc#overview,overview>> at the very beginning of this manual, one of the main motivations behind a message-oriented framework such as Spring Integration is to promote loose coupling between components.
The message channel plays an important role, in that producers and consumers do not have to know about each other.
However, the advantages also have some drawbacks.
Some things become more complicated in a loosely coupled environment, and one example is error handling.

When sending a message to a channel, the component that ultimately handles that message may or may not be operating within the same thread as the sender.
If using a simple default `DirectChannel` (when the `<channel>` element that has no `<queue>` child element and no 'task-executor' attribute),
the message handling occurs in the same thread that sends the initial message.
In that case, if an `Exception` is thrown, it can be caught by the sender (or it may propagate past the sender if it is an uncaught `RuntimeException`).
So far, everything is fine.
This is the same behavior as an exception-throwing operation in a normal call stack.

A message flow that runs on a caller thread might be invoked through a messaging gateway (see <<./gateway.adoc#gateway,Messaging Gateways>>) or a `MessagingTemplate` (see <<./channel.adoc#channel-template,`MessagingTemplate`>>).
In either case, the default behavior is to throw any exceptions to the caller.
For the messaging gateway, see <<./gateway.adoc#gateway-error-handling,Error Handling>> for details about how the exception is thrown and how to configure the gateway to route the errors to an error channel instead.
When using a `MessagingTemplate` or sending to a `MessageChannel` directly, exceptions are always thrown to the caller.

When adding asynchronous processing, things become rather more complicated.
For instance, if the 'channel' element does provide a 'queue' child element, the component that handles the message operates in a different thread than the sender.
The same is true when an `ExecutorChannel` is used.
The sender may have dropped the `Message` into the channel and moved on to other things.
There is no way for the `Exception` to be thrown directly back to that sender by using standard `Exception` throwing techniques.
Instead, handling errors for asynchronous processes requires that the error-handling mechanism also be asynchronous.

Spring Integration supports error handling for its components by publishing errors to a message channel.
Specifically, the `Exception` becomes the payload of a Spring Integration `ErrorMessage`.
That `Message` is then sent to a message channel that is resolved in a way that is similar to the 'replyChannel' resolution.
First, if the request `Message` being handled at the time the `Exception` occurred contains an 'errorChannel' header (the header name is defined in the `MessageHeaders.ERROR_CHANNEL` constant), the `ErrorMessage` is sent to that channel.
Otherwise, the error handler sends to a "`global`" channel whose bean name is `errorChannel` (this is also defined as a constant: `IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME`).

A default `errorChannel` bean is created internally by the Framework.
However, you can define your own if you want to control the settings.
The following example shows how to define an error channel backed by a queue with a capacity of 500:

====
[source,xml]
----
<int:channel id="errorChannel">
<int:queue capacity="500"/>
</int:channel>
----
====

NOTE: The default error channel is a `PublishSubscribeChannel`.

The most important thing to understand here is that the messaging-based error handling applies only to exceptions that are thrown by a Spring Integration task that is executing within a `TaskExecutor`.
This does not apply to exceptions thrown by a handler that operates within the same thread as the sender (for example, through a `DirectChannel` as described earlier in this section).

NOTE: When exceptions occur in a scheduled poller task's execution, those exceptions are wrapped in `ErrorMessage` instances and sent to the 'errorChannel' as well.

To enable global error handling, register a handler on that channel.
For example, you can configure Spring Integration's `ErrorMessageExceptionTypeRouter` as the handler of an endpoint that is subscribed to the 'errorChannel'.
That router can then spread the error messages across multiple channels, based on the `Exception` type.

Starting with version 4.3.10, Spring Integration provides the `ErrorMessagePublisher` and the `ErrorMessageStrategy`.
You can use them as a general mechanism for publishing `ErrorMessage` instances.
You can call or extend them in any error handling scenarios.
The `ErrorMessageSendingRecoverer` extends this class as a `RecoveryCallback` implementation that can be used with retry, such as the
<<./handler-advice.adoc#retry-advice,`RequestHandlerRetryAdvice`>>.
The `ErrorMessageStrategy` is used to build an `ErrorMessage` based on the provided exception and an `AttributeAccessor` context.
It can be injected into any `MessageProducerSupport` or `MessagingGatewaySupport`.
The `requestMessage` is stored under `ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY` in the `AttributeAccessor` context.
The `ErrorMessageStrategy` can use that `requestMessage` as the `originalMessage` property of the `ErrorMessage` it creates.
The `DefaultErrorMessageStrategy` does exactly that.

Starting with version 5.2, all the `MessageHandlingException` instances thrown by the framework components, includes a component `BeanDefinition` resource and source to determine a configuration point form the exception.
In case of XML configuration, a resource is an XML file path and source an XML tag with its `id` attribute.
With Java & Annotation configuration, a resource is a `@Configuration` class and source is a `@Bean` method.
In most case the target integration flow solution is based on the out-of-the-box components and their configuration options.
When an exception happens at runtime, there is no any end-user code involved in stack trace because an execution is against beans, not their configuration.
Including a resource and source of the bean definition helps to determine possible configuration mistakes and provides better developer experience.

[[global-properties]]
=== Global Properties

Expand Down
72 changes: 72 additions & 0 deletions src/reference/asciidoc/error-handling.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
[[error-handling]]
== Error Handling

As described in the <<./overview.adoc#overview,overview>> at the very beginning of this manual, one of the main motivations behind a message-oriented framework such as Spring Integration is to promote loose coupling between components.
The message channel plays an important role, in that producers and consumers do not have to know about each other.
However, the advantages also have some drawbacks.
Some things become more complicated in a loosely coupled environment, and one example is error handling.

When sending a message to a channel, the component that ultimately handles that message may or may not be operating within the same thread as the sender.
If using a simple default `DirectChannel` (when the `<channel>` element that has no `<queue>` child element and no 'task-executor' attribute), the message handling occurs in the same thread that sends the initial message.
In that case, if an `Exception` is thrown, it can be caught by the sender (or it may propagate past the sender if it is an uncaught `RuntimeException`).
This is the same behavior as an exception-throwing operation in a normal Java call stack.

A message flow that runs on a caller thread might be invoked through a messaging gateway (see <<./gateway.adoc#gateway,Messaging Gateways>>) or a `MessagingTemplate` (see <<./channel.adoc#channel-template,`MessagingTemplate`>>).
In either case, the default behavior is to throw any exceptions to the caller.
For the messaging gateway, see <<./gateway.adoc#gateway-error-handling,Error Handling>> for details about how the exception is thrown and how to configure the gateway to route the errors to an error channel instead.
When using a `MessagingTemplate` or sending to a `MessageChannel` directly, exceptions are always thrown to the caller.

When adding asynchronous processing, things become rather more complicated.
For instance, if the 'channel' element does provide a 'queue' child element (`QueueChannel` in Java & Annotations Configuration), the component that handles the message operates in a different thread than the sender.
The same is true when an `ExecutorChannel` is used.
The sender may have dropped the `Message` into the channel and moved on to other things.
There is no way for the `Exception` to be thrown directly back to that sender by using standard `Exception` throwing techniques.
Instead, handling errors for asynchronous processes requires that the error-handling mechanism also be asynchronous.

Spring Integration supports error handling for its components by publishing errors to a message channel.
Specifically, the `Exception` becomes the payload of a Spring Integration `ErrorMessage`.
That `Message` is then sent to a message channel that is resolved in a way that is similar to the 'replyChannel' resolution.
First, if the request `Message` being handled at the time the `Exception` occurred contains an 'errorChannel' header (the header name is defined in the `MessageHeaders.ERROR_CHANNEL` constant), the `ErrorMessage` is sent to that channel.
Otherwise, the error handler sends to a "`global`" channel whose bean name is `errorChannel` (this is also defined as a constant: `IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME`).

A default `errorChannel` bean is created internally by the Framework.
However, you can define your own if you want to control the settings.
The following example shows how to define an error channel in XML configuration backed by a queue with a capacity of `500`:

====
[source,xml]
----
<int:channel id="errorChannel">
<int:queue capacity="500"/>
</int:channel>
----
====

NOTE: The default error channel is a `PublishSubscribeChannel`.

The most important thing to understand here is that the messaging-based error handling applies only to exceptions that are thrown by a Spring Integration task that is executing within a `TaskExecutor`.
This does not apply to exceptions thrown by a handler that operates within the same thread as the sender (for example, through a `DirectChannel` as described earlier in this section).

NOTE: When exceptions occur in a scheduled poller task's execution, those exceptions are wrapped in `ErrorMessage` instances and sent to the 'errorChannel' as well.

To enable global error handling, register a handler on that channel.
For example, you can configure Spring Integration's `ErrorMessageExceptionTypeRouter` as the handler of an endpoint that is subscribed to the 'errorChannel'.
That router can then spread the error messages across multiple channels, based on the `Exception` type.

Starting with version 4.3.10, Spring Integration provides the `ErrorMessagePublisher` and the `ErrorMessageStrategy`.
You can use them as a general mechanism for publishing `ErrorMessage` instances.
You can call or extend them in any error handling scenarios.
The `ErrorMessageSendingRecoverer` extends this class as a `RecoveryCallback` implementation that can be used with retry, such as the
<<./handler-advice.adoc#retry-advice,`RequestHandlerRetryAdvice`>>.
The `ErrorMessageStrategy` is used to build an `ErrorMessage` based on the provided exception and an `AttributeAccessor` context.
It can be injected into any `MessageProducerSupport` or `MessagingGatewaySupport`.
The `requestMessage` is stored under `ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY` in the `AttributeAccessor` context.
The `ErrorMessageStrategy` can use that `requestMessage` as the `originalMessage` property of the `ErrorMessage` it creates.
The `DefaultErrorMessageStrategy` does exactly that.

Starting with version 5.2, all the `MessageHandlingException` instances thrown by the framework components, includes a component `BeanDefinition` resource and source to determine a configuration point form the exception.
In case of XML configuration, a resource is an XML file path and source an XML tag with its `id` attribute.
With Java & Annotation configuration, a resource is a `@Configuration` class and source is a `@Bean` method.
In most case the target integration flow solution is based on the out-of-the-box components and their configuration options.
When an exception happens at runtime, there is no any end-user code involved in stack trace because an execution is against beans, not their configuration.
Including a resource and source of the bean definition helps to determine possible configuration mistakes and provides better developer experience.
3 changes: 3 additions & 0 deletions src/reference/asciidoc/index-single.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ The appendices cover advanced topics and additional resources.

The last appendix covers the history of Spring Integration.

[appendix]
include::./error-handling.adoc[]

[appendix]
include::./spel.adoc[]

Expand Down
1 change: 1 addition & 0 deletions src/reference/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ as single searchable link:index-single.html[html] and link:../pdf/spring-integra
**Appendices** ::

[horizontal]
<<./error-handling.adoc#error-handling,Error Handling>> ::
<<./spel.adoc#spel,Spring Expression Language (SpEL)>> ::
<<./message-publishing.adoc#message-publishing,Message Publishing>> ::
<<./transactions.adoc#transactions,Transaction Support>> ::
Expand Down
2 changes: 1 addition & 1 deletion src/reference/asciidoc/scatter-gather.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ In other cases something like a "`compensation message`" should be considered fo

Every async sub-flow should be configured with a `errorChannel` header for the proper error message sending from the `MessagePublishingErrorHandler`.
Otherwise, an error will be sent to the global `errorChannel` with the common error handling logic.
See <<./configuration.adoc#namespace-errorhandler,Error Handling>> for more information about async error processing.
See <<./error-handling.adoc#error-handling,Error Handling>> for more information about async error processing.

Synchronous flows may use an `ExpressionEvaluatingRequestHandlerAdvice` for ignoring the exception or returning a compensation message.
When an exception is thrown from one of the sub-flows to the `ScatterGatherHandler`, it is just re-thrown to upstream.
Expand Down
2 changes: 1 addition & 1 deletion src/reference/asciidoc/whats-new.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ The `Function<MessageGroup, Map<String, Object>>` strategy has been introduced f
See <<./aggregator.adoc#aggregator-api,Aggregator Programming Model>> for more information.

All the `MessageHandlingException` s thrown in the framework, includes now a bean resource and source for back tracking a configuration part in case no end-user code involved.
See <<./configuration.adoc#namespace-errorhandler,Error Handling>> for more information.
See <<./error-handling.adoc#error-handling,Error Handling>> for more information.

[[x5.2-amqp]]
==== AMQP Changes
Expand Down