Spring の JmsInvoker を使用すると、JMS経由でのRMI処理が簡単に書けます。ここでは、サービスを提供するサーバ上の AccountService をクライアントからJMS経由で呼び出す例を見てみます。
使用ライブラリ
Spring関連
- spring.jar(2.5.5)
- spring-test.jar(2.5.5)
ActiveMQ関連
- activemq-all-5.2.0.jar
- xbean-spring-3.4.jar
- junit-4.7.jar
サーバが提供するサービス
サービスとして以下のインターフェースを考えます。
package etc9.service; public interface AccountService { public String getAccountName(Long accountId); }
サービスの実装は以下のような実装としておきます。
package etc9.service; public class AccountServiceImpl implements AccountService { @Override public String getAccountName(Long accountId) { if (accountId > 10) return "200-" + accountId; return "000-" + accountId; } }
サービスを利用するテストケース
上記サービスを利用するテストケースを以下とします。
package etc9.service; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class AccountServiceTest { @Autowired private AccountService accountServiceClient; @Test public final void testCancelAccount() { System.out.println( accountServiceClient.getAccountName(new Long(12))); } }
JMSの設定
SpringJUnit4ClassRunner より呼び出す AccountServiceTest-context.xml を用意します。ここでは、サーバとクライアントを同一ホストで動作させるため、設定も1つのファイルでまとめて記述します。AccountServiceTest と同じディレクトリに AccountServiceTest-context.xml を作成します。
まずは、ActiveMQ の設定から見ていきます。http://activemq.apache.org/schema/coreのネームスペースで Spring コンテキストに ActiveMQ の設定を合わせて記述します。
<!-- ActiveMQ Broker の設定 --> <amq:broker id="broker" useJmx="false" persistent="false"> <amq:transportConnectors> <amq:transportConnector uri="tcp://localhost:0" /> </amq:transportConnectors> </amq:broker> <!-- ActiveMQ Destination --> <amq:queue id="destination" physicalName="jms.service.test" /> <!-- embedded モードでの MessageBroker を使用 --> <amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost" />
vm://localhost の設定にて組み込みモードで ActiveMQ を起動するため、別途 ActiveMQ を起動する必要はありません。
サーバ側のサービス設定
JmsInvokerServiceExporter にて公開するサービスを設定し、DefaultMessageListenerContainer のリスナーとして登録しています。
<!-- コネクションファクトリの定義 --> <bean id="jmsServerConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory"><ref local="jmsFactory" /></property> </bean> <!-- リモートサービスの定義 --> <bean id="accountServiceServer" class="org.springframework.jms.remoting.JmsInvokerServiceExporter"> <property name="serviceInterface" value="etc9.service.AccountService"/> <property name="service"> <bean class="etc9.service.AccountServiceImpl"/> </property> </bean> <!-- JMSのリスナのためのコンテナ定義 --> <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsServerConnectionFactory"/> <property name="destination" ref="destination"/> <property name="messageListener" ref="accountServiceServer"/> </bean>
クライアント側のサービス設定
JmsInvokerProxyFactoryBean として公開サービスのインターフェースを登録しています。このプロキシを経由してサービスを呼び出すことで、JMS経由でのサービスが利用できます。
<!-- コネクションファクトリ定義 --> <bean id="jmsClientConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory" ref="jmsFactory" /> </bean> <!-- JMS経由のサービスプロキシ定義 --> <bean id="accountServiceClient" class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean"> <property name="serviceInterface" value="etc9.service.AccountService"/> <property name="connectionFactory" ref="jmsClientConnectionFactory"/> <property name="queue" ref="destination"/> </bean>
実行結果
テストケースを実行すると、以下のような出力が得られます。
200-12
設定ファイルの記述が多少面倒ですが、サービスのリモート呼出が簡単に実現できます。なお、サービス呼出時には引数や戻り値はシリアライズされるため、Serializable である必要があります。
AccountServiceTest-context.xml全文
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"> <amq:broker id="broker" useJmx="false" persistent="false"> <amq:transportConnectors> <amq:transportConnector uri="tcp://localhost:0" /> </amq:transportConnectors> </amq:broker> <amq:queue id="destination" physicalName="jms.service.test" /> <amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost" /> <bean id="jmsServerConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory"><ref local="jmsFactory" /></property> </bean> <bean id="accountServiceServer" class="org.springframework.jms.remoting.JmsInvokerServiceExporter"> <property name="serviceInterface" value="etc9.service.AccountService"/> <property name="service"> <bean class="etc9.service.AccountServiceImpl"/> </property> </bean> <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="jmsServerConnectionFactory"/> <property name="destination" ref="destination"/> <property name="messageListener" ref="accountServiceServer"/> </bean> <bean id="jmsClientConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory" ref="jmsFactory" /> </bean> <bean id="accountServiceClient" class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean"> <property name="serviceInterface" value="etc9.service.AccountService"/> <property name="connectionFactory" ref="jmsClientConnectionFactory"/> <property name="queue" ref="destination"/> </bean> </beans>