2. Buri 入門
2007.03.09 株式会社四次元データ 鈴木 圭
本章では 1 章で紹介したワークフロー・エンジンの一つである Buri について解説します。また、実際に Buri に付属しているサンプル・プログラムの実行も行います。なお、本章以降では S2Container や S2Dao についてある程度の知識を持っていることを前提とします。
2.1. Buri とは
Buri(Business Unit Routing Integration)は DI コンテナの一つである S2Container を活用したワークフロー・エンジンです。Apache License, Version 2.0 として公開されています。また、動作には S2Container 2.4 以上、S2Dao、S2Dao が対応する RDB、J2SE 5.0 以上が必要となります。
ワークフロー・エンジンは世の中に多々ありますが、DI コンテナを活用したワークフロー・エンジンというものは稀でしょう。また、Buri はワークステート・エンジンとも呼ばれます。Buri がワークフロー・エンジンとして必要な機能を完全に実装することを目標とせず、あくまで実際の開発における重要度の高い機能だけを実装しています。そのため、一般のワークフロー・エンジンと区別するためにワークステート・エンジンとも呼ばれます。
DI コンテナを利用していることを含め、Buri には次のような特徴があります:
- XPDL 形式で記述されたワークフローを実行する
- S2Dao によって状態を永続化する
- ワークフロー上の処理は OGNL 式で指定する
- S2Container 上のオブジェクトにアクセス可能
- BAO(BusinessLogic Access Object)という高水準インタフェースによる開発
- Buri 自体が S2Container 上のオブジェクトに過ぎないので、他のアプリケーションとの連携が容易
Buri で実行するワークフローは 1 章で紹介した XPDL という形式で記述します。XPDL ファイルの作成は後に紹介する JaWE(Java Workflow Editor)などのワークフロー・エディタを利用することができるので、XPDL の構文まで覚える必要はありません。また、ワークフロー上の処理は OGNL(Object Graph Navigation Language)という式言語によって記述します。OGNL 式の中では S2Container 上のオブジェクトにアクセスすることができます。
Buri を利用した開発では BAO(BusinessLogic Access Object)というインタフェースを介して、ワークフローにアクセスします(BAO を利用しなくてもアクセス可能です)。BAO とはワークフローに対する高水準のインタフェースを提供するインタフェースで、JDBC に対する DAO(Data Access Object)のような位置づけです。これによってワークフロー・エンジンの存在をあまり意識することなくアプリケーションを開発することができます。
2.2. 基本的な用語の導入
実際に Buri を導入する前に、最低限必要な用語の導入を行います。下図は「1.4. モデリング」で使用したものと同じ図ですので、これと照らし合わせながら確認してみてください:

- アクティビティ(Activity)
- ワークフロー上の「処理」や「状態」を表します。上図には「登録」「確認待機」「承認」「非承認」という四つのアクティビティが含まれています。アクティビティに処理を設定しておくことで、オブジェクトがそのアクティビティに遷移してきたときに実行させることができます。
- トランジション(Transition)
- アクティビティからアクティビティへの遷移を表します。上図では矢印がトランジションを表しています。トランジションには条件を設定することもでき、指定した条件に合致した場合のみ、その先のアクティビティに遷移させることができます。
2.3. Buri の導入
それでは実際に Buri を動かしてみましょう。Buri は以下のサイトで入手することができます:
http://s2buri.sandbox.seasar.org/
ここでは最新バージョンである 0.3.1 をダウンロードしたとして説明を続けます。ダウンロードしたファイルを展開すると、Buri を利用するために必要なファイル一式が含まれたフォルダが得られます。その中には Eclipse や Maven 用のプロジェクト・ファイルも含まれています。本章の残りの部分では、Buri に含まれているサンプル・プログラムを実行し、それに対して解説を行っていきます。
Buri のプロジェクトのインポート
まずは Buri のプロジェクトを Eclipse にインポートします。サンプル・プログラムの実行は Eclipse 上で行うことにします(サンプル・プログラムは JUnit の TestCase として提供されています)。プロジェクトのインポートの手順は以下の通りです:
- Eclipse 上で [File]->[Import] メニューを選択する
- プロジェクトの種類に [General]->[Existing Projects into Workspace] を選択する
- [Select root directory] で展開した Buri のディレクトリを選択する

