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:
Gary Russell
2014-08-29 22:16:51 +03:00
committed by Artem Bilan
parent e4eac27b12
commit b64bf03fce
13 changed files with 547 additions and 47 deletions

View File

@@ -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>

View File

@@ -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&lt;Integer&gt; 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>&lt;gateway/&gt;</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&lt;?&gt;</classname></ulink>
return type. The internal <classname>AsyncInvocationTask</classname> is wrapped in a

View File

@@ -11,13 +11,13 @@
</para>
<section id="4.1-new-components">
<title>New Components</title>
<section id="4.1-promise-gateway">
<title>Promise&lt;?&gt; 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&lt;?&gt; 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>