6.Converter

さて今日はConveter。これは、Baoの中で定義する定数アノテーションの1つです。
まずはマニュアルの該当部分を見てみますか。

CONVERTER
値の自動変換のためのアノテーションです
オプション

主キーのidを渡されてDtoを読み込んだり、他のフローなどから呼びだされたときに別のDtoから変換するためのアノテーションです。

ex)
public static BuriConvert CONVERTER[] = new BuriConvert[]{
new BuriConvert(Long.class,"FurnitureItemDao.getFurnitureItem(#data)")
};

Long.class型の引数を発見すると自動的にFurnitureItemDao.getFurnitureItem(#data)を呼びだしてその戻り値を#dataとして処理します。

要は、Baoの中に定義されているメソッドで、そのBaoが処理するべきDto以外のオブジェクトを引数として渡されたときに、そのオブジェクトをキーにして本来のDtoオブジェクトに変換するメソッドを呼び出すということね。


FurnitureManagementBaoでは、CONVETERはこう書かれてます。って、マニュアルに載ってるのとまったく一緒だけど。




    public static BuriConvert CONVERTER[] new BuriConvert[]{

        new BuriConvert(Long.class,"FurnitureItemDao.getFurnitureItem(#data)")

    };


CONVERTER定数アノテーションの型は、BuriConvertオブジェクトの配列。BuriConvertクラスのコンストラクタ引数には、変換元のオブジェクトの型と、変換する際のロジックをOGNLで記述します。ここでは第一引数にLong.class、第二引数に"FurnitureItemDao.getFurnitureItem(#data)"と書かれているので、Baoのメソッド内でLong型引数を持つものがあったら、FurnitureItemDaoのgetFurnitureItemメソッドを呼び出します。ちなみに、引数中の"FurnitureItemDao"ってのは、クラス名を示しているわけではなく、diconファイル内で定義されたコンポーネント名です。このチュートリアル中のサンプルではクラス名=コンポーネント名として記述しているので一見勘違いするかもしれませんが、S2Buri中でOGNLを書けるところは(多分)全て、このようにdicon中のコンポーネント名を使ってS2管理下のコンポーネントを呼び出せるのです。
さて、このFurnitureItemDaoのメソッドのシグネチャはこうなってます。




    public FurnitureItemDto getFurnitureItem(String furnitureID);


引数はLong型なのかと思いきやString型……。ちょっと意外。OGNLの制限で、こういうのはString型じゃないと駄目ってこと??よくわからんかったので、無理矢理引数をString型からLong型に変えてみたところ、問題なく実行できた。どっちでもいいらしい。ふーん。

このConverterの使い道としては、基本的には、テーブル中のキーとなるオブジェクトを引数に、Daoのメソッドを呼び出してDtoを引っ張ってくる、という使い方になるでしょーね。いちいち自分でDaoを呼び出さなくて済むようになると。便利!

あと注意事項としては、CONVERTERが有効なのは、Baoのメソッドの中でも、フロー遷移用のメソッドのみ、ってこと。Baoが持っているもう一つのメソッド、データ取得用のメソッド(get/select/find/countで始まるメソッド)では、CONVERTERが指定されてても変換されません。ま、役割から考えれば当然なんですけど。

また、CONVERTERアノテーションを書くと、Bao中の全てのフロー遷移用メソッドで有効になりますが、個別のメソッドごとに定義したい場合は、_CONVERTERアノテーションを使います。"メソッド名_CONVERTER"という名前で定数を宣言する以外はCONVETERアノテーションと一緒。


さて、今回はこれまで……、って思ったけど、今回はいつもより短いし時間も余ってるので、Baoのフロー遷移メソッドが呼び出されてから、実際にフロー遷移が行われるところまでのシーケンスを纏めてみた。Converterに関係ないところはざっくり省略。

簡単にここに出てくるオブジェクトの説明も書いておく(間違いあるかもしれんけど)。

オブジェクト名 説明
プログラム 自分で実装するところ
BaoInterceptor Baoの呼出しをインターセプトするクラス
BaoInvoker BaoInterceptorが、実際の処理を委譲するクラス
BaoInvokeMetaData Baoのメタデータアノテーションの情報)を保持するクラス
ScriptProcessor OGNLの解釈・実行を行うクラス
BuriEngine フロー遷移を実行するクラス

5.Bao基礎編その2

今回は、Baoの各メソッドを実行していって見ましょう。

しかし、実行するためには色々前準備が必要で面倒……。というわけで、幸いBuriソースコード中にTestが含まれている(testソースフォルダのorg.seasar.buri.bao.test.FurnitureManagementBaoTest2.java)ので、これを元にします。ただ、このままではちょっと説明しづらいので、コピーして書きなおしてみました。





01 package org.seasar.buri.bao.test;

02 

03 import java.util.Date;

04 import java.util.List;

05 

06 import org.seasar.buri.bao.FurnitureManagementBao;

07 import org.seasar.buri.dto.FurnitureItemDto;

08 import org.seasar.buri.engine.BuriEngine;

09 import org.seasar.extension.unit.S2TestCase;

10 

11 public class FurnitureManagementBaoTest2 extends S2TestCase {

12     private BuriEngine buriEngine_;

13     private FurnitureManagementBao fmBao_;

14 

15     public FurnitureManagementBaoTest2(String arg0) {

16         super(arg0);

17     }

18 

19     protected void setUp() throws Exception {

20         super.setUp();

21         include("org/seasar/buri/bao/test/dicon/FurnitureManagement.dicon");

22     }

23     

24     public void testFMBaoTx() {

25         buriEngine_.getWorkflows().readWorkFlowFromResource("資産管理","FurnitureManagement.xpdl");

26         

27         List datas;

28         //購買備品

29         FurnitureItemDto buyItemDto = new FurnitureItemDto();

30         buyItemDto.setName("T45_001");

31         buyItemDto.setType("PC");

32         buyItemDto.setAcquisitionTypeBuy();

33         buyItemDto.setAcquisition(new Date());

34 

35         //購買備品を登録

36         fmBao_.enterItem(buyItemDto);

37 

38         //リース備品

39         FurnitureItemDto leaseItemDto = new FurnitureItemDto();

40         leaseItemDto.setName("T45_002");

41         leaseItemDto.setType("PC");

42         leaseItemDto.setAcquisitionTypeLease();

43         leaseItemDto.setAcquisition(new Date());        

44 

45         //リース備品を登録

46         fmBao_.enterItem(leaseItemDto);

47 

48         //「利用中」の備品リスト取得

49         datas = fmBao_.getNowUse();

50         System.out.println("「利用中」のデータ" + datas);

51         assertEquals(2, datas.size());

52         

53         //「利用中」の備品の中から、"T45_002"という名前のDTOを取得

54         FurnitureItemDto findDto = new FurnitureItemDto();

55         findDto.setName("T45_002");

56         datas = fmBao_.findNowUse(findDto);

57         System.out.println("名前が「T45_002」のデータ" + datas);

58         assertEquals(1, datas.size());

59         

60         //「利用中」の備品の中から、タイプが"PC"であるDTOを取得

61         findDto = new FurnitureItemDto();

62         findDto.setType("PC");

63         datas = fmBao_.findNowUse(findDto);

64         System.out.println("タイプが「PC」で「利用中」のデータ" + datas);

65         assertEquals(2, datas.size());

66 

67 

68         //購買備品の使用期間終了

69         fmBao_.timeorverItem(buyItemDto.getFurnitureID());

70         

71         //「償却期間終了」の備品リスト取得

72         datas = fmBao_.getEndDepreciation();

73         System.out.println("「償却期間終了」状態のデータ" + datas);

74         assertEquals(1, datas.size());

75 

76 

77         //「利用中」の備品リスト取得

78         datas = fmBao_.getNowUse();

79         System.out.println("「利用中」状態のデータ" + datas);

80         assertEquals(1, datas.size());

81 

82         //リース備品の使用期間終了

83         fmBao_.timeorverItem(leaseItemDto.getFurnitureID());

84         

85         //「リース終了」の備品リスト取得

86         datas = fmBao_.getEndLease();

87         System.out.println("「リース終了」状態のデータ" + datas);

88         assertEquals(1, datas.size());

89 

90         //「利用中」の備品リスト取得

91         datas = fmBao_.getNowUse();

92         System.out.println("「利用中」状態のデータ"+ datas);

93         assertEquals(0, datas.size());

94         

95     }

96 

97 }


まず、このテストケースはS2Unitを利用してます。S2Unitを使っているということは、setUp()でdiconファイルをインクルードさせておくと、フィールド変数にdicon内で定義したコンポーネントが入ってきて、各テストメソッドではそのコンポーネントを利用できる、ということになります。
このソース上では21行目でdiconを読み込んでます。diconの中身はこれ。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
	"http://www.seasar.org/dtd/components21.dtd">
<components>
	<include path="bao.dicon"/>
	<include path="org/seasar/buri/dicon/allTestDao.dicon"/>
	
	<component class="org.seasar.buri.bao.FurnitureManagementBao">
		<aspect>bao.interceptor</aspect>
	</component>
</components>

FurnitureManagementBaoにbao.interceptorというASPECTが適用されてます。FurnitureManagementBaoは単なるインターフェースなので、このbao.interceptorってのがBaoの全挙動をコントロールしているということに。このbao.interceptorの中身を見たい気もしますが、とりあえずおいといて次へ。


testメソッドであるtestFMBaoTx()では、最初にXPDLを読み込んでいます。これと同等のことは、dicon内で定義することもできて、その場合には、buri2.dicon内のWakanagoFlowsImplコンポーネントのところでこう書いておけばOKです。

	<component class="org.seasar.buri.impl.WakanagoFlowsImpl">
		<aspect>BuriInterceptorChain</aspect>
		<initMethod name="readWorkFlowFromResource">
			<arg>"資産管理"</arg>
			<arg>"FurnitureManagement.xpdl"</arg>
		</initMethod>
	</component>

ここまでが準備段階、これでBaoを利用できるようになります。
testメソッドでは、まず、FurnitureManagementDtoのインスタンスを生成し、FurnitureManagementBao#enterItemメソッドを呼び出し、そのインスタンスをBaoに渡します。これでワークフローにDtoを登録したことになります。これでフローが開始されます(28〜46行目)。この例では2つのインスタンスを作っていますが、2つの大きな違いは、最初のものは購買備品(AcquisitionTypeBuyをセットしている)で、2つめがリース備品(AquisitionTypeLeaseをセットしている)ってことです。

さて次に、これらがほんとに登録されたのか参照します。これが48〜51行目。ここで実行しているFurnitureManagementBao#getNowUse()メソッドは、メソッド名が「get」で始まっているので、前回見たとおり、データ取得用のメソッドになります。そして、どんなデータを取得するかは、Baoの定数アノテーションを見れば分かります。FurnitureManagementBao内で、

    public static String getNowUse_ACTIVITY = "利用中";

このように記述されてますので、これは、「利用中」状態のデータを取得するメソッド、ということが分かります。enterItemメソッドでデータを登録すると、まず「利用中」状態になる、ってことです。
でも、ここでフロー定義を思い出してください(下図参照)。定義上では、「利用中」より前に「備品登録」という状態があります。

この「備品登録」はなぜ飛ばされてしまったのでしょうか?その答えは、「備品登録」と「利用中」の2つのプロパティを見比べてみればわかります。「備品登録」の方は、finish modeがAutomatic、「利用中」の方は、manualと定義されてます。Automaticの場合には、その名の通り自動的に終了し、次の状態に移ってしまうのです。一方manualだと、勝手には終了することはなく、そこで待機することになります。

では、さらにテストメソッドの解説を進めていきます。53〜65行目ではFurnitureManagementBaoのfindNowUseメソッドを実行しています。このメソッドは、「find」で始まってるので、データ取得用メソッドです。ですが、このメソッドはFurnitureManagementDtoを引数にしています。この場合引数のDtoは、取得の条件を表すオブジェクトという意味になります。引数のDtoと同一のプロパティを保持するデータのみを取得します。
53〜58行目の場合、nameプロパティが「T45_002」であるDtoのみ取得しているため、返り値は1件のみ。一方、60〜65行目では、typeプロパティが「PC」であるDtoを取得しようとしており、2つとも返ってきていることを確認しています。

さて次は、この「利用中」の状態から、別の状態に遷移させてみましょう。今回のフローでは「利用中」の次は「期間終了」。この遷移のときに呼びだすメソッドとして、FurnitureManagementBaoではtimeorverItemメソッド(timeoverのスペルミス?)が定義されています。このメソッドを呼び出しているのが、68〜93行目。前半の68〜80行目は、timeorverItemメソッドに、購買備品データのIDを渡して呼び出してます。呼び出した結果、「償却期間終了」状態のデータを取得するgetEndDepreciationメソッドでこの購買備品のデータを取得できるようになり、「利用中」状態のデータを取得するgetNowUseメソッドの返り値には、購買備品データが無くなりました。購買備品が「利用中」状態から「償却期間終了」状態へ遷移したのです。そして同様に、82〜93行目ではリース備品に対してtimeorverItemメソッドを呼んでます。こちらは、「利用中」状態(getNowUseで取得できる状態)から「リース終了」状態(getEndLeaseで取得できる状態)へ遷移しています。

同じメソッドを実行したのになぜ遷移先が変わるのか……。これは、フローの定義を見ていく必要があります。
まず、データ遷移用のメソッド、timeorverItemメソッドが呼ばれたことで、「利用中」状態から「期間終了」状態へ遷移します。そして、この「期間終了」Activityの定義では、Finish modeがAutomatic。だから、自動的に次の状態に移ることになります。だけど、今回の場合は、遷移先が2つに分かれているので、どっちに遷移するのかこれだけでは分かりません。そこで、その条件が定義されている必要があります。この定義に関しては、第三回で纏めてたのですが、再掲します。XPDL内「期間終了」ActivityのPostconditionとして、こんな感じの定義が記述されているのです。

遷移 条件
期間終了→償却期間終了 #data.isBuying == true
期間終了→リース終了 #data.isLease == true

#dataというのがDtoインスタンスを示しています。そのインスタンスのisBuyingがtrueなら(購買備品なら)「償却期間終了」Activityへ遷移、isLeaseがtrueなら(リース備品なら)「リース終了」Activityへ遷移する、というのがこの定義の言わんとしているところなのです。

さて、これでFurnitureManagementBaoのメソッドは一通り実行したことに。なんとなく使い方は分かってきたかな……。
要は、Baoの役割は、ワークフロー上のデータを取得するのと、そのデータを遷移させるのと、2つの役割があるよ、ってことっすね。インターフェース定義するだけでこれだけのことが出来るってのは便利便利。
次回は、前回スルーしてたConverterを見てみるかな?、ということで今回はおしまい。

impressionバトン

izuちんから回ってきたので書く。っていうか、3ヶ月くらい前にほとんど同じバトンを他の人からMixiで受け取った訳だが……(^^;
ま、でも問題が微妙に変わってる。伝言ゲーム的に、少しずつ形を変えながらいろんな人を経由してたのかなぁ、と思うとおもしろいね〜。

(1)バトンを回してくれた人の印象

ネット上ではもう2年以上の付き合い(そんなに経つのね……)だけど、リアルで会ったことあるの1回だけやし、基本的に偏見で(ぉ

  • 曲がった事が嫌い
  • 情に厚い
  • うんこ

(2)周りから見た自分はどんな人だと思う?

  • 意外と乗せられやすいタイプ
  • 計算高そうでそうでもない
  • マゾ
  • 大雑把

(3)自分が好きな人間性について5個。

  • ポジティブな人
  • 努力してる人
  • 有限実行な人
  • 細かい配慮が出来る人
  • 素直な人

(4)では、反対に嫌いなタイプは?

ほとんど裏返しで……

  • ネガティブな人
  • 努力しないで愚痴ばかり言う人
  • 間違ったことをしても認めない人
  • 自分のことしか考えない人
  • ケチな人

(5)自分がこうなりたいと思う理想像は?

周囲の人にプラスの影響を与えられる人

(6)回す人5人と印象

回されたい方是非受け取ってください〜。

4.Bao基礎編その1

前回から2週間も空いてしまった……orz ごめんなさいごめんなさい。このペースじゃ駄目駄目です。気合入れないと。
ちなみにS2Buri勉強会は私用により参加不可能。無念だ……。


さて、今まではXPDLを見てきましたが、今日はJavaの実行コードを見ていきます。
XPDLで定義したワークフローにアクセスするためのインターフェースとなるのが、Baoです。
Baoとは、リファレンスの説明によれば、"BusinessLogic Access Object"だとか。一説によるとBaoのBはBuriのBだという話も……。
今まで見てきたFurnitureManagement.xpdlを利用するBaoがBuriのtestソースフォルダ中にあるので、中身を見てみましょう。

package org.seasar.buri.bao;

import java.util.List;

import org.seasar.buri.bao.BuriConvert;
import org.seasar.buri.dto.FurnitureItemDto;

public interface FurnitureManagementBao {
    public static String PROCESS = "資産管理.備品管理";

    public static BuriConvert CONVERTER[] = new BuriConvert[]{
        new BuriConvert(Long.class,"FurnitureItemDao.getFurnitureItem(#data)")
    };
    
    public static String getNowUse_ACTIVITY = "利用中";
    public List getNowUse();
    
    public static String findNowUse_ACTIVITY = "利用中";
    public List findNowUse(FurnitureItemDto findDto);
    
    public static String getEndDepreciation_ACTIVITY = "償却期間終了";
    public List getEndDepreciation();
    
    public static String getEndLease_ACTIVITY = "リース終了";
    public List getEndLease();
    
    public void enterItem(FurnitureItemDto dto);

    public void timeorverItem(long itemID);
}

ただのJavaのインターフェースです。
ただし、PROCESSとかCONVERTERとかACTIVITYとか名前がついた定数が書いてあるのが特徴的。この書き方はS2Daoの定数アノテーションに似てますね……。と思ってリファレンス中の説明を読むと、やはり定数アノテーションと書いてありますね。それに、このFurnitureManagementBaoはインターフェースですが、それに対応する実装クラスがソース中のどこにもありません。これもS2Dao同様。

さて、それはさておき、Baoの中を1行1行みていってみますか。
まず最初の行が、PROCESSアノテーション

    public static String PROCESS = "資産管理.備品管理";

これは、このBaoがどのワークフロー(プロセス)を扱うかを指定するアノテーションになります。この場合、「資産管理」パッケージの「備品管理」プロセスを示してます。XPDLの構造では、まずパッケージがあってその中にプロセスがある、という形になってるので、こういう形式で指定することが出来るわけですね。このPROCESSアノテーションは、Bao内に1つ定義しておく必要があるようです。

で次に出てくるのがCONVERTERアノテーションなんですが……。ちょっと説明省略。次のやつを説明してからの方がよいと思われるので(^^;


というわけでCONVERTERは飛ばし。そしてそれ以降の箇所なんですが、ここは基本的にメソッドと、メソッドに付随するアノテーションです。Baoのメソッドは、シグネチャによってメソッドの振る舞いが変わります。これもS2Daoと同様。Convention Over Configurationの一種ですね。詳しいルールは、マニュアルの方に載ってるのでそっち参照。そこにいろいろ書いてあるのですが、一番重要なのが、メソッド名に関して。メソッド名がget/select/find/countで始まる場合、データ取得用のメソッドと見なされます。それ以外ならば、フロー処理を行うメソッドとみなされます。ここで挙げた「データ取得」と「フロー処理」の二つが、Baoが持ってる二大処理、と思ってよさげ?

FurnitureManagementBaoの例でいうと、以下の4つがデータ取得用メソッドです。

  • getNowUse()
  • findNowUse(FurnitureItemDto findDto)
  • getEndDepreciation()
  • getEndLease()

残りの以下2つがフロー処理用メソッド。

  • enterItem(FurnitureItemDto dto)
  • timeorverItem(long itemID)

この各メソッドの役割ですが、これは実際に動作させてみて比較するのがよいかな……、ということで次回へ。