注意として、Buri のソース・ファイルは Shift_JIS で記述されています。そのため、必要ならばインポートした Buri のプロジェクトの設定で、文字コードを Shift_JIS や Windows-31J などに変更します。
2.4. サンプル・プログラムの実行
取り上げるサンプル・プログラム
Buri のプロジェクトにはいくつかのサンプル・プログラムが含まれていますが、その中から example.org.seasar.buri.test.BaoTest クラスの testNomalOrderTx メソッドをピックアップして解説します。BaoTest では高水準インタフェースである BAO の利用例を見ることができます。ここでは全体の感じを掴んでいただくことが目的なので、一つひとつの機能を詳しく解説することは致しません。この解説によって Buri を利用すると何ができるのかを実感していただけたら幸いです。
今回取り上げる BaoTest では商品の注文処理に関するワークフローを扱います。BaoTest で使用する XPDL には{請求,注文,出荷,出荷詳細}という四つのフロー(XPDL の用語ではプロセス)が含まれており、互いに関連しています。また、それぞれプロセスには対応する BAO が存在します:
- billBao - 請求
- orderBao - 注文
- shippingBao - 出荷
- shippingItemBao - 出荷詳細
各 BAO は対応するフローに対するアクセスを提供します。これらのインタフェースの実装は Buri によって行われます。
実行
それでは BaoTest を実行してみてください。実行すると大量のログが出力されると思いますが、ほとんどはデータベース関係のものです。BaoTest を実行しただけでは何が行われているのか分かりませんが、ここではテストケースが正しく終了することを確認してください。
解説
testNomalOrderTx メソッドの先頭では Buri の実行エンジンである org.seasar.buri.engine.BuriEngine の取得と、実行するワークフローを指定する XPDL ファイルの読み込みが行われています:
// 本当はburi2.diconに書くもの
BuriEngine buriEngine = (BuriEngine)getComponent("BuriSimpleEngine");
buriEngine.readWorkFlowFromResource("wakanagoxpdl/orderBao.xpdl","注文管理");
XPDL ファイルの読み込みは readWorkFlowFromResource メソッドで行います。readWorkFlowFromResource メソッドの第一引数には読み込む XPDL ファイル、第二引数には実行するパッケージ(XPDL の最上位要素)を指定します。
その直後の
customerSetup();
itemSetup();
List datas = null;
OrderInfoDto orderInfoDto1 = orderSetup1();
では、それぞれテストで使用する DTO(CustomerDto 及び ItemDto)のインスタンス(客1,客2,商品1,...,商品5)が生成されます。datas は BAO のデータ取得系のメソッドの戻り値を受け取るための変数です。そして orderSetup1 メソッドでは「客1」が「商品2」と「商品3」を購入するという情報を持った OrderInfoDto が作成されます。orderSetup1 メソッドの戻り値は orderInfoDto1 に設定され、以降、orderInfoDto1 オブジェクトがワークフロー上で管理されます。
ここから後は BAO を使った具体的な処理に入ります。次の行では orderBao の order メソッドによって注文処理が開始されます。換言すると orderInfoDto1 のワークフロー上でのライフサイクルが開始されます。(「///」で始まるコメントは説明の便宜上追加したものです。)
/// 注文を行う(注文フロー)
orderBao.order(orderInfoDto1);
ワークフロー上に投入されたオブジェクト(orderInfoDto1)は、適切なアクティビティまで遷移します。そして次の行ではワークフロー上のオブジェクトを取得します。
/// 出荷作業中のオブジェクトを取得(注文フロー)
datas = orderBao.getUnderWork();
assertEquals(datas.size(),1);
System.out.println(datas);
orderBao.getUnderWork メソッドはワークフロー上の特定のアクティビティ(実際には「出荷作業中」というアクティビティ)にあるオブジェクトを取得するものです。ここでは orderBao.order によって追加されたオブジェクト(orderInfoDto1)が取得されます。そして最後の System.out.println メソッドによって、取得された orderInfoDto1 の情報が出力されます(「↓」は表示上の都合で入れた改行を表します):
[{[/orderTitleID=110/orderDate=2007-02-23 18:44:11.936/customerID=225/status=↓
0]/orderDetail=[[/orderDetailID=219/orderCount=1/itemID=562/orderTitleID=110]↓
, [/orderDetailID=220/orderCount=2/itemID=563/orderTitleID=110]]}]
orderBao.order では注文フローが開始されるとともに、その裏側で出荷フローと出荷詳細フローも開始されるようになっています。次の部分では出荷フローと出荷詳細フローに投入されたオブジェクトの取得を行っています。
/// 出荷依頼中のオブジェクトを取得(出荷フロー)
datas = shippingBao.getNowWaiting();
assertEquals(datas.size(),1);
System.out.println(datas);
/// 商品準備中のオブジェクトを取得(出荷詳細フロー)
datas = shippingItemBao.getItemWaiting();
assertEquals(datas.size(),2);
System.out.println(datas);
最後の System.out.println メソッドによって次のような出力が行われます:
[{[/shippingID=110/shippingDate=9970-12-31 23:59:59.0/orderTitleID=110/custom↓
erID=225]/items=[[/shippingItemID=219/orderDetailID=219/shippingID=110], [/sh↓
ippingItemID=220/orderDetailID=220/shippingID=110]]}]
[[/shippingItemID=219/orderDetailID=219/shippingID=110], [/shippingItemID=220↓
/orderDetailID=220/shippingID=110]]
ここまでの内容で、orderBao.order メソッドの実行の裏側で多くの処理が行われていることがお分かりいただけたと思います。
さらに次の部分では、ワークフロー上のオブジェクトを状態遷移させています。
ShippingItemDto shippingItemDto = (ShippingItemDto)datas.get(0);
/// 商品準備完了状態のオブジェクトを取得(出荷詳細フロー)
shippingItemBao.endShipping(shippingItemDto);
shippingItemBao の endShipping メソッドは商品の出荷準備が完了したときに呼び出すメソッドです。これによりワークフロー上のオブジェクトがさらに状態遷移していきます。これ以降も BAO というインタフェースを介してオブジェクトの状態遷移や、特定のアクティビティにあるオブジェクトの取得を行いながら、ワークフローの実行が続けられていきます。
OrderBao の中を見る
テストケースの内容を一通り見ましたので、次は BAO の内容を見てみることにしましょう。OrderBao(org.seasar.buri.bao パッケージ)の内容を見ると、以下のようになっています:
public interface OrderBao {
public static Class TARGETDTO = OrderInfoDto.class;
public static String PROCESS = "注文管理.注文";
public static BuriConvert CONVERTER[] = new BuriConvert[]{
new BuriConvert(Long.class,"OrderInfoDao.getOrderInfo(#data)")
};
public static String getUnderWork_ACTIVITY = "出荷作業中";
public List getUnderWork();
public static String getEndShipping_ACTIVITY = "出荷終了";
public List getEndShipping();
... (中略)
public static String cancel_ACTIVITY = "出荷作業中,出荷終了";
public static String cancel_ACTION = "cancel";
public static String cancel_RESULT = "#cancelStatus";
public String cancel(long orderID);
}
BAO がインタフェースであることは既に述べましたが、BAO インタフェースの内には Buri で定義されている定数アノテーションが散りばめられています。また、OrderBao の実装クラスを探しても、どこにも見当たりません。Buri を用いた開発では、定数アノテーションを付加した BAO インタフェースを定義するだけで、その実装部分は Buri 本体が担当します。つまり、少しのアノテーションと XPDL ファイルを用意するだけで、フロー処理を行うシステムを開発することができます。
まとめ
今回は Buri の導入とサンプル・プログラムの実行を行いました。サンプル・プログラムを見たことで、ワークフローとシステム開発とのつながりや、ワークフロー・エンジンを導入するメリットを少しでも感じていただければ幸いです。また、OrderBao の内容を見たことで、Buri 特有の開発スタイルも垣間見ることができました。次回からは XPDL の作成方法や、BAO による開発の詳細について解説を行っていきます。