16. Inbound Messaging Gateways

16.1 GatewayProxyFactoryBean

Working with Objects instead of Messages is an improvement. However, it would be even better to have no dependency on the Spring Integration API at all - including the gateway class. For that reason, Spring Integration also provides a GatewayProxyFactoryBean that generates a proxy for any interface and internally invokes the gateway methods shown above. Namespace support is also provided as demonstrated by the following example.

<gateway id="fooService"
         service-interface="org.example.FooService"
         default-request-channel="requestChannel"
         default-reply-channel="replyChannel"/>

Then, the "fooService" can be injected into other beans, and the code that invokes the methods on that proxied instance of the FooService interface has no awareness of the Spring Integration API. The general approach is similar to that of Spring Remoting (RMI, HttpInvoker, etc.). See the "Samples" Appendix for an example that uses this "gateway" element (in the Cafe demo).

The reason that the attributes on the 'gateway' element are named 'default-request-channel' and 'default-reply-channel' is that you may also provide per-method channel references by using the @Gateway annotation.

 public interface Cafe {

     @Gateway(requestChannel="orders")
     void placeOrder(Order order);

 }

... as well as method sub element if yuo prefer XML configuration (see next paragraph)

It is also possible to pass values to be interpreted as Message headers on the Message that is created and sent to the request channel by using the @Header annotation:

 public interface FileWriter {

     @Gateway(requestChannel="filesOut")
     void write(byte[] content, @Header(FileHeaders.FILENAME) String filename);

 }

If you prefer XML way of configuring Gateway methods, you can provide method sub-elements to the gateway configuration (see below)

<si:gateway id="myGateway" service-interface="org.foo.bar.TestGateway"
      default-request-channel="inputC">
  <si:method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/>
      <si:method name="echoUpperCase" request-channel="inputB"/>
      <si:method name="echoViaDefault"/>
</si:gateway>

You can also provide individual headers per method invocation via XML. This could be very useful if the headers you want to set are static in nature and you don't want to embed them in the gateway's method signature via @Header annotations. For example, in the Loan Broker example we want to influence how aggregation of the Loan quotes will be done based on what type of request was initiated (single quote or all quotes). Determining the type of the request by evaluating what gateway method was invoked, although possible would violate the separation of concerns paradigm (method is a java artifact),  but expressing your intention (meta information) via Message headers is natural in a Messaging architecture.

<int:gateway id="loanBrokerGateway"
         service-interface="org.springframework.integration.loanbroker.LoanBrokerGateway">
  <int:method name="getLoanQuote" request-channel="loanBrokerPreProcessingChannel">
    <int:header name="RESPONSE_TYPE" value="BEST"/>
  </int:method>
  <int:method name="getAllLoanQuotes" request-channel="loanBrokerPreProcessingChannel">
    <int:header name="RESPONSE_TYPE" value="ALL"/>
  </int:method>
</int:gateway>

In the above case you can clearly see how a different header value will be set for the 'RESPONSE_TYPE' header based on the gateway's method.

As with anything else, Gateway invocation might result in errors. By default any error that has occurred downstream will be re-thrown as a MessagingExeption (RuntimeException) upon the Gateway's method invocation. However there are times when you may want to treat an Exception as a valid reply, by mapping it to a Message. To accomplish this our Gateway provides support for Exception mappers via the exception-mapper attribute.

<si:gateway id="sampleGateway"
    default-request-channel="gatewayChannel"
    service-interface="foo.bar.SimpleGateway"
    exception-mapper="exceptionMapper"/>

<bean id="exceptionMapper" class="foo.bar.SampleExceptionMapper"/>

        

foo.bar.SampleExceptionMapper is the implementation of org.springframework.integration.message.InboundMessageMapper which only defines one method: toMessage(Object object).

public static class SampleExceptionMapper implements InboundMessageMapper<Throwable>{
  public Message<?> toMessage(Throwable object) throws Exception {
    MessageHandlingException ex = (MessageHandlingException) object;
    return MessageBuilder.withPayload("Error happened in message: " +
                ex.getFailedMessage().getPayload()).build();
  }

}
        

[Important]Important
Exposing messaging system via POJO Gateway is obviously a great benefit, but it does come at the price so there are certain things you must be aware of. We want our Java method to return as quick as possible and not hang for infinite amount of time until they can return (void , exception or return value). When regular methods are used as a proxies in front of the Messaging system we have to take into account the asynchronous nature of the Messaging Systems. This means that there might be a chance that a Message hat was initiated by a Gateway could be dropped by a Filter, thus never reaching a component that is responsible to produce a reply. Some Service Activator method might result in the Exception, thus resulting in no-reply (as we don't generate Null messages).So as you can see there are multiple scenarios where reply message might not be coming which is perfectly natural in messaging systems. However think about the implication on the gateway method.  The Gateway's method input arguments  were incorporated into a Message and sent downstream. The reply Message would be converted to a return value of the Gateway's method. So you can see how ugly it could get if you can not guarantee that for each Gateway call there will alway be a reply Message. Basically your Gateway method will never return and will hang infinitely. (work in progress!!!!) One of the ways of handling this situation is via AsyncGateway (explained later in this section). Another way of handling it is to explicitly set the reply-timeout attribute. This way gateway will not hang for more then the time that was specified by the reply-timout and will return 'null'. 

16.2 Asynchronous Gateway

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. And GatewayProxyFactoryBean provides a convenient way to expose a Proxy over a service-interface thus giving you a 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 "for each request there will always be be a reply".  With Spring Integration 2.0 we are introducing support for an Asynchronous Gateway 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 it to arrive.

A natural way to handle these types of scenarios in Java would be relying upon java.util.concurrent.Future instances, and that is exactly what Spring Integration uses to support an Asynchronous Gateway.

From the XML configuration, there is nothing different and you still define Asynchronous Gateway the same way as a regular Gateway.

<int:gateway id="mathService" 
     service-interface="org.springframework.integration.sample.gateway.futures.MathServiceGateway"
     default-request-channel="requestChannel"/>

However the Gateway Interface (service-interface) is a bit different.

public interface MathServiceGateway {
  Future<Integer> multiplyByTwo(int i);
}

As you can see from the example above the return type for the gateway method is Future. When GatewayProxyFactoryBean sees that the return type of the gateway method is Future, it immediately switches to the async mode by utilizing an AsyncTaskExecutor. That is all. The call to a method always returns immediately with Future encapsulating  the interaction with the framework. Now you can interact with the Future at your own pace to get the result, timeout, get the exception etc...

MathServiceGateway mathService = ac.getBean("mathService", MathServiceGateway.class);
Future<Integer> result = mathService.multiplyByTwo(number);
// do something else here since the reply might take a moment
int finalResult =  result.get(1000, TimeUnit.SECONDS);

For a more detailed example, please refer to the async-gateway sample distributed within the Spring Integration samples.

16.3 Gateway behavior when no response is coming

As it was explained earlier, Gateway provides a convenient way of interacting with Messaging system via POJO method invocations, but realizing that a typical method invocation, which is generally expected to always return (even with Exception), might not always map one-to-one to message exchanges (e.g., reply message might not be coming which is equivalent to method not returning), it is important to go over several scenarios especially in the Sync Gateway case and understand what the default behavior of the Gateway and how to deal with these scenarios to make Sync Gateway behavior more predictable regardless of the outcome of the message flow that was initialed from such Gateway.

There are certain attributes that could be configured to make Sync Gateway behavior more predictable, but some of them might not always work as you might have expected. One of them is reply-timeout. So, lets look at the reply-timeout attribute and see how it can/can't influence the behavior of the Sync Gateway in various scenarios. We will look at single-theraded scenario (all components downstream are connected via Direct Channel) and multi-theraded scenarios (e.g., somewhere downstream you may have Pollable or Executor Channel which breaks single-thread boundary)

Long running process downstream

Sync Gateway - single-threaded. If a component downstream is still running (e.g., infinite loop or a very slow service), then setting reply-timeout has no effect and Gateway method call will not return until such downstream service exits (e.g., return or exception). Sync Gateway - multi-threaded. If a component downstream is still running (e.g., infinite loop or a very slow service), in a multi-threaded message flow setting reply-timeout will have an effect by allowing gateway method invocation to return once the timeout has been reached, since GatewayProxyFactoryBean  will simply poll on the reply channel waiting for a message untill the timeout expires. However it could result in the 'null' return from the Gateway method if the timeout has been reached before the actual reply was produced. It is also important to understand that the reply message (if produced) will be sent to a reply channel after Gateway method invocation might have returned, so you must be aware of that and design your flow with this in mind.

Downstream component returns 'null'

Sync Gateway - single-threaded. If a component downstream returns 'null' and no reply-timeout has been configured, the Gateway method call will hang indefinitely unless: a) reply-timeout has been configured or b) requires-reply attribute has been set on the downstream component (e.g., service-activator) that might return 'null'. In this case, the exception will be thrown and propagated to the Gateway. Sync Gateway - multi-threaded. Behavior is the same as above.

Downstream component return signature is 'void' while Gateway method signature is non-void

Sync Gateway - single-threaded. If a component downstream returns 'void' and no reply-timeout has been configured, the Gateway method call will hang indefinitely unless reply-timeout has been configured  Sync Gateway - multi-threaded Behavior is the same as above.

Downstream component results in Runtime Exception (regardless of the method signature)

Sync Gateway - single-threaded. If a component downstream throws a Runtime Exception, such exception will be propagated via Error Message back to the gateway and re-thrown. Sync Gateway - multi-threaded Behavior is the same as above.

[Important]Important
It is also important to understand that by default reply-timout is unbounded which means that if not explicitly set there are several scenarios (described above) where your Gateway method invocation might hang indefinitely, so make sure you analyze your flow and if there is even a remote possibility of one of these scenarios to occur, set the reply-timout attribute to a 'safe' value or better off set the requires-reply attribute of the downstream component to 'true' to ensure a timely response. But also, realize that there are some scenarios (see the very first one) where reply-timout will not help which means it is also important to analyze your message flow and decide when to use Sync Gateway vs Async Gateway where Gateway method invocation is always guaranteed to return while giving you a more granular control over the results of the invocation via Java Futures.

Also, when dealing with Router you should remember that seeting resolution-required attribute to 'true' will result in the exception thrown by the router if it can not resolve a particular chanel. And when dealing with the filter you can also set throw-exception-on-rejection attribute. Both of these will help to ensure a timely response from the Gateway method invocation.