Seasar Conference 2008 Autumn の LT で何か話してたよなぁとか思い出しつつ。
まず TERASOLUNAフレームワーク とは何モンかというと、NTT データが提供してるオープンソースのフレームワーク。今んとこ 4 種類あって、Java で Web アプリ作る時用の TERASOLUNA Server Framework for Java(Web版)、Ajax と絡めて使う場合に使うっぽい TERASOLUNA Server Framework for Java(Rich版)、Java でバッチアプリ作る時用の TERASOLUNA Batch Framework for Java、.NET 環境と連携して何かする的な用の TERASOLUNA Server/Client Framework for .NET というラインナップになっている。このエントリでは、Java でバッチアプリ作る時用のフレームワークについてだけ触れています。
やったのは、サイトの方で用意されているチュートリアルどおりに作ってみただけです。そこから得られた感想とか、バッチフレームワークってこんな構成でした的なことを書いていきたいと思います。
アーキテクチャのちゃんとした説明に関してはドキュメントがあるので、TERASOLUNAフレームワーク - ドキュメント を参照して下さい。あと TERASOLUNA Batch Framework for Java チュートリアル もかな。なお、トップページにある ドキュメント のリンク先は空なので御注意。
TERASOLUNA Batch Framework for Java の概要
カンタンにいうと Spring + iBatis です。DB アクセスや O/R マッピングの部分は iBatis にだいたいお任せ。で、TERASOLUNA 流のバッチ作成のお作法があり、そのお作法に従って設定ファイルで指定したクラスが順番に実行されていく、ってトコです。
プロジェクト構成
とりあえず チュートリアル 通りに作るとこんな感じになります。batchapps/common, batchapps/template 下には特にファイル追加してないので展開表示してません。
TERASOLUNA Batch Framework for Java の動作を大雑把に
で、どんな順番でどんなクラスが実行されていくか、ですが。TERASOLUNA は Spring を使用して、bean id で指定されたクラスを決められた順序に従って実行していきます。具体的な作業としてはは、そのクラス設定を↑の main 引数で指定した設定ファイルに書いていきます。
たとえばビジネスロジックは blogic という bean id で指定してやります。
<bean id="blogic" class="jp.terasoluna.batch.tutorial.uc0001.jb0001.DBBLogic"> <property name="queryDAO" ref="queryDAO" /> <property name="updateDAO" ref="updateDAO" /> <property name="messageAccessor" ref="messageAccessor"/> </bean>
他に、ジョブ前処理は jobPreLogicList、ジョブ後処理は jobPostLogicList、という id で指定します。前・後処理は複数設定可能なので list になっとります。
<util:list id="jobPreLogicList"> <bean class="jp.terasoluna.batch.tutorial.uc0001.jb0001.DBJobPreLogic"> <property name="queryDAO" ref="queryDAO" /> </bean> </util:list> <util:list id="jobPostLogicList"> <bean class="jp.terasoluna.batch.tutorial.uc0001.jb0001.DBJobPostLogic"> <property name="updateDAO" ref="updateDAO" /> </bean> </util:list>
jobPreLogicList で指定したクラスが前処理、blogic で指定したクラスが本処理、jobPostLogicList で指定したクラスが後処理、の順に動く、ってのが大まかな動きです。つまり、Template Method 的*1に必要な bean 定義が決まっていて、その定義に沿ってクラス作ってくと、決められた順序に沿ってクラスが実行される、ってトコですかね。
これらの bean id は TERASOLUNA の決め打ちです。なので、bean id にどんなモノがあって、どんな時に実行されて、どんな property が設定可能かはドキュメントをちゃんと読む必要があります。TERASOLUNA のバッチアプリフレームワーク使う場合は、設定ファイルのドキュメントと睨めっこですな。
トランザクションのスコープ範囲などなどでクラスの実行順序とかどんな bean id 使うかとか書き方とか色々変わってくるんで、正確な設定ファイルの詳細についてはドキュメントを参照して下さい。
とまぁ、バッチフレームワークの動作は大雑把に書くとそんなトコです。
セッターインジェクション
でまぁ続きですが。そのクラスに DI でなんか値突っ込みたい場合は↓みたいな感じです。ちょっと上に書いた bean id="blogic" んとこも参照してください。この辺はフツーに Spring というかセッターインジェクションというか。
public class DBBLogic implements BLogic<JB0001Data, JB0001JobContext> { private QueryDAO queryDAO = null; private UpdateDAO updateDAO = null; private MessageAccessor messageAccessor = null; ... public BLogicResult execute(JB0001Data nyukinData, JB0001JobContext jobContext) { .... } public void setQueryDAO(QueryDAO queryDAO) { this.queryDAO = queryDAO; } public void setUpdateDAO(UpdateDAO updateDAO) { this.updateDAO = updateDAO; } public void setMessageAccessor(MessageAccessor msgAcc) { this.messageAccessor = msgAcc; }
ついでに付け加えとくと、ビジネスロジック担当クラスの場合は BLogic って interface を実装してやる必要があります。
あと、JB0001Data とか JB0001JobContext って何よ? と思うかもしれませんが、メンドイので説明省略します。カンタンに言うと、このクラスより前に実行されるデータ取得担当クラス用のクラスで作成されるデータ格納用のクラスです。DB からデータ取ってくる処理を自分で書いとくと、ここの execute で突っ込んでくれる、みたいな感じです。
SQL
細かいとこは省きますが DB アクセスの部分についてさくっと。
SQL は、専用の定義ファイルに XML で書いてきます。例えばこんな感じ。ここは TERASOLUNA がどうこうというより、iBatis の流儀の部分でしょうな。
<select id="getZandakaData" parameterClass="java.lang.String" resultClass="jp.terasoluna.batch.tutorial.uc0001.jb0001.ZandakaData"> SELECT ZANDAKA FROM ZANDAKATABLE WHERE KOKYAKUID = #value# </select>
でまぁ、この SQL 呼びたいときは Java 側でこんな感じのコードを書く。
ZandakaData zandakaData = queryDAO.executeForObject( "UC0001.getZandakaData", nyukinData.getKokyakuid(), ZandakaData.class);
以下、雑感。
TERASOLUNA 固定の Bean 定義と実行順序、Spring と iBatis の機能の把握が鍵
TERASOLUNA のバッチフレームワーク使って開発する場合は、コレが鍵なんだろなーと思うのでした。
XML 地獄の予感
ですよねー……俺はチュートリアルの分量でゲンナリしてしまったので、実開発だと膨大な設定ファイルかかにゃならんことは明らかなワケで。まぁそういうモンだと言えばそういうモンなんだけど……
作られた時代背景などを考えるとしゃーないんだろうけど、もちっと CoC で自動にやっちゃう部分設けても良かったんじゃないかなぁと、Seasar2 とか Rails とか見てると思ってしまいます。例えば、QueryDAO queryDAO とかなんてほぼ確実に使うんだから、public フィールドで勝手にインスタンス突っ込んでくれやがりましてもいいんじゃないかなーみたいな。
*1:DI で制御の反転してるんで、Template Method はちょっと正しくないですがマァいいや……