jBPM5:GenericHTWorkItemHandlerではまったこと

渡邊です。こんにちは。

jBPM5の研究をしています。
備忘録を兼ねて、GenericHTWorkItemHandlerではまったことを記します。

問題となったのは、Drools 5.5.0.Final + jBPM 5.4.0.Finalです。
JBoss World 2012 Keynoteのソースコードを参考にし、より実践的なアプリケーションを作成していました。
一通りの動作確認を終えたところで、サーバ再起動後に限り、サーバ再起動前に開始したプロセスの任意タスクを完了できないという不具合がみつかりました。

画面操作によってタスクを完了させようとしても、できませんでした。例外がスローされた訳でもないので、もう一度同じ操作をしたところ、今度は例外がスローされました。
実は、当該タスク自体は完了している(Taskテーブルのstatusが”Completed”になっている)のに、次のタスクが生成されていない(Taskテーブルに次のタスクがINSERTされていない)ことがわかりました。

話は変わりますが、あるプロセスがタスクAからタスクBに進むと同時に、そのプロセスに紐づくドメインモデルのステータスをAからBに更新する為、ProcessEventListenerを利用しました。具体的には、ProcessEventListener.afterNodeTriggeredにおいて、ドメインモデルのステータス更新処理を実装しました。
この実装では、タスクA完了直後におけるドメインモデルのステータスは、Aです。プロセスがタスクBに達した時に(ProcessEventListener.afterNodeTriggeredが実行され)、ステータスはBに更新されます。

以上により、次のような不整合が生じているにも関わらず、トランザクションは正常終了していました。

  • タスクAは完了している
  • タスクBが生成されていない
  • ドメインモデルのステータスはA
  • この不具合は、GenericHTWorkItemHandlerの初期化時に、connectメソッドを実行することで、解決しました。修正後のコードは次の通りです。

    @Create
    public void initialize() {
        processLock = new ReentrantLock(true);
        knowledgeBase = createKnowledgeBase();
    
        Environment environment = EnvironmentFactory.newEnvironment();
        environment.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManager.getEntityManagerFactory());
    
        session = JPAKnowledgeService.newStatefulKnowledgeSession(knowledgeBase, null, environment);
        session.addEventListener(new UpdateCouponWorkflowStatusProcessEventListener());
        TaskService taskService = LocalHumanTaskService.getService(environment);
        GenericHTWorkItemHandler humanTaskHandler = new GenericHTWorkItemHandler(session);
    
        localTaskService = new LocalTaskService(taskService);
        humanTaskHandler.setClient(localTaskService);
        humanTaskHandler.setLocal(true);
        humanTaskHandler.setIpAddress("127.0.0.1");
        humanTaskHandler.setPort(9999);
        humanTaskHandler.connect();
        WorkItemManager workItemManager = session.getWorkItemManager();
        workItemManager.registerWorkItemHandler("Human Task", humanTaskHandler);
    }
    

    修正前は、humanTaskHandler.connect();がありませんでした。それがないとどうなるか。以下のようになりました。

    タスク完了処理の入り口は、次の通りです。

    localTaskService.complete(taskSummary.getId(), actorId, ContentMarshallerHelper.marshal(results, localTaskService.getEnvironment()));
    

    localTaskService.completeを掘り下げていくと、org.jbpm.eventmessaging.EventKeys.getTargetsに到達します。
    サーバ再起動後は、このEventKeysクラスのkeysフィールドが初期化されていませんでした。結果的にGenericHTWorkItemHandlerのインナークラスとして宣言されたTaskCompletedHandler.handleCompletedTaskが実行されず、次のタスクが生成されないことがわかりました。

    なぜサーバ再起動時だけ、この不具合が発生したのでしょうか。
    EventKeys.keysは、EventKeys.registerで初期化されています。プロセス開始処理にEventKeys.registerが実行されていました。
    タスク完了処理では、TaskCompletedHandler.handleCompletedTaskから(WorkItemManagerなどを経由して)GenericHTWorkItemHandler.connectが実行され、同メソッドからEventKeys.registerが実行されていました。しかし、この順序は矛盾しています。EventKeys.keysが初期化されていないと、TaskCompletedHandler.handleCompletedTaskが実行されないのです。

    試しに、サーバ再起動後に新しいプロセスを1個以上開始させ、サーバ再起動前に開始させたプロセスのタスクを完了させました。当該タスクは無事に完了し、次のタスクが生成されました。
    まとめると、こうなります。

    サーバ初回起動時は、

  • テスト前に必ず1個以上のプロセスを開始する必要がある(プロセスの開始自体もテストだが)
  • 任意タスク完了操作前に、必ずEventKeys.keysが初期化されている
  • サーバ再起動後は、

  • テスト前にプロセスを開始する必要がない(再起動前に開始させたプロセスを使えばいい)
  • 任意タスク完了操作前に、EventKeys.keysが初期化されているとは限らない
  • 従って、GenericHTWorkItemHandler初期化時に、GenericHTWorkItemHandler.connectを実行し、EventKeys.keysを初期化するように修正しました。何か間違えているような気がしてなりません。

    コメントを残す

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