INT-3944: Async JMS Outbound Gateway

JIRA: https://jira.spring.io/browse/INT-3944

Polishing (PR comments) and Doc Polish

Polishing - PR Comments

Remove custom `async` boolean in favor of the now public setter.

Add exception to `JmsException` hierarchy.

INT-3944: Polishing

* Rename `asyncReplySupported` just to `async`. As well as its getter and setter. Plus fix docs on the matter
* Polishing for the `JmsOutboundGateway` according PR comments
* Rework `JmsOutboundGateway` to return `AbstractIntegrationMessageBuilder` instead of `Message`
since `AbstractMessageProducingHandler` rebuilds `Message` for a new one to copy headers from request
* Add `getPayload()` and `getHeaders()` to the `AbstractIntegrationMessageBuilder` to make the `Routing Slip`
users happy, when the `AbstractIntegrationMessageBuilder` is pushed to the `Routing Slip path` function
* Fix race condition in the `ChatMessageListeningEndpointTests`: the `stanza` parsing is done in the different Thread.

Doc Polishing (Routing Slip)

Fix timing issue in the `LastModifiedFileListFilterTests`:
`Thread.sleep()` not always reflects the reality.
Switch to the "past simulation" via explicit `File.setLastModified()`
This commit is contained in:
Gary Russell
2016-03-23 10:12:13 -04:00
committed by Artem Bilan
parent 0fd72d2d18
commit 4bfcdb9dfa
24 changed files with 539 additions and 325 deletions

View File

@@ -915,8 +915,10 @@ public class AmqpJavaApplication {
The gateway discussed in the previous section is synchronous, in that the sending thread is suspended until a
reply is received (or a timeout occurs).
Spring Integration _version 4.3_ added this asynchronous gateway, which uses the `AsyncRabbitTemplate` from Spring AMQP.
When a message is sent, the thread returns immediately and the reply is sent on the template's listener container
thread when it is received.
When a message is sent, the thread returns immediately after the send completes, and the reply is sent on the template's
listener container thread when it is received.
This can be useful when the gateway is invoked on a poller thread; the thread is released and is available for other
tasks in the framework.
Configuration for an AMQP Async Outbound Gateway is shown below.

View File

@@ -397,6 +397,23 @@ __NOT ALLOWED__
User-supplied correlation ids are not permitted with a reply listener; the gateway will not initialize with this
configuration.
[[jms-async-gateway]]
==== Async Gateway
Starting with _version 4.3_, you can now specify `async="true"` (or `setAsync(true)`) when configuring the outbound
gateway.
By default, when a request is sent to the gateway, the requesting thread is suspended until the reply is received and
the flow then continues on that thread.
If `async` is true, the requesting thread is released immediately after the send completes, and the reply is returned
(and the flow continues) on the listener container thread.
This can be useful when the gateway is invoked on a poller thread; the thread is released and is available for other
tasks within the framework.
`async` requires a `<reply-listener/>` (or `setUseReplyContainer(true)` when using Java configuration); it also
requires a `correlationKey` (usually `JMSCorrelationID`) to be specified.
If either of these conditions are not met, `async` is ignored.
[[jms-og-attributes]]
==== Attribute Reference
@@ -426,9 +443,10 @@ configuration.
request-destination-name="" <21>
request-pub-sub-domain="" <22>
time-to-live="" <23>
requires-reply=""> <24>
idle-reply-listener-timeout <25>
<int-jms:reply-listener /> <26>
requires-reply="" <24>
idle-reply-listener-timeout="" <25>
async=""> <26>
<int-jms:reply-listener /> <27>
</int-jms:outbound-gateway>
----
@@ -541,7 +559,9 @@ are outstanding).
The container will be started again on the next request.
The stop time is a minimum and may actually be up to 1.5x this value.
<26> When this element is included, replies are received by an asynchronous `MessageListenerContainer` rather than
<26> See <<jms-async-gateway>>.
<27> When this element is included, replies are received by an asynchronous `MessageListenerContainer` rather than
creating a consumer for each reply.
This can be more efficient in many cases.

View File

@@ -28,16 +28,6 @@ In order to provide a quick overview, all available attributes are listed in the
[cols="2,1,1,1,1,1,1", options="header"]
|===
| Attribute
@@ -59,12 +49,6 @@ In order to provide a quick overview, all available attributes are listed in the
| exception type router
| apply-sequence
@@ -76,12 +60,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| default-output-channel
@@ -93,12 +71,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| resolution-required
@@ -110,12 +82,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| ignore-send-failures
@@ -127,12 +93,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| timeout
@@ -144,12 +104,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| id
@@ -161,12 +115,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| auto-startup
@@ -178,12 +126,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| input-channel
@@ -195,12 +137,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| order
@@ -212,12 +148,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| method
@@ -229,12 +159,6 @@ a| image::images/tickmark.png[]
|
| ref
@@ -246,12 +170,6 @@ a| image::images/tickmark.png[]
|
| expression
@@ -263,12 +181,6 @@ a| image::images/tickmark.png[]
|
| header-name
@@ -280,12 +192,6 @@ a| image::images/tickmark.png[]
|
| evaluate-as-string
@@ -297,12 +203,6 @@ a| image::images/tickmark.png[]
|
| xpath-expression-ref
@@ -314,12 +214,6 @@ a| image::images/tickmark.png[]
|
| converter
@@ -331,10 +225,6 @@ a| image::images/tickmark.png[]
|
|===
.Routers Inside of a Chain
@@ -342,15 +232,6 @@ a| image::images/tickmark.png[]
|===
| Attribute
@@ -372,12 +253,6 @@ a| image::images/tickmark.png[]
| exception type router
| apply-sequence
@@ -389,12 +264,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| default-output-channel
@@ -406,12 +275,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| resolution-required
@@ -423,12 +286,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| ignore-send-failures
@@ -440,12 +297,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| timeout
@@ -457,12 +308,6 @@ a| image::images/tickmark.png[]
a| image::images/tickmark.png[]
| id
@@ -474,12 +319,6 @@ a| image::images/tickmark.png[]
|
| auto-startup
@@ -491,12 +330,6 @@ a| image::images/tickmark.png[]
|
| input-channel
@@ -508,12 +341,6 @@ a| image::images/tickmark.png[]
|
| order
@@ -525,12 +352,6 @@ a| image::images/tickmark.png[]
|
| method
@@ -542,12 +363,6 @@ a| image::images/tickmark.png[]
|
| ref
@@ -559,12 +374,6 @@ a| image::images/tickmark.png[]
|
| expression
@@ -576,12 +385,6 @@ a| image::images/tickmark.png[]
|
| header-name
@@ -593,12 +396,6 @@ a| image::images/tickmark.png[]
|
| evaluate-as-string
@@ -610,12 +407,6 @@ a| image::images/tickmark.png[]
|
| xpath-expression-ref
@@ -627,12 +418,6 @@ a| image::images/tickmark.png[]
|
| converter
@@ -644,10 +429,6 @@ a| image::images/tickmark.png[]
|
|===
[IMPORTANT]
@@ -1310,6 +1091,15 @@ This is to avoid the overhead of `EvaluationContext` creation for each `Expressi
It is a simple Java Bean with two properties - `Message<?> request` and `Object reply`.
With this expression implementation, we can specify Routing Slip `path` entries using SpEL (`@routingSlipRoutingPojo.get(request, reply)`, `request.headers[myRoutingSlipChannel]`) avoiding a bean definition for the `RoutingSlipRouteStrategy`.
NOTE: The `requestMessage` argument is always a `Message<?>`; depending on context, the reply object may be a
`Message<?>`, an `AbstractIntegrationMessageBuilder` or an arbitrary application domain object (if, for example,
it is returned by a POJO method invoked by a service activator).
In the first two cases, the usual "message" properties are available (`payload` and `headers`) when using SpEL (or
a Java implementation).
When an arbitrary domain object, these properties are, obviously, not available.
Care should be taken when using routing slips in conjunction with POJO methods if the result is used to determine the
next path.
IMPORTANT: If a _Routing Slip_ is involved in a distributed environment - cross-JVM application, `request-reply` through a Message Broker (e.g.
<<amqp>>, <<jms>>), or persistence `MessageStore` (<<message-store>>) is used in the integration flow, etc., - it is recommended to *not* use _inline_ expressions for the Routing Slip `path`.
The framework (`RoutingSlipHeaderValueMessageProcessor`) converts them to `ExpressionEvaluatingRoutingSlipRouteStrategy` objects and they are used in the `routingSlip` message header.

View File

@@ -114,7 +114,7 @@ The service activator is invoked by the calling thread; this would be some upstr
`SubscribableChannel`, or a poller thread for a `PollableChannel`.
If the service returns a `ListenableFuture<?>` the default action is to send that as the payload of the message sent
to the output (or reply) channel.
Starting with _version 4.3_, you can now set the `async` attribute to true (`setAsyncReplySupported(true)` when using
Starting with _version 4.3_, you can now set the `async` attribute to true (`setAsync(true)` when using
Java configuration).
If the service returns a `ListenableFuture<?>` when this is true, the calling thread is released immediately, and the
reply message is sent on the thread (from within your service) that completes the future.

View File

@@ -48,10 +48,17 @@ See <<imap-seen>> for more information.
==== JMS Changes
===== Header Mapper
The `DefaultJmsHeaderMapper` now maps the standard `correlationId` header as a message property by invoking its
`toString()` method.
See <<jms-header-mapping>> for more information.
===== Async Gateway
The JMS Outbound gateway now has an `async` property.
See <<jms-async-gateway>> for more information.
==== Aggregator Changes
There is a change in behavior when a POJO aggregator releases a collection of `Message<?>` objects; this is rare but if