→jBPM Suite関連Tips
#contents
※以下のTipsは jBPM Ver.6.0.1(コミュニティ版)で調査したものです
*関連Web [#bb4fd608]
-[[How to add users/groups to Human Task Service in BRMS / BPM Suite>https://access.redhat.com/solutions/155533]]
-[[How to implement a custom UserGroupCallback in BPM Suite 6>https://access.redhat.com/solutions/1149763]]
-[[How to implement Task Event Listeners in BPM Suite 6.0 ?>https://access.redhat.com/solutions/883683]]
*タスクからタスク変数を参照する [#j66e5531]
//このやり方で一応取得できるようですが、もっと簡単な方法がないのだろうか…
public Map<String, Object> getTaskVariable(KieSession ksession,
TaskService taskService, TaskSummary task) {
Task t = taskService.getTaskById(task.getId());
TaskData td = t.getTaskData();
long contentId = td.getDocumentContentId();
Content content = taskService.getContentById(contentId);
byte[] contBytes = content.getContent();
Environment env = ksession.getEnvironment();
@SuppressWarnings("unchecked")
Map<String, Object> data = (Map<String, Object>)
ContentMarshallerHelper.unmarshall(contBytes,env);
return data;
}
-もっと簡単なやり方として、以下でも同じ効果がある
--TaskServiceをInternalTaskServiceにキャストする
--InternalTaskService#getTaskContent(taskId)を呼ぶ
-WorkItemを取得して getParameter でも良い。
-なおタスク変数を使うには、タスクノードのプロパティでタスク変数のマッピング設定を行い、以下のタグがBPMNのXML内に設定されている必要があります。
・ioSpecification
・dataInputAssociation
・dataOutputAssociation
-タスク変数に独自の型(自分で作ったクラス)を指定したい場合は、プロセスのImportsの設定にそのクラスを指定すれば良いです。そのクラスは Serializable を実装している必要があります。
-似たやり方でWorkItemIdも取得できるが、このIDでWorkItemを取得できるわけでもないので、あまり使い道がなさそう…
public long getWorkItemIdFromTask(TaskService taskService, TaskSummary task) {
Task t = taskService.getTaskById(task.getId());
TaskData td = t.getTaskData();
long wid = td.getWorkItemId();
return wid;
}
-タスク変数のアウトプット側変数のセットは、TaskService#complete の第3引数に Mapを作って渡してやればOKです。
-【重要】プロセス変数とタスク変数のInputでマップするときに、プロセス変数の IDとName を同じ値にしないとなぜか変数値がタスク変数に設定されません。もしかしてバグ?
*ヒューマンタスクからWorkItemを取得するには [#p0904e27]
-サンプルその1
@SuppressWarnings("restriction")
private void dispHumanTaskWorkItem(ProcessInstance processInstance, long tid){
WorkflowProcessInstance wpi = (WorkflowProcessInstance) processInstance;
NodeInstance ni = wpi.getNodeInstance(tid);
if ( ni instanceof HumanTaskNodeInstance ) {
HumanTaskNodeInstance htni = (HumanTaskNodeInstance) ni;
WorkItem wi = htni.getWorkItem();
System.out.println("wi=" + wi.toString());
} else {
System.out.println("Not Human Task error");
}
}
-サンプルその2
@SuppressWarnings("restriction")
private void dispHumanTaskParams(ProcessInstance processInstance) {
System.out.println("WorkItemによるparameter表示開始");
WorkflowProcessInstance wpi = (WorkflowProcessInstance) processInstance;
Collection<NodeInstance> ndis = wpi.getNodeInstances();
for( NodeInstance ni : ndis) {
if ( ni instanceof HumanTaskNodeInstance ) {
HumanTaskNodeInstance htni = (HumanTaskNodeInstance) ni;
WorkItem wi = htni.getWorkItem();
Map<String,Object> prms = wi.getParameters();
for (String key : prms.keySet()) {
System.out.println(key + " = " + prms.get(key));
}
// 試しにこんなふうに パラメータ値をいじってもWorkItemの持っているハッシュ値が変わるだけであって、
// 実際にタスク定義している値には反映されない模様
WorkItemImpl wip = (WorkItemImpl)wi;
wip.setParameter("GroupId", "admins"); // グループIDが変えられるかと思ったが、できず
wip.setParameter("input1", "modified value"); // タスク変数の値を修正してみたが、反映されず
//htni.setVariable("GroupId", "admins"); // これはKnowledgeRuntimeがnullで例外になります
}
}
System.out.println("WorkItemによる表示終了");
}
*ヒューマンタスクのWorkItemHandlerの実装は? [#t0746159]
-org.jbpm.services.task.wih.LocalHTWorkItemHandler
--RuntimeManagerで管理されているもの。これが本筋と見られる
-java.org.jbpm.services.task.wih.NonManagedLocalHTWorkItemHandler
--RuntimeManagerで管理されていないもの。互換のためにあるらしい
*xmlファイルからのタスクパラメータの読み込み [#k444d18b]
-このあたりでやっている模様
--org.jbpm.bpmn2.xml.TaskHandler
*タスクのポテンシャルオーナーを追加するサンプル [#labf85ab]
-AssignmentServiceのソースが参考になる
-ただし、この方法で追加してもtaskService#getTaskByIdでタスクを取得しなおすとリセットされてしまう
package org.jbpm.services.task.internals.rule;
import java.util.List;
import org.kie.api.task.model.Group;
import org.kie.api.task.model.OrganizationalEntity;
import org.kie.api.task.model.Task;
import org.kie.internal.task.api.TaskModelProvider;
import org.kie.internal.task.api.model.InternalOrganizationalEntity;
public class AssignmentService {
public void assignTask(Task task) {
List<OrganizationalEntity> potentialOwners = task.getPeopleAssignments().getPotentialOwners();
potentialOwners.clear();
Group group = TaskModelProvider.getFactory().newGroup();
((InternalOrganizationalEntity) group).setId("Crusaders");
potentialOwners.add(group);
}
}
*タスクの担当GroupID,ActorIDを動的に変更する方法 [#z049e0d8]
-プロセス定義の時点でGroupIDやActorIDに以下の書式でプロセス変数を設定できる
#{processVar}
-このprocessVarをプロセス変数として定義し、プログラムから入れ替えてやれば担当者や担当グループが動的に変更できます。
*TaskService の実装解析メモ [#q61e7475]
-InternalTaskService は TaskService を実装しているが、これもインターフェイス
-このインターフェイスは addGroup や addUser というメンバーを持っている。
-しかしこのメソッドは セッション全体に対するもので、タスクをターゲットとしたものではありません。
-org.jbpm.runtime.manager.impl.task.SynchronizedTaskService が実装らしくメンバーとして org.jbpm.services.task.impl.command.CommandBasedTaskService を持っている
-実際の処理は CommandBasedTaskService でやっているが、CommandBasedTaskService はメンバにCommandService という Executor を持っており、これが実際の処理をコマンドとして実行する。
-たとえば、 TaskService#activate メソッドの処理の実態は ActivateTaskCommand#execute である
-これをさらに手繰っていくと TaskInstanceServiceImpl#activate に至る
-この処理は LifeCycleManager というインターフェイスに渡され、 その実装は MVELLifeCycleManager#taskOperation らしい
-さらに同クラスの evalCommands > commands が呼ばれる
-実装の階層は
SynchronizedTaskService>InternalTaskService>TaskService
-参考:TaskService#claim を呼んだ場合のスタックトレースの例
at org.jbpm.services.task.internals.lifecycle.
MVELLifeCycleManager.evalCommand(MVELLifeCycleManager.java:98)
at org.jbpm.services.task.internals.lifecycle.
MVELLifeCycleManager.taskOperation(MVELLifeCycleManager.java:322)
at org.jbpm.services.task.identity.
UserGroupLifeCycleManagerDecorator.taskOperation(UserGroupLifeCycleManagerDecorator.java:46)
at org.jbpm.services.task.impl.TaskInstanceServiceImpl.claim(TaskInstanceServiceImpl.java:117)
at org.jbpm.services.task.commands.ClaimTaskCommand.execute(ClaimTaskCommand.java:51)
at org.jbpm.services.task.commands.ClaimTaskCommand.execute(ClaimTaskCommand.java:33)
at org.jbpm.services.task.commands.
TaskCommandExecutorImpl$SelfExecutionCommandService.execute(TaskCommandExecutorImpl.java:65)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:41)
at org.jbpm.services.task.persistence.TaskTransactionInterceptor.execute(TaskTransactionInterceptor.java:54)
at org.jbpm.services.task.commands.
TaskCommandExecutorImpl.execute(TaskCommandExecutorImpl.java:40)
at org.jbpm.services.task.impl.command.CommandBasedTaskService.claim(CommandBasedTaskService.java:136)
at org.jbpm.runtime.manager.impl.task.SynchronizedTaskService.claim(SynchronizedTaskService.java:100)
at com.sample.ProcessTest.testProcess(ProcessTest.java:97)
*claimについて [#cb90c077]
-他のユーザがclaim済み(ステータス=Reserved)のタスクを他のユーザで claimなりstart させようとするとPermissionDeniedException がスローされます
-メッセージの例
org.jbpm.services.task.exception.PermissionDeniedException:
User '[UserImpl:'mary']' does not have permissions to
execution operation 'Start' on task id 1
-また、claim したユーザはそのタスクの ActualOwner に設定されます。
-同じユーザで2回claimするのもダメです。同様のエラーになります。
*delegateとforwardの違い [#va1e6d27]
-delegateは ActualOwner から任意の他のユーザーへActualOwnerを引き継ぐ。
-delegateで引き継ぎ先に指定されたユーザーはポテンシャルオーナーに追加され、ActualOwnerになる。
-delegateの引き継ぎ元、引き継ぎ先にはグループは指定できない。
-delegateの引き継ぎ先ユーザーはタスクで候補指定されたグループにいない人でもOK
-forwardはポテンシャルオーナー(ActorId)に登録されているユーザーからフォワード先のユーザーへポテンシャルオーナーを交代する。
-フォワード先のユーザーはActualOwnerにはならず、ポテンシャルオーナーになるだけ
-フォワード元のユーザーはフォワードしたらポテンシャルオーナーから消える
-フォワード元のユーザーはプロセスのGroupIdに指定されたグループに所属しているだけではダメ。ActorIdに個人として登録されている必要がある。(ポテンシャルオーナーとして入れ替わる場所がないとダメ)
-フォワード先にグループも指定できる。ただしこのグループはフォワード元のユーザーが属しているグループでなくてはダメ
-フォワード先にユーザーを指定する場合は、フォワード元のユーザーが属しているグループに属していなくても良い。(グループは属してないとダメなのに…不可解な仕様です)
-フォワード元にグループは指定できない。
-つまり、delegateとforwardを組み合わせることによって、候補のユーザーやグループを動的に追加していくことが可能となる。(ただし、プロセス定義が書き換わるわけではなく、そのプロセスインスタンスのみの変更です)
*stop/releaseについて [#t90bad8e]
-stopは、startしてInProgressになっているタスクをReservedなステータスへ戻す。
-ちなみに、ReservedからReadyに戻すにはreleaseする。(forwardでもそうなるが、その場合はポテンシャルオーナーも変わる)