こんにちは、平田です。
技術ブログのトップ記事がいつまでも恨み節では縁起が悪いので、現在調査中のjBPMについて、メモ代わりに公開します。
今回調査したのはjBPMでタスクを動的に生成する方法です。
例えば書類を作成し、回覧するようなフローがあるとします。ここで、回覧者の人数を実行時に決めたい場合、どうすれば良いでしょうか。
何も考えずにjBPMのタスクノードにタスクを定義した場合、タスクノードに遷移した時点で一件だけタスクが生成され、要件を満たせません。
こんなときはデフォルトのタスク生成処理をキャンセルし、node-enterイベントにてタスクを生成します。
<process-definition name="kairan"> <start-state name="開始"> <transition to="作成"/> </start-state> <task-node name="作成"> <task name="回覧に出す"/> <transition to="回覧"/> </task-node> <task-node name="回覧" create-tasks="false"><!-- デフォルトの生成処理をキャンセル --> <task name="確認する"/> <event type="node-enter"> <action name="確認するタスク生成" class="com.natswell.jbpm.kairan.TestKairan$ConfirmTasksGenerator"/> </event> <transition to="終了"/> </task-node> <end-state name="終了"/> </process-definition>
Javaの実装は以下の様になります。
public class TestKairan extends TestCase { public static class ConfirmTasksGenerator implements ActionHandler { public static final String STAKEHOLDERS = "stakeholders"; @Override public void execute(ExecutionContext executionContext) throws Exception { List<String> stakeholders = (List<String>) executionContext.getVariable(STAKEHOLDERS); Task confirmTask = ((TaskNode) executionContext.getProcessDefinition().getNode("回覧")).getTask("確認する"); for (String each : stakeholders) { TaskInstance confirm = executionContext.getTaskMgmtInstance().createTaskInstance(confirmTask, executionContext); confirm.setActorId(each); } } } /** * [開始] → 作成 → 回覧 → [終了] */ static final ProcessDefinition kairanDef = ProcessDefinition.parseXmlResource("com/natswell/jbpm/kairan/kairan/processdefinition.xml"); static final List<String> stakeholders = Arrays.asList( new String[] { "なかい", "きむら", "いながき", "くさなぎ", "かとり" }); ProcessInstance proc; Token token; @Override protected void setUp() throws Exception { proc = new ProcessInstance(kairanDef); token = proc.getRootToken(); } /** * 開始 * ↓ * 作成 ... 5人に回覧する * ↓ * 回覧 ... 5人が確認する * ↓ * 終了 */ public void test任意の数の_確認する_タスクを生成できる() { do開始(); do作成(stakeholders); { assertNode("回覧"); Collection<TaskInstance> tasks = unfinishedTasks(); assertEquals("回覧する人数分だけ「確認する」タスクが生成されたはず", stakeholders.size(), tasks.size()); for (TaskInstance confirm : tasks) { assertEquals("「確認する」タスクのみのはず", "確認する", confirm.getName()); assertTrue("回覧する人にアサインされているはず", stakeholders.contains(confirm.getActorId())); confirm.end(); } } do終了(); } void do終了() { assertNode("終了"); } void do作成(List<String> stakeholders) { assertNode("作成"); Collection<TaskInstance> tasks = unfinishedTasks(); assertEquals("「回覧に出す」タスク一件のみのはず", 1, tasks.size()); TaskInstance send = tasks.iterator().next(); // 回覧に出す際、人数を指定する send.setVariable(ConfirmTasksGenerator.STAKEHOLDERS, stakeholders); send.end(); } void do開始() { assertNode("開始"); token.signal(); } void endTasks(int count) { List<TaskInstance> taskList = new ArrayList<TaskInstance>(unfinishedTasks()); for (int i = 0; i < count; i++) taskList.get(i).end(); } Collection<TaskInstance> allTasks(String name) { Collection<TaskInstance> namedTasks = new ArrayList<TaskInstance>(); Collection<TaskInstance> tasks = proc.getTaskMgmtInstance().getTaskInstances(); for (TaskInstance each : tasks) { if (each.getName().equals(name)) namedTasks.add(each); } return tasks; } Collection<TaskInstance> unfinishedTasks() { return proc.getTaskMgmtInstance().getUnfinishedTasks(token); } void assertNode(String nodeName) { assertSame(kairanDef.getNode(nodeName), token.getNode()); } }
これにてステークホルダー全員に書類が回覧されるようになりました。