Spring Integrationでサーバにメッセージを送る前にチェックをする方法

### はじめに
Spring Frameworkの記事は結構見かけますが、日本ではメジャーじゃないのか、Spring Integrationの記事がなかなか見つからないので、不定期で投稿したいと思います。（投稿するとは言っていない:sunglasses:）

※この記事で想定しているケースは、`TcpOutBoundGateway`を使ってサーバとTCP通信するケースです。

### 開発環境
- Java 1.8
- STS 3.9.0.RELEASE
- Spring Framework 4.3.5.RELEASE
- Spring Integration 4.3.6.RELEASE

### そもそもTCP通信でメッセージを送る処理はどこ？
`TcpOutboundGateway`の`handleRequestMessage`です。
このメソッドの中で`ClientConnectionFactory`から取得した`TcpConnection`を使ってサーバにメッセージを送っています。

↓`TcpOutboundGateway`のメッセージ送信部（一部省略）

```java:TcpOutboundGateway.java
protected Object handleRequestMessage(Message<?> requestMessage) {
	TcpConnection connection = null;
	String connectionId = null;
	try {
			connection = this.connectionFactory.getConnection();
			AsyncReply reply = new AsyncReply(this.remoteTimeoutExpression.getValue(this.evaluationContext,
					requestMessage, Long.class));
			connectionId = connection.getConnectionId();
			this.pendingReplies.put(connectionId, reply);
			if (logger.isDebugEnabled()) {
				logger.debug("Added pending reply " + connectionId);
			}
			connection.send(requestMessage);
```

### 送信チェックをする理由
普通は送信時にチェックをする必要なんてないのですが、案件によっては、"あるコンポーネントの状態を監視して、その状態によってメッセージを送信しない。"というような要件があるかもしれません。今、私が携わっている案件がそれです:v:

### 送信チェックをする方法
とりあえず、私が考えた方法は２つあります。

#### TcpOutboundGatewayを拡張する
まぁ、愚直な発想ですが……
`TcpOutboundGateway`を拡張して`handleRequestMessage`をオーバーライドします。
`getStatus`の戻り値が`true`の場合だけ、送信処理をおこないます。

```java:CustomizedTcpOutboundGateway.java
public class CustomizedTcpOutboundGateway extends TcpOutboundGateway {

  @Override
  public Object handleRequestMessage(Message<?> message) {
    if (getStatus()) {
      return super.handleRequestMessage(message);
    } else {
      return new GenericMessage<String>("can not send");
    }
  }
```
元の`handleRequestMessage`の戻り値は`Object`型ですが、正常系の場合は受信メッセージを返しているので、適当な`Message`オブジェクトを返しています。ここは要件しだいですね。

#### Interceptorを活用する
↑の方法でもいいのですが、あまりフレームワークのクラスを拡張したくないなぁという場合は、`Interceptor`を活用する方法があります。
手順は以下の通りです。

まず`TcpConnectionInterceptor`を拡張し、メッセージ送信時にチェック処理を挟ませます。

```java:TcpConnectionInterceptor.java
public class TcpConnectionInterceptor extends TcpConnectionInterceptorSupport {

  @Override
  public void send(Message<?> message) throws Exception {
    if (getStatus()) {
      super.send(message);
    }
  }
 // 略
```
`getStatus()`の値によってメッセージを送信します。`TcpConnectionInterceptorSupport`には接続周りのメソッドが複数あるので、今回のような送信前以外のタイミングにも処理を挟み込むことができます:hugging:。

次に、今作った`TcpConnectionInterceptor`を返す`TcpConnectionInterceptorFactory`を実装したクラスを作ります。実装は適当です。

```java:CustomizedTcpConnectionInterceptorFactory.java
public class CustomizedTcpConnectionInterceptorFactory implements TcpConnectionInterceptorFactory {

  private final TcpConnectionInterceptorSupport interceptor;

  public CustomizedTcpConnectionInterceptorFactory(TcpConnectionInterceptorSupport interceptor) {
    this.interceptor = interceptor;
  }

  @Override
  public TcpConnectionInterceptorSupport getInterceptor() {
    return interceptor;
  }
}
```

あとは、これを`TcpConnectionInterceptorFactoryChain`に渡して、`ClientConnectionFactory`に設定すればOKです。

```xml
  <int-ip:tcp-connection-factory type="client" host="localhost" port="56789"
    deserializer="deserializer" serializer="serializer" interceptor-factory-chain="interceptorFactoryChain" />

  <bean id="interceptorFactoryChain"
    class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
    <property name="interceptors">
      <array>
        <bean
          class="com.neriudon.sample.interceptor.CustomizedTcpConnectionInterceptorFactory"
          c:interceptor-ref="tcpConnectionInterceptor" />
      </array>
    </property>
  </bean>

  <bean id="tcpConnectionInterceptor"
    class="com.neriudon.sample.interceptor.TcpConnectionInterceptor" />
```
ClientConnectionFactoryの設定は適当です。:frowning2:

こちらの方法は、手順が少し複雑ですが、元の`Gateway`をいじらないのが利点でしょうか。

### この記事を投稿するに至った経緯
現在携わっている案件で、この機能が必要ととなったためメモとして。

### さいごに
Spring Integrationのクラス名でググっても、日本語のサイトがヒットしないケースがざらなので、みなさんもSpring Integrationを触ってみてください。:kissing_heart: