GH-2873: Preserve mapping order in the router (#2877)

* GH-2873: Preserve mapping order in the router

Fixes https://github.com/spring-projects/spring-integration/issues/2873

Sometime it is important to map to most specific exception instead of
its super class.

* Use `LinkedHashMap` for mapping keys in the
`ErrorMessageExceptionTypeRouter`, as well as in its
`AbstractMappingMessageRouter` superclass.
Since we don't do that internal map modification, there is no reason to
worry about concurrent access: we just replace an internal instance
atomically with a new `LinkedHashMap` every time we modify a mapping
for router

**Cherry-pick to 5.1.x**

* * Fix `RouterSpec.RouterMappingProvider` to `LinkedHashMap` as well

* * Fix `RouterTests` for proper mapping order
* Polishing for `AbstractMappingMessageRouter` hierarchy, so we don't
use `protected channelMappings` field access any more
This commit is contained in:
Artem Bilan
2019-04-01 15:29:39 -04:00
committed by Gary Russell
parent 3fc278688c
commit 23a73fab87
7 changed files with 51 additions and 55 deletions

View File

@@ -750,13 +750,12 @@ See <<xml-xpath-routing>>.
Spring Integration also provides a special type-based router called `ErrorMessageExceptionTypeRouter` for routing error messages (defined as messages whose `payload` is a `Throwable` instance).
`ErrorMessageExceptionTypeRouter` is similar to the `PayloadTypeRouter`.
In fact, they are almost identical.
The only difference is that, while `PayloadTypeRouter` navigates the instance hierarchy of a payload instance (for example, `payload.getClass().getSuperclass()`) to find the most specific type and channel mappings,
the `ErrorMessageExceptionTypeRouter` navigates the hierarchy of 'exception causes' (for example, `payload.getCause()`)
to find the most specific `Throwable` type or channel mappings and uses `mappingClass.isInstance(cause)` to match the
`cause` to the class or any super class.
The only difference is that, while `PayloadTypeRouter` navigates the instance hierarchy of a payload instance (for example, `payload.getClass().getSuperclass()`) to find the most specific type and channel mappings, the `ErrorMessageExceptionTypeRouter` navigates the hierarchy of 'exception causes' (for example, `payload.getCause()`) to find the most specific `Throwable` type or channel mappings and uses `mappingClass.isInstance(cause)` to match the `cause` to the class or any super class.
NOTE: Since version 4.3 the `ErrorMessageExceptionTypeRouter` loads all mapping classes during the initialization
phase to fail-fast for a `ClassNotFoundException`.
IMPORTANT: The channel mapping order in this case matters.
So, if there is a requirement to get mapping for an `IllegalArgumentException`, but not a `RuntimeException`, the last one must be configured on router first.
NOTE: Since version 4.3 the `ErrorMessageExceptionTypeRouter` loads all mapping classes during the initialization phase to fail-fast for a `ClassNotFoundException`.
The following example shows a sample configuration for `ErrorMessageExceptionTypeRouter`: