入門記9:動かしてみる。(「あなたは右に、わたしは左に、振り向いたら負けよ」編)
BuriAutoSelectProcessorの次は権限だ!
と思っていたのですが、ぶり祭りにて羽生さんに「いや分岐だろ、jk。っていうか、Ebiのことも思い出してあげてください(w」と言われたので、Ebiはさておきとりあえず分岐させます。
この機会に、まっとうなサンプルを考えてみようかな?とも思ったのですが、今のタイミングだとBuri以前にそのサンプルにはまりそうなのでもう少し我慢します。
今回はこんなフローを書きました。
「登録」→「公開中」の後のルートが即「公開終了」とするケースと一度「印刷待ち」を挟む場合と分岐する、XORの分岐と合流のパターンを使ったフローです。(実際には印刷完了をハンドリングする何かがないと実用的じゃないと思いますが)
それぞれのアクティビティに設定するModeは・・・
Activity | Start mode | Finish mode |
---|---|---|
登録 | 空欄(Automatic) | 空欄(Automatic) |
公開中 | 空欄(Automatic) | Manual |
公開終了 | 空欄(Automatic) | Manual |
印刷待ち | 空欄(Automatic) | Manual |
です。
そして、「公開中」から延びる矢印(トランジション)のうち、「公開終了」に伸びる線に以下のような設定をします。
- type=[Condition]を選ぶ。
- Extended attributes, 1 element(s)のNameに[action]、Valueに[close]を設定。
続いて、「公開中」から「印刷待ち」に伸びる線には・・・
- type=[Condition]を選ぶ。
- Extended attributes, 1 element(s)のNameに[action]、Valueに[print]を設定。
そして以下のようなドライバを作ります。
今回、toNextStatusのほかに、toNextStatusActionというメソッドを使いました。
toNextStatusは、アクティビティ名を指定しないで使う、というトライを1つしてみました。ぶり祭りの時、id:makotanから「動くよ、でもデバッグが必要になった時、軽く死ねるよ」と言われていたので非推奨って位置づけなのですが、まぁ試してみたかったんです。
また、toNextStatusActionですが、そのシグネチャは「void toNextStatusAction(String path, Object data, Object userData, Object action)」で、pathは以下のコードにあるようにフローから組み立てられたパッケージ名+プロセス名+アクティビティ名(パッケージ名はXPDLで定義したものではなく、buri-user.diconに指定した名前が適用されるようですが)を、dataはDtoのインスタンスを、userDataには権限管理をする場合に使用するようで今回は仕様しないのでnullを、actionは文字列で矢印に指定したものを指定します。
DocumentProcessorTest2 extends S2TestCase { private static final String DICON_PATH = "app.dicon"; static final String FLOW_PACKAGE = "文書管理パッケージ1."; static final String FLOW_PROCESS = "文書管理プロセス3."; @Override protected void setUp() throws Exception { super.setUp(); super.include(DICON_PATH); } @SuppressWarnings("unchecked") public void testTx() { BuriAutoSelectProcessor processor = (BuriAutoSelectProcessor) super.getComponent(BuriAutoSelectProcessor.class); DocumentDto doc1 = new DocumentDto("文書A", "文書Aです。"); // toNextStatus(1) processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS + "登録", doc1, null); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // toNextStatusAction(1) processor.toNextStatusAction(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, null, "close"); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // パスをちゃんと指定しないでtoNextStatus(1) processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS, doc1, null); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // もっかい // toNextStatus(2) processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS + "登録", doc1, null); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // toNextStatusAction(2) processor.toNextStatusAction(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, null, "print"); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // パスをちゃんと指定しないでtoNextStatus(2) processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS, doc1, null); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // toNextStatus(1) processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, null); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); } }
結果はオールグリーン。ちゃんと動きました。
続いて、今回のタイトルでもある「振り向いたら負け」の部分。
こんなフローが必要になるようです。(BuriにはtoPrevStatus、toBackStatusは存在しない)
で、以下のように設定。
- 「印刷待ち」から「公開終了」に伸びる線に以下を設定。
- type=[Condition]を選ぶ。
- Extended attributes, 1 element(s)のNameに[action]、Valueに[close]を設定。
- 「印刷待ち」から「Dummy」に伸びる線に以下を設定。
- type=[Condition]を選ぶ。
- Extended attributes, 1 element(s)のNameに[action]、Valueに[back]を設定。
最後に追加した「Dummy」のアクティビティはStart modeもFinish modeも空欄(Automatic)にしておくことで、「印刷待ち」から[back]の指定を受けた後、そのまま「公開中」に差し戻されます。ぶり祭りで一瞬話題に上った「なかった事にするメソッド」です。
という訳で動かしてみました。
ちゃんと、「なかった事」になりました。(Buriが履歴を管理してくれているので、キレイになかった事になる訳ではないですが)
public class DocumentProcessorTest3 extends S2TestCase { private static final String DICON_PATH = "app.dicon"; static final String FLOW_PACKAGE = "文書管理パッケージ1."; static final String FLOW_PROCESS = "文書管理プロセス4."; @Override protected void setUp() throws Exception { super.setUp(); super.include(DICON_PATH); } @SuppressWarnings("unchecked") public void testTx() { BuriAutoSelectProcessor processor = (BuriAutoSelectProcessor) super.getComponent(BuriAutoSelectProcessor.class); DocumentDto doc1 = new DocumentDto("文書A", "文書Aです。"); // toNextStatus processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS + "登録", doc1, null); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // toNextStatusAction processor.toNextStatusAction(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, null, "print"); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // 振り向く processor.toNextStatusAction(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, null, "back"); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // toNextStatusAction processor.toNextStatusAction(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, null, "close"); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(1, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); // パスをちゃんと指定しないでtoNextStatus processor.toNextStatus(FLOW_PACKAGE + FLOW_PROCESS, doc1, null); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開中", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "公開終了", doc1, DocumentDto.class).size()); assertEquals(0, processor.getDataListFromPath(FLOW_PACKAGE + FLOW_PROCESS + "印刷待ち", doc1, DocumentDto.class).size()); } }
この「なかった事にするメソッド」はともかく、toBackStatus、toPrevStatusですが、Buriのメイン開発者は「要らん」と断言されています。理由は簡単で、単純にステータスを以前のものに戻す、という事が「お客様の業務」として基本的に無いからではないか、と僕は考えています。
お客様の業務で「以前の状態」に戻すという場合、戻すための手続きなりが必ずあって、それはフロー上にちゃんと表現されていないとおかしい筈なのです。だから、今後どんなにBuriが普及しようとも利用者が増えようとも、toBackStatus、toPrevStatusが設けられる事はないんだろうと思います。唯一、今後内部統制なんかと同じノリでそういったフローが存在する制度が出来たら、もしかしたらあるかも知れないな、と思ってます。
さて、このようにアクションを指定してフローの行き先を決められるだけで、結構「アプリ」っぽくなってきました!
次回は僕がBuriで一番感動したAnd-Split、And-Joinです!!