jBPMでタスクを動的に生成する

こんにちは、平田です。

技術ブログのトップ記事がいつまでも恨み節では縁起が悪いので、現在調査中の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());
    }
}

これにてステークホルダー全員に書類が回覧されるようになりました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です