Seasar Conference 2009 White

  • CubbyMavenを使った開発のまとめスレ
  • 最新のDI & AOP
  • JBoss Application Server入門 - Seasar2を動かして見よう! -
  • Hudsonを用いたOSS開発
  • ライトニングトーク

と聞いてきました。
その後懇親会にも出席。妻が子供を連れて実家に帰っているので、今日は最後まで行こう!と思って行ったら、「最後」は朝でした。。。途中寝てしまいましたが、それでも色々な方とお話ができてよかったです。

IBM WebSphere MQ with Sun Generic Resource Adapter for JMS on GlassFish

Sun Generic Resource Adapter for JMSと IBM WebSphere MQ の組み合わせをGlassFish上で動かしてみます。

Sun Generic Resource Adapter for JMSのデプロイ

管理コンソールのアプリケーション > コネクタモジュールから配備します。ファイルを選択して「了解」ボタンをクリックするとリソースアダプタのプロパティー編集画面へ遷移します。ここでorg.seasar.jms.core.deploy.impl.WMQResourceAdapterDeployer#setupProperties()にてセットされている通りにプロパティをセットします。

コネクタ接続プールの作成

管理コンソールのリソース > コネクタ > コネクタ接続プールから作成します。リソースアダプタには先ほどデプロイしたものを指定します。次の画面で追加プロパティを設定しますが、ここにはorg.seasar.jms.core.deploy.impl.JMSManagedConnectionFactoryDeployer#setProperty()と同じものを設定します(名前:ConnectionFactoryProperties,値:QueueManager=Xxx,…)。

コネクタリソースの作成

管理コンソールのリソース > コネクタ > コネクタリソースから作成します。今回JNDI名は/jms/connectionとしました。

以上でGlassFish側の設定は終了です。次にS2JMSの設定です。
S2JMS側ではリソースアダプタの設定が不要になります。コネクションファクトリをlookupするだけになります。

jms.dicon

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
"http://www.seasar.org/dtd/components24.dtd">
<components>
	<!-- コネクションファクトリ -->
	<component class="javax.jms.QueueConnectionFactory">
		@org.seasar.extension.j2ee.JndiResourceLocator@lookup("jms/connection")
	</component>

	<!-- セッションファクトリ -->
	<component
		class="org.seasar.jms.core.session.impl.SessionFactoryImpl" />

	<!-- メッセージ送信コンポーネント -->
	<component instance="prototype"
		class="org.seasar.jms.core.impl.MessageSenderImpl">
		<property name="destinationFactory">
			<!-- デスティネーション (キューまたはトピック) ファクトリ -->
			<component
				class="org.seasar.jms.core.destination.impl.QueueFactory">
				<!-- キュー名を指定します -->
				<property name="name">"TEST_QUEUE"</property>
			</component>
		</property>
	</component>
</components>

s2container.dicon

こちらの設定も忘れずに。。。これでJDBC,JMSともにGlassFishJTAで動かすことができます。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
	"http://www.seasar.org/dtd/components24.dtd">
<components>
	<include condition="#ENV == 'ut'" path="warmdeploy.dicon" />
	<include condition="#ENV == 'ct'" path="hotdeploy.dicon" />
	<include condition="#ENV != 'ut' and #ENV != 'ct'"
		path="cooldeploy.dicon" />
	<component
		class="org.seasar.framework.container.factory.SimplePathResolver">
		<initMethod name="addRealPath">
			<arg>"jta.dicon"</arg>
			<arg>"jta-sun9.dicon"</arg>
		</initMethod>
	</component>
</components>

OracleRownumPagingSqlRewriterとBooleanToIntStatementFactoryの併用

共にOracleの場合に使うものなので、問題なく併用できそうな気がするのですが、実際に使ってみると問題が発生しました。
調べてみると、まず最初に元のSQLを書き換えて件数を取得しているのですが、そこでは設定に関わらずBasicStatementFactoryを使っているようでした。したがって、PagerConditionを使いかつ条件にbooleanが含まれる場合は件数を取得するSQLの実行でエラーとなっていまいます。
回避策としてはgetCountメソッドをオーバーライドしたクラスを作ってそっちを利用するしかないような感じなので、とりあえずはそうしてます。

S2 2.4.18以降でS2JMS

S2JMSのアウトバウンド通信で受信を繰り返していたら、なぜか10回受信したところで止まってしまう現象に遭遇。

この10回という数字はなんだろう?と思っていろいろやってみたところ、コネクションプールのデフォルト値らしいことが判明。さらによ〜く調べてみるとプールからコネクションを取れずにずっと空きまちになっている感じでした。さらにさらによ〜〜く調べてみるとプールにコネクションを戻せていないようでした。


前は普通に使えてたのにな・・・って思ったところで閃きました。S2のバージョンをS2JMS(というかS2JCA)が依存している2.4.17にしてみようと。


結果、やはり2.4.17にしたら普通に使えました。で、移行ガイドを見たら

S2JTA において,Synchronization#afterCompletion(int) メソッドは完全にトランザクションコンテキスト外で呼び出されるようになりました (JTA の仕様です).afterCompletion() 内で TransactionManager#getTransaction() を呼び出すと,従来は完了した Transaction を取得できましたが,rc4 からは null が返されます.

これかー。でも2.4.18以降が使いたいなー。という訳でS2JCAをいじってみました。

org.seasar.jca.outbound.policy.AbstractTransactionBoundedPoolingPolicy

Synchronizationを実装するのをやめて以下のような内部クラスを作ります。

public class SynchronizationImpl implements Synchronization {

    /** トランザクション */
    protected final Transaction tx;

    /**
     * インスタンスを構築します。
     * 
     * @param tx
     *            トランザクション
     */
    public SynchronizationImpl(final Transaction tx) {
        this.tx = tx;
    }

    public final void beforeCompletion() {
    }

    public void afterCompletion(final int status) {
        releaseContext(tx);
    }
}

releaseContextはトランザクションをもらうように変更します。

protected void releaseContext(Transaction tx) {
    final ManagedConnectionPool<Object> pool = pools.get(tx);
    if (pool != null) {
        pool.close();
    }
}

org.seasar.jca.outbound.policy.LocalTransactionBoundedPoolingPolicy

org.seasar.jca.outbound.policy.XATransactionBoundedPoolingPolicy

associateTxの

tx.registerSynchronization(this);

の部分を

TransactionUtil.registerSynchronization(tx, new SynchronizationImpl(tx));

あるいは

tx.registerSynchronization(new SynchronizationImpl(tx));

に変更する。


まぁほとんどorg.seasar.extension.dbcp.impl.ConnectionPoolImplのコピペですが、とりあえずこれで動くようになりました。

S2JMS on GlassFish その2

前回はうっかりS2JTAのままで動かしてしまったので、今回はアプリケーションサーバ側のJTAを使って動かしたいと思います。

s2container.dicon

Sun Java System Application Server用の設定ファイルが用意されているようなので、それを使うように変更します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
	"http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="cooldeploy.dicon"/>
    <component class="org.seasar.framework.container.factory.SimplePathResolver">
  		<initMethod name="addRealPath">
    		<arg>"jta.dicon"</arg>
    		<arg>"jta-sun9.dicon"</arg>
  		</initMethod>
	</component>
</components>
JmsPage.java

画面はロールバック感を出すために、基本的に1〜10を送信するがXが入力された場合だけ10を送信せずに例外を投げるように変更。また、受信側もタイムアウトの設定を追加。

package hatena.beerman.web.jms;

import org.seasar.jms.core.MessageReceiver;
import org.seasar.jms.core.MessageSender;

public class JmsPage {

	public String sendMessage;

	public String recieveMessage;

	public MessageSender messageSender;

	public MessageReceiver messageReceiver;

	public javax.jms.ConnectionFactory connectionFactory;

	public void doSend() {
		final int LOOP = 10;
		final String ROLL_BACK = "X";
		for (int i = 1; i <= LOOP; i++) {
			if (i == LOOP && ROLL_BACK.equals(sendMessage)) {
				throw new RuntimeException();
			} else {
				messageSender.send(String.valueOf(i));
			}
		}
	}

	public void doRecieve() {
		final long TIMEOUT = 1000L;
		recieveMessage = messageReceiver.setTimeout(TIMEOUT).receiveText();
	}
}

今回も無事動きました!と言いたいところでしたが、TransactionManagerのlookupに失敗してしまいました。TransactionManagerの部分は

@org.seasar.extension.j2ee.JndiResourceLocator@lookup("java:appserver/TransactionManager")

とやるのが正解みたいです。という訳でそこだけ書き換えたjta-sun9.diconを用意してみたところ無事に動きました!

on GlassFish

Sun Java System Application Server9.1でS2JMSを動かしてみました。

接続プールの作成

管理コンソールから簡単に作れました。

リソース-->JMSリソース-->接続ファクトリ

JNDI名「jms/hoge1」で登録。
Queueは

リソース-->JMSリソース-->送信先リソース

で登録すれば作成されるみたいです。とりあえず「hoge」というQueueを登録しました。JNDI名は「jms/hoge2」。
普通は外部のQueueを使うと思いますが、ここでは自分のとこのQueueに送受信してみます。

S2JMS側の設定

Dolteng 0.30.0で作ったdiconファイルを基に作りました。jms-outbound.diconを以下のように変更します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
	"http://www.seasar.org/dtd/components24.dtd">
<components>
	<description>
		1.マネージドコネクションファクトリをJNDIで取得するように変更。
		2.リソースアダプタはアプリケーションサーバのものを使うので削除。
		3.デスティネーションファクトリはSimpleDestinationFactoryに変更。
	</description>
	<!-- マネージドコネクションファクトリ -->
	<component class="javax.jms.ConnectionFactory">
		@org.seasar.extension.j2ee.JndiResourceLocator@lookup("jms/hoge1")
	</component>

	<!-- セッションファクトリ -->
	<component class="org.seasar.jms.core.session.impl.SessionFactoryImpl"/>

	<!-- メッセージ送信コンポーネント -->
	<component instance="prototype"
			class="org.seasar.jms.core.impl.MessageSenderImpl">
		<property name="destinationFactory">
			<!-- デスティネーション (キューまたはトピック) ファクトリ -->
			<component class="org.seasar.jms.core.destination.impl.SimpleDestinationFactory">
				<property name="destination">
					@org.seasar.extension.j2ee.JndiResourceLocator@lookup("jms/hoge2")
				</property>
			</component>
		</property>
	</component>

	<!-- メッセージ受信コンポーネント -->
	<component instance="prototype"
			class="org.seasar.jms.core.impl.MessageReceiverImpl">
		<property name="destinationFactory">
			<!-- デスティネーション (キューまたはトピック) ファクトリ -->
			<component class="org.seasar.jms.core.destination.impl.SimpleDestinationFactory">
				<property name="destination">
					@org.seasar.extension.j2ee.JndiResourceLocator@lookup("jms/hoge2")
				</property>
			</component>
		</property>
	</component>
</components>

動作確認

Teedaで簡単な確認画面を作成しました(htmlは省略)。

package hatena.beerman.web.jms;

import org.seasar.jms.core.MessageReceiver;
import org.seasar.jms.core.MessageSender;

public class JmsPage {

	public String sendMessage;

	public String recieveMessage;

	public MessageSender messageSender;

	public MessageReceiver messageReceiver;

	public void doSend() {
		messageSender.send(sendMessage);
	}

	public void doRecieve() {
		recieveMessage = messageReceiver.receiveText();
	}
}

ちゃんと動いてくれました〜。

ジェネリックスのワイルドカードとキャスト

S2JUnit4の拡張アサートの「データセットとマップのリストが等しいことをアサート」するメソッド

public static void assertMapEquals(String message, DataSet expected,
        List<Map<?, ?>> list) {
    adapter.assertMapListEquals(message, expected, list);
}

を使いたかったのですが、なぜか「データセットとマップが等しいことをアサート」するメソッド

public static void assertMapEquals(String message, DataSet expected,
        Map<?, ?> map) {
    adapter.assertMapEquals(message, expected, map);
}

の方を呼びにいこうとしてしまい、パラメータの型が一致しないとコンパイラに怒られてしまって困ってしまいました。
原因はパラメータとしてList>型を渡していたことだったのですが、これが分からず結構ハマってしまいました。要はジェネリックスについての理解が足りなかったようです。

List<?> list1 = new ArrayList<String>();
List<?> list2 = new ArrayList<?>();
List<Map<?, ?>> list3 = new ArrayList<Map<String, Object>>();
List<Map<?, ?>> list4 = new ArrayList<Map<?, ?>>();

list1とlist3が○でlist2とlist4が×だと勘違いしていたのですが、実際にはlist1とlist4が○でlist2とlist3が×でした。よくよく考えれば確かに納得なのですが。。。