66.6. プロセスエンジンのイベントリスナー

プロセスやタスクがライフサイクルの異なるポイントに移行するたびに、プロセスエンジンはイベントを生成します。このようなイベントを受信して処理するクラスを開発することができます。このクラスを イベントリスナー と呼びます。

プロセスエンジンは、イベントオブジェクトをこのクラスに渡します。オブジェクトは、関連情報へのアクセスを提供します。例えば、イベントがプロセスノードに関連する場合、オブジェクトはプロセスインスタンスとノードインスタンスへのアクセスを提供します。

66.6.1. イベントリスナーのインターフェイス

プロセスエンジンのイベントリスナーを開発するために、以下のインターフェイスを使用することができます。

66.6.1.1. プロセスイベントリスナー用インターフェイス

ProcessEventListener インターフェイスを実装するクラスを開発できます。このクラスは、プロセスの開始や完了、またはノードへの入出力などのプロセス関連のイベントをリッスンできます。

以下のソースコードは、ProcessEventListener インターフェイスのさまざまなメソッドを示しています。

ProcessEventListener インターフェイス

public interface ProcessEventListener
    extends
    EventListener {

    /**
     * This listener method is invoked right before a process instance is being started.
     * @param event
     */
    void beforeProcessStarted(ProcessStartedEvent event);

    /**
     * This listener method is invoked right after a process instance has been started.
     * @param event
     */
    void afterProcessStarted(ProcessStartedEvent event);

    /**
     * This listener method is invoked right before a process instance is being completed (or aborted).
     * @param event
     */
    void beforeProcessCompleted(ProcessCompletedEvent event);

    /**
     * This listener method is invoked right after a process instance has been completed (or aborted).
     * @param event
     */
    void afterProcessCompleted(ProcessCompletedEvent event);

    /**
     * This listener method is invoked right before a node in a process instance is being triggered
     * (which is when the node is being entered, for example when an incoming connection triggers it).
     * @param event
     */
    void beforeNodeTriggered(ProcessNodeTriggeredEvent event);

    /**
     * This listener method is invoked right after a node in a process instance has been triggered
     * (which is when the node was entered, for example when an incoming connection triggered it).
     * @param event
     */
    void afterNodeTriggered(ProcessNodeTriggeredEvent event);

    /**
     * This listener method is invoked right before a node in a process instance is being left
     * (which is when the node is completed, for example when it has performed the task it was
     * designed for).
     * @param event
     */
    void beforeNodeLeft(ProcessNodeLeftEvent event);

    /**
     * This listener method is invoked right after a node in a process instance has been left
     * (which is when the node was completed, for example when it performed the task it was
     * designed for).
     * @param event
     */
    void afterNodeLeft(ProcessNodeLeftEvent event);

    /**
     * This listener method is invoked right before the value of a process variable is being changed.
     * @param event
     */
    void beforeVariableChanged(ProcessVariableChangedEvent event);

    /**
     * This listener method is invoked right after the value of a process variable has been changed.
     * @param event
     */
    void afterVariableChanged(ProcessVariableChangedEvent event);

    /**
     * This listener method is invoked right before a process/node instance's SLA has been violated.
     * @param event
     */
    default void beforeSLAViolated(SLAViolatedEvent event) {}

    /**
     * This listener method is invoked right after a process/node instance's SLA has been violated.
     * @param event
     */
    default void afterSLAViolated(SLAViolatedEvent event) {}

    /**
     * This listener method is invoked when a signal is sent
     * @param event
     */
    default void onSignal(SignalEvent event) {}

    /**
     * This listener method is invoked when a message is sent
     * @param event
     */
    default void onMessage(MessageEvent event) {}
}

これらのメソッドのいずれかを実装することで、対応するイベントを処理することができます。

プロセスエンジンがメソッドに渡すイベントクラスの定義については、Java ドキュメントorg.kie.api.event.process パッケージを参照してください。

イベントクラスのメソッドを使用して、イベントに関与するエンティティーに関するすべての情報が含まれる他のクラスを取得することができます。

次の例は、afterNodeLeft() などのノード関連イベントの一部で、プロセスインスタンスおよびノードタイプを取得します。

ノード関連イベントのプロセスインスタンスおよびノードタイプの取得

WorkflowProcessInstance processInstance = event.getNodeInstance().getProcessInstance()
NodeType nodeType = event.getNodeInstance().getNode().getNodeType()

66.6.1.2. タスクライフサイクルイベントリスナー用インターフェイス

TaskLifecycleEventListener インターフェイスを実装するクラスを開発できます。このクラスは、タスクのライフサイクルに関連するイベント (オーナーの割り当てやタスクの完了など) をリッスンすることができます。

以下のソースコードは、TaskLifecycleEventListener インターフェイスのさまざまなメソッドを示しています。

TaskLifecycleEventListener インターフェイス

public interface TaskLifeCycleEventListener extends EventListener {

    public enum AssignmentType {
        POT_OWNER,
        EXCL_OWNER,
        ADMIN;
    }

    public void beforeTaskActivatedEvent(TaskEvent event);
    public void beforeTaskClaimedEvent(TaskEvent event);
    public void beforeTaskSkippedEvent(TaskEvent event);
    public void beforeTaskStartedEvent(TaskEvent event);
    public void beforeTaskStoppedEvent(TaskEvent event);
    public void beforeTaskCompletedEvent(TaskEvent event);
    public void beforeTaskFailedEvent(TaskEvent event);
    public void beforeTaskAddedEvent(TaskEvent event);
    public void beforeTaskExitedEvent(TaskEvent event);
    public void beforeTaskReleasedEvent(TaskEvent event);
    public void beforeTaskResumedEvent(TaskEvent event);
    public void beforeTaskSuspendedEvent(TaskEvent event);
    public void beforeTaskForwardedEvent(TaskEvent event);
    public void beforeTaskDelegatedEvent(TaskEvent event);
    public void beforeTaskNominatedEvent(TaskEvent event);
    public default void beforeTaskUpdatedEvent(TaskEvent event){};
    public default void beforeTaskReassignedEvent(TaskEvent event){};
    public default void beforeTaskNotificationEvent(TaskEvent event){};
    public default void beforeTaskInputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
    public default void beforeTaskOutputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
    public default void beforeTaskAssignmentsAddedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};
    public default void beforeTaskAssignmentsRemovedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};

    public void afterTaskActivatedEvent(TaskEvent event);
    public void afterTaskClaimedEvent(TaskEvent event);
    public void afterTaskSkippedEvent(TaskEvent event);
    public void afterTaskStartedEvent(TaskEvent event);
    public void afterTaskStoppedEvent(TaskEvent event);
    public void afterTaskCompletedEvent(TaskEvent event);
    public void afterTaskFailedEvent(TaskEvent event);
    public void afterTaskAddedEvent(TaskEvent event);
    public void afterTaskExitedEvent(TaskEvent event);
    public void afterTaskReleasedEvent(TaskEvent event);
    public void afterTaskResumedEvent(TaskEvent event);
    public void afterTaskSuspendedEvent(TaskEvent event);
    public void afterTaskForwardedEvent(TaskEvent event);
    public void afterTaskDelegatedEvent(TaskEvent event);
    public void afterTaskNominatedEvent(TaskEvent event);
    public default void afterTaskReassignedEvent(TaskEvent event){};
    public default void afterTaskUpdatedEvent(TaskEvent event){};
    public default void afterTaskNotificationEvent(TaskEvent event){};
    public default void afterTaskInputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
    public default void afterTaskOutputVariableChangedEvent(TaskEvent event, Map<String, Object> variables){};
    public default void afterTaskAssignmentsAddedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};
    public default void afterTaskAssignmentsRemovedEvent(TaskEvent event, AssignmentType type, List<OrganizationalEntity> entities){};

}

これらのメソッドのいずれかを実装することで、対応するイベントを処理することができます。

プロセスエンジンがメソッドに渡すイベントクラスの定義については、Java ドキュメントorg.kie.api.task パッケージを参照してください。

イベントクラスのメソッドを用いて、タスク、タスクコンテキスト、タスクメタデータを表すクラスを取得することができます。

66.6.2. イベントリスナーへの呼び出しのタイミング

イベントの before および after に多数のイベントリスナーの呼び出しがあります (例: beforeNodeLeft() および afterNodeLeft()beforeTaskActivatedEvent() および afterTaskActivatedEvent())。

通常、before イベントおよび after イベントの呼び出しは、スタックのように動作します。イベント A が直接イベント B を発生する場合は、以下の呼び出しシーケンスが実行されます。

  • Before A
  • Before B
  • After B
  • After A

たとえば、ノード X を残すと、ノード Y のトリガーに関連するすべてのイベント呼び出しが、ノード X の beforeNodeLeft 呼び出しと afterNodeLeft 呼び出しの間で実行されます。

同様に、プロセスを開始すると一部のノードが直接開始する場合に、すべての nodeTriggered イベントおよび nodeLeft イベント呼び出しは、beforeProcessStarted 呼び出しと afterProcessStarted 呼び出しの間に発生します。

このアプローチは、イベント間の原因と効果の関係を反映しています。ただし、イベントコール のタイミングと順序は常に直感的には限りません。たとえば、afterProcessStarted 呼び出しは、プロセス内の一部のノードを afterNodeLeft 呼び出しの後に発生する可能性があります。

通常、特定のイベントの発生時に通知するには、before 呼び出しをイベントに使用します。たとえば、特定のプロセスインスタンスの開始に関連するすべての手順が完了したときに、このイベントに関連するすべての処理が終了する場合のみ、after の呼び出しを使用してください。

ノードのタイプによって、一部のノードは nodeLeft 呼び出しのみを生成する可能性があり、nodeTriggered 呼び出しのみを生成する可能性があります。たとえば、catch 中間イベントノードは、別のプロセスノードによってトリガーされないため、nodeTriggered 呼び出しを生成しません。同様に、throw 中間イベントノードには別のノードへの外向き接続がないため、これらのノードは nodeLeft 呼び出しを生成しません。

66.6.3. イベントリスナー開発のためのプラクティス

プロセスエンジンは、イベントやタスクの処理中にイベントリスナーを呼び出します。呼び出しは、プロセスエンジンのトランザクションとブロック実行の中で行われます。したがって、イベントリスナーはプロセスエンジンのロジックや性能に影響を与える可能性があります。

中断を最小限に抑えるため、以下のガイドラインに従ってください。

  • どんなアクションでも、できるだけ短くしなければならない。
  • リスナークラスは状態を持ってはいけない。プロセスエンジンは、いつでもリスナークラスを破棄し、再作成することができます。
  • リスナーがリスナーメソッドのスコープ外に存在するリソースを変更する場合、そのリソースが現在のトランザクションに登録されていることを確認する。トランザクションがロールバックされる可能性があります。この場合、変更されたリソースがトランザクションの一部でない場合、リソースの状態の一貫性が失われます。

    Red Hat JBoss EAP が提供するデータベース関連のリソースは、常に現在のトランザクションに登録されます。それ以外の場合は、使用している実行環境の JTA 情報を確認してください。

  • 異なるイベントリスナーの実行順序に依存するロジックは使用しない。
  • リスナー内にプロセスエンジン外の異なるエンティティーとのインタラクションを含めない。例えば、イベント通知のための REST 呼び出しを含めないでください。代わりに、プロセスノードを使用してそのような呼び出しを完了します。例外はロギング情報の出力です。しかし、ロギングリスナーは可能な限りシンプルにする必要があります。
  • リスナーを使用して、イベントに関与するプロセスやタスクの状態を変更することができる (例: その変数を変更する)。
  • リスナーを使用して、プロセスエンジンと対話することができる (例: イベントに関与していないプロセスインスタンスにシグナルを送信する、プロセスインスタンスと対話する)。

66.6.4. イベントリスナーの登録

KieSession クラスは、以下のリストのようにイベントリスナーの登録、削除、および一覧表示を行うメソッドを提供する RuleRuntimeEventManager インターフェイスを実装します。

RuleRuntimeEventManager インターフェイスのメソッド

    void addEventListener(AgendaEventListener listener);
    void addEventListener(RuleRuntimeEventListener listener);
    void removeEventListener(AgendaEventListener listener);
    void removeEventListener(RuleRuntimeEventListener listener);
    Collection<AgendaEventListener>	getAgendaEventListeners();
    Collection<RuleRuntimeEventListener> getRuleRintimeEventListeners();

ただし、典型的な場合には、これらのメソッドは使用しないでください。

RuntimeManager インターフェイスを使用している場合は、RuntimeEnvironment クラスを使用してイベントリスナーを登録します。

サービス API を使用している場合は、プロジェクトの META-INF/services/org.jbpm.services.task.deadlines.NotificationListener ファイルに、イベントリスナーの完全修飾クラス名を追加できます。また、Services API は、イベントの電子メール通知を送信できる org.jbpm.services.task.deadlines.notifications.impl.email.EmailNotificationListener などのデフォルトのリスナーも登録します。

デフォルトのリスナーを除外するには、リスナーの完全修飾名を JVM システムプロパティー org.kie.jbpm.notification_listeners.exclude に追加します。

66.6.5. KieRuntimeLogger イベントリスナー

KieServices パッケージには、KIE セッションに追加できる KieRuntimeLogger イベントリスナーが含まれます。このリスナーを使用して監査ログを作成できます。このログには、起動時に発生した異なるイベントがすべて含まれます。

注記

これらのロガーはデバッグの目的で使用されます。ビジネスレベルのプロセス分析では詳細すぎる可能性があります。

リスナーは以下のロガータイプを実装します。

  • コンソールロガー: このロガーはすべてのイベントをコンソールに書き込みます。このロガーの完全修飾クラス名は org.drools.core.audit.WorkingMemoryConsoleLogger です。
  • ファイルロガー: このロガーは XML 表現を使用してすべてのイベントをファイルに書き込みます。IDE でログファイルを使用して、実行時に発生したイベントのツリーベースの視覚化を生成できます。このロガーの完全修飾クラス名は org.drools.core.audit.WorkingMemoryFileLogger です。

    ファイルロガーは、ロガーを閉じるときや、ロガーのイベント数が事前定義レベルに達した場合にのみ、イベントをディスクに書き込みます。したがって、ランタイム時のプロセスのデバッグには適していません。

  • スレッドファイルロガー: このロガーは、指定した時間間隔の後にイベントをファイルに書き込みます。このロガーを使用して、プロセスのデバッグ中に進捗をリアルタイムで視覚化することができます。このロガーの完全修飾クラス名は org.drools.core.audit.ThreadedWorkingMemoryFileLogger です。

ロガーの作成時に、KIE セッションを引数として渡す必要があります。ファイルロガーでは、ログファイルの名前を作成する必要もあります。スレッド化されたファイルロガーには、イベントが保存される間隔 (ミリ秒単位) が必要です。

アプリケーションの末尾で常にロガーを閉じます。

以下の例は、ファイルロガーの使用例を示しています。

ファイルロガーの使用

  import org.kie.api.KieServices;
  import org.kie.api.logger.KieRuntimeLogger;
  ...
  KieRuntimeLogger logger = KieServices.Factory.get().getLoggers().newFileLogger(ksession, "test");
  // add invocations to the process engine here,
  // e.g. ksession.startProcess(processId);
  ...
  logger.close();

ファイルベースのロガーによって作成されるログファイルには、プロセスのランタイム中に発生したすべてのイベントの XML ベースの概要が含まれます。