INT-3506 Async Gateway Improvements
JIRA: https://jira.spring.io/browse/INT-3506 JIRA: https://jira.spring.io/browse/INT-3428 Support flows downstream of the gateway that support returning a `Future<?>` payload. Currently, any method that returns a type that is assignable to `Future<?>` runs async and returns a `FutureTask<?>`. This prevents a service-interface method that returns a custom `Future<?>` object from being invoked without wrapping that `Future<?>` in a `FutureTask<?>`. Allow the async-executor to be set to `null` causing any method returning `Future<?>` to run on the calling thread. Add support for `ListenableFuture<?>`. If the return type is a `RunnableFuture`, `ListenableFuture` or `Future`, or exactly a `FutureTask` or `ListenableFutureTask`, run the flow on the executor (if present); otherwise run on the calling thread. INT-3506 Fix Object returnType INT-3506 Polishing - PR Comments Perform dummy invocations of `submit` and `submitListenable` to determine the actual return types so that we can determine at runtime whether the executor will return a type that is compatible with the method return type. If not, run on the caller's thread. Add DEBUG Log If Incompatible Future<?> INT-3506 Add Support for MessagingGateway Add a constant to indicate no executor. Add tests. INT-3506 Polishing and Docs - Docbook - XSD - Change test to send calling thread in payload so we can determine whether we need to return a Future or not; previously relied on the thread name which was brittle. Polishing JavaDocs. Change `amqp.xml` to use `org.springframework.amqp.support.AmqpHeaders` instead of an old one.
This commit is contained in:
committed by
Artem Bilan
parent
e4eac27b12
commit
b64bf03fce
@@ -749,7 +749,7 @@ public Object handle(@Payload String payload, @Header(AmqpHeaders.CHANNEL) Chann
|
||||
generic <code>*</code>, to avoid mapping of <emphasis>request</emphasis> headers to the reply.
|
||||
</para>
|
||||
<para>
|
||||
Class <classname><ulink url="http://static.springsource.org/spring-integration/api/org/springframework/integration/amqp/AmqpHeaders.html">AmqpHeaders</ulink></classname>
|
||||
Class <classname>org.springframework.amqp.support.AmqpHeaders</classname>
|
||||
identifies the default headers that will be used by the
|
||||
<classname>DefaultAmqpHeaderMapper</classname>:
|
||||
</para>
|
||||
|
||||
@@ -442,13 +442,13 @@ of this chapter.
|
||||
<section id="async-gateway">
|
||||
<title>Asynchronous Gateway</title>
|
||||
<para>
|
||||
As a pattern the Messaging Gateway is a very nice way to hide messaging-specific code while still exposing the full capabilities of the
|
||||
As a pattern, the Messaging Gateway is a very nice way to hide messaging-specific code while still exposing the full capabilities of the
|
||||
messaging system. As you've seen, the <classname>GatewayProxyFactoryBean</classname> provides a convenient way to expose a Proxy over a service-interface
|
||||
thus giving you POJO-based access to a messaging system (based on objects in your own domain, or primitives/Strings, etc). But when a
|
||||
gateway is exposed via simple POJO methods which return values it does imply that for each Request message (generated when the method is invoked)
|
||||
there must be a Reply message (generated when the method has returned). Since Messaging systems naturally are asynchronous you may not always be
|
||||
able to guarantee the contract where <emphasis>"for each request there will always be be a reply"</emphasis>.
|
||||
With Spring Integration 2.0 we are introducing support for an <emphasis>Asynchronous Gateway</emphasis> which is a convenient way to initiate
|
||||
With Spring Integration 2.0 we introduced support for an <emphasis>Asynchronous Gateway</emphasis> which is a convenient way to initiate
|
||||
flows where you may not know if a reply is expected or how long will it take for replies to arrive.
|
||||
</para>
|
||||
<para>
|
||||
@@ -462,14 +462,16 @@ of this chapter.
|
||||
service-interface="org.springframework.integration.sample.gateway.futures.MathServiceGateway"
|
||||
default-request-channel="requestChannel"/>]]></programlisting>
|
||||
<para>
|
||||
However the Gateway Interface (service-interface) is a bit different.
|
||||
However the Gateway Interface (service-interface) is a little different:
|
||||
</para>
|
||||
<programlisting language="java">public interface MathServiceGateway {
|
||||
|
||||
Future<Integer> multiplyByTwo(int i);
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para>
|
||||
As you can see from the example above the return type for the gateway method is a <classname>Future</classname>. When
|
||||
As you can see from the example above, the return type for the gateway method is a <classname>Future</classname>. When
|
||||
<classname>GatewayProxyFactoryBean</classname> sees that the
|
||||
return type of the gateway method is a <classname>Future</classname>, it immediately switches to the async mode by utilizing
|
||||
an <classname>AsyncTaskExecutor</classname>. That is all. The call to such a method always returns immediately with a <classname>Future</classname> instance.
|
||||
@@ -483,18 +485,85 @@ int finalResult = result.get(1000, TimeUnit.SECONDS);</programlisting>
|
||||
<ulink url="https://github.com/SpringSource/spring-integration-samples/tree/master/intermediate/async-gateway">
|
||||
<emphasis>async-gateway</emphasis></ulink> sample distributed within the Spring Integration samples.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">ListenableFuture</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
Starting with <emphasis>version 4.1</emphasis>, async gateway methods can also return
|
||||
<interfacename>ListenableFuture</interfacename> (introduced in Spring Framework 4.0). These
|
||||
return types allow you to provide a callback which is invoked when the result is available
|
||||
(or an exception occurs). When the gateway detects this return type, and the task executor
|
||||
(see below) is an <interfacename>AsyncListenableTaskExecutor</interfacename>, the executor's
|
||||
<code>submitListenable()</code> method is invoked.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[ListenableFuture<String> result = this.asyncGateway.async("foo");
|
||||
result.addCallback(new ListenableFutureCallback<Thread>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Thread result) {
|
||||
...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
...
|
||||
}
|
||||
});]]></programlisting>
|
||||
<para><emphasis role="bold">Asynchronous Gateway and AsyncTaskExecutor</emphasis></para>
|
||||
<para>
|
||||
By default <classname>GatewayProxyFactoryBean</classname> uses <classname>org.springframework.core.task.SimpleAsyncTaskExecutor</classname>
|
||||
By default, the <classname>GatewayProxyFactoryBean</classname> uses <classname>org.springframework.core.task.SimpleAsyncTaskExecutor</classname>
|
||||
when submitting internal <classname>AsyncInvocationTask</classname> instances for any gateway method whose
|
||||
return type is <classname>Future.class</classname>. However the <literal>async-executor</literal> attribute in the
|
||||
return type is <classname>Future</classname>. However the <literal>async-executor</literal> attribute in the
|
||||
<literal><gateway/></literal> element's configuration allows you to provide a reference to any implementation of
|
||||
<classname>java.util.concurrent.Executor</classname> available within the Spring application context.
|
||||
</para>
|
||||
<para>
|
||||
The (default) <classname>SimpleAsyncTaskExecutor</classname> supports both
|
||||
<interfacename>Future</interfacename> and <interfacename>ListenableFuture</interfacename>
|
||||
return types, returning <classname>FutureTask</classname> or <classname>ListenableFutureTask</classname>
|
||||
respectively. Even though there is a default executor, it is often useful to provide an external
|
||||
one so that you can identify its threads in logs (when using XML, the thread name is based on the executor's
|
||||
bean name):
|
||||
<para>
|
||||
<programlisting language="java"><![CDATA[@Bean
|
||||
public AsyncTaskExecutor exec() {
|
||||
SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
|
||||
simpleAsyncTaskExecutor.setThreadNamePrefix("exec-");
|
||||
return simpleAsyncTaskExecutor;
|
||||
}
|
||||
|
||||
@MessagingGateway(asyncExecutor = "exec")
|
||||
public interface ExecGateway {
|
||||
|
||||
@Gateway(requestChannel = "gatewayChannel")
|
||||
Future<?> doAsync(String foo);
|
||||
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
If you wish to return a different <interfacename>Future</interfacename>
|
||||
implementation, you can provide a custom executor, or disable the executor altogether and
|
||||
return the <interfacename>Future</interfacename> in the reply message payload from the downstream flow.
|
||||
To disable the executor, simply set it to <code>null</code> in the
|
||||
<classname>GatewayProxyFactoryBean</classname> (<code>setAsyncTaskExecutor(null)</code>). When configuring
|
||||
the gateway with XML, use <code>async-executor=""</code>; when configuring using the
|
||||
<classname>@MessagingGateway</classname> annotation, use:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[@MessagingGateway(asyncExecutor = AnnotationConstants.NULL)
|
||||
public interface NoExecGateway {
|
||||
|
||||
@Gateway(requestChannel = "gatewayChannel")
|
||||
Future<?> doAsync(String foo);
|
||||
|
||||
}]]></programlisting>
|
||||
<important>
|
||||
If the return type is a specific concrete <interfacename>Future</interfacename> implementation
|
||||
or some other subinterface that is not supported by the configured executor, the flow will
|
||||
run on the caller's thread and the flow must return the required type in the reply message
|
||||
payload.
|
||||
</important>
|
||||
<para><emphasis role="bold">Asynchronous Gateway and Reactor Promise</emphasis></para>
|
||||
<para>
|
||||
Starting with <emphasis>version 4.1</emphasis>, the <classname>GatewayProxyFactoryBean</classname> allows the
|
||||
Also starting with <emphasis>version 4.1</emphasis>, the <classname>GatewayProxyFactoryBean</classname> allows the
|
||||
use of a <classname>Reactor</classname> with gateway interface methods, utilizing a
|
||||
<ulink url="https://github.com/reactor/reactor/wiki/Promises"><classname>Promise<?></classname></ulink>
|
||||
return type. The internal <classname>AsyncInvocationTask</classname> is wrapped in a
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
</para>
|
||||
<section id="4.1-new-components">
|
||||
<title>New Components</title>
|
||||
<section id="4.1-promise-gateway">
|
||||
<title>Promise<?> Gateway</title>
|
||||
<para>
|
||||
A Reactor <classname>Promise</classname> return type is now supported for Messaging Gateway methods.
|
||||
See <xref linkend="async-gateway"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-promise-gateway">
|
||||
<title>Promise<?> Gateway</title>
|
||||
<para>
|
||||
A Reactor <classname>Promise</classname> return type is now supported for Messaging Gateway methods.
|
||||
See <xref linkend="async-gateway"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
<section id="4.1-general">
|
||||
<title>General Changes</title>
|
||||
@@ -177,5 +177,15 @@
|
||||
See <xref linkend="syslog-inbound-adapter"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-async-gateway">
|
||||
<title>Async Gateway</title>
|
||||
<para>
|
||||
In addition to the <classname>Promise</classname> return type mentioned above,
|
||||
gateway methods may now return a <classname>ListenableFuture</classname>, introduced
|
||||
in Spring Framework 4.0. You can also disable the async processing in the gateway,
|
||||
allowing a downstream flow to directly return a <classname>Future</classname>.
|
||||
See <xref linkend="async-gateway"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user