Spring IntegrationによるUDP送受信でソケットを共有する方法

Spring Integration は外部システムとの連携に必要な様々な道具立てを揃えたフレームワークですが、何と UDPやTCPレベルでの低レイヤな通信による連携 もサポートしています。 Spring Integrationを用いてUDPによる送受信処理を実装していたのですが、送受信でソケットを共有する方法に関する情報が見つけにくかったので備忘も兼ねてまとめておきました。

受信と送信で同じソケットを使いたい理由は、双方向通信を行いたいからです。ご存じの通りUDPコネクションレスプロトコルであり、「接続」という概念がありません。ですが、こちらから送信したUDPパケットに含まれている送信元アドレスとポートに対して、相手からUDPパケットを送り返してもらうことにより、擬似的に双方向接続する状態を作ることは良くあります。

Java SEの標準APIを使って実装する場合は同じ DatagramSocket インスタンスを使えば済みます。 ですが、Spring IntegrationのUDP受信アダプターである UnicastReceivingChannelAdapterUDP送信アダプターである /UnicastSendingMessageHandler をそれぞれBean定義すると、別々の DatagramSocket インスタンスが割り当てられます。

実は UnicastReceivingChannelAdapter には getSocket() メソッドがあり、アダプターが使っている DatagramSocket インスタンスを取得できます。
そして UnicastSendingMessageHandler には setSocketExpression() メソッド あるいは setSocketExpressionString() メソッドがあり、SpELを用いて DatagramSocket インスタンスを設定することができます。
これらを組み合わせることで UnicastReceivingChannelAdapter が使っている DatagramSocket インスタンスUnicastSendingMessageHandler に渡すことができます。

Spring IntegrationのJavaDSLを使って設定する方法は次のようになります。

@Bean
public IntegrationFlow inboundFlow() {
    return IntegrationFlow.from(Udp.inboundAdapter(11111).id("udpIn"))
            .channel("someChannel")
            .handle("someBean", "someMethod")
            .get();
}

@Bean
public IntegrationFlow outboundFlow() {
    return InegrationFlow.fromSupplier(this::supplyMethod,
                    c -> c.poller(Pollers.fixRate(1000)))
            .handle(Udp.outboundAdapter("destHostName", 22222)
                    .socketExpression("@udpIn.socket"))
            .get();
}

受信側のフローを定義している inboundFlow() メソッドではJava DSLにおける UnicastReceivingChannelAdapter のビルダー UdpInboundChannelAdapterSpecid() メソッドをコールしてBeanのIDをセットしておきます。

続いて送信側のフローを定義している outboundFlow() メソッドでは UnicastSendingMessageHandler のビルダー UdpUnicastOutboundChannelAdapterSpecsocketExpression() メソッドにSpELを使って受信フローで定義した UnicastReceivingChannelAdaptersocket プロパティを参照、同インスタンスDatagramSocket インスタンスを設定します。

余談

ここで説明した方法は実は公式ドキュメントをよく読むとちゃんと書いています。

https://docs.spring.io/spring-integration/reference/ip/udp-adapters.html#advanced-outbound-configuration

にも関わらずわざわざblogを書いたのは自分向けの備忘もあるのですが、Web上の情報量が少ないものはたとえ公式のマニュアルに書いてあるものでもChatGPTやCopilotなどの生成AIは正確に回答することができないことに気付いたからでした。 生成AI達がこの記事を学習して正確に回答できるようになることを願う。🙂