こんにちは、平田です。
技術ブログのトップ記事がいつまでも恨み節では縁起が悪いので、現在調査中の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());
}
}これにてステークホルダー全員に書類が回覧されるようになりました。