WebアプリケーションでSpring Integrationを使用する場合のスレッド

今回のブログは、Spring Framework Advent Calendar 201220日目の記事として投稿します。

前回のブログで、WebアプリケーションでSpring Integrationを使用すると、Spring Integrationが生成するスレッドをAPサーバが管理できないので危険だというお話をしました。

この件について、SpringSource CommunityのForumに、「Spring IntegrationをWebアプリケーションで使用するのは、スレッドの観点から安全なのか?」質問したところ、APサーバが管理するスレッドをSpring Integrationが使用する方法を紹介してもらいました。

今回のブログでは、APサーバが管理するスレッドをSpring Integrationが利用する方法を紹介します。

Spring Integrationには、CommonJと呼ばれるAPIを介して並列処理を実施する仕組みが提供されています。CommonJは、「JSR 237: Work Manager for Application Servers」と「JSR 236: Concurrency Utilities for JavaTM EE」を合わせた呼び名で、EJBServletがコンテナ内で並列処理をするためのAPIです。ちなみにWebLogicやWebSphereは対応しているようですが、Tomcatは対応していません。

CommonJでは、非同期処理用のcommonj.work.WorkManagerインタフェースと、スケジューリング用のcommonj.timers.TimerManagerインタフェースが提供されています。Spring Integrationはそれぞれに対応したorg.springframework.scheduling.commonj.WorkManagerTaskExecutorクラスとorg.springframework.jms.listener.DefaultMessageListenerContainerクラスが提供されており、Bean定義ファイルでBeanを定義して利用します。

TimerManagerTaskSchedulerのBean定義は以下になります。

    <bean id="taskScheduler" class="org.springframework.scheduling.commonj.TimerManagerTaskScheduler">
      <property name="timerManagerName" value="java:comp/env/tm/MyTimerManager" />
    </bean>

propertyで指定しているのは、APサーバが提供しているTimerManagerのJNDI名です。この設定により、ポーリングするようなEndpointでは、このBeanが使われるようです(動作確認は、タグとタグで作成したAdapterでのみ実施)。idは、"taskScheduler"にする必要があるようです(idの値を変えると動作しませんでした)。

WorkManagerTaskExecutorのBean定義は以下になります。

    <bean id="taskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
      <property name="workManagerName" value="java:comp/env/wm/MyWorkManager" />
    </bean>

動作確認のため、タグでJMSのメッセージに応じて非同期処理を行うEndpointを作成しようとしましたが、タグだけでは、taskExecuterを利用できませんでした。Forumに問い合わせたところ、以下のように、DefaultMessageListenerContainerクラスのBeanを仲介する必要があると教わりました。

    <bean id="sampleQueueListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="taskExecutor" ref="taskExecutor"/>
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destinationName" value="sampleQueue"/>
    </bean>
    <jms:message-driven-channel-adapter
        id="inboundAdapter" 
        channel="channel2" 
        container="sampleQueueListenerContainer"/>

DefaultMessageListenerContainerのpropertyでtaskExecutorを指定し、タグのcontainer属性で、DefaultMessageListenerContainerのBeanを指定します。

さいごに

タグを使うときはDefaultMessageListenerContainerを使用しなければAPサーバのWorkManagerが使用されないなど、癖があるようです。実際の開発プロジェクトで、APサーバが管理するスレッドをSpring Integrationで使う場合は、作成するEndpointの種類ごとにWorkManagerやTimerManagerがきちんと利用されるか?を検証したほうがよさそうです。