68.2. CDI との統合
プロセスエンジンは、CDI との統合を自動的にサポートします。CDI フレームワークでは、ほとんどの API を変更せずに使用できます。
プロセスエンジンは、CDI コンテナー用に特別に設計された専用のモジュールも提供します。最も重要なモジュールは、プロセスエンジンサービスの CDI ラッパーを提供する jbpm-services-cdi です。これらのラッパーを使用して、CDI アプリケーションでプロセスエンジンを統合することができます。モジュールは、以下のサービスセットを提供します。
-
DeploymentService -
ProcessService -
UserTaskService -
RuntimeDataService -
DefinitionService
これらのサービスは、他の CDI Bean の挿入に使用できます。
68.2.1. CDI のデプロイメントサービス
DeploymentService サービスは、ランタイム環境でデプロイメントユニットをデプロイし、デプロイ解除します。このサービスを使用してユニットをデプロイすると、デプロイメントユニットの実行が準備され、そのユニットに RuntimeManager インスタンスが作成されます。DeploymentService を使用して以下のオブジェクトを取得することもできます。
-
特定のデプロイメント ID の
RuntimeManagerインスタンス -
指定のデプロイメント ID の完全なデプロイメントユニットを表す
DeployedUnitインスタンス - デプロイメントサービスで認識されている全ユニットの一覧
デフォルトでは、デプロイメントサービスは、デプロイされたユニットに関する情報を永続ストレージに保存しません。CDI フレームワークでは、サービスを使用するコンポーネントは、データベース、ファイル、システム、リポジトリーなどを使用して、デプロイメントユニット情報を保存し、復元できます。
デプロイメントサービスは、デプロイメントおよびデプロイメント解除で CDI イベントを実行します。サービスを使用するコンポーネントは、これらのイベントを処理してデプロイメントを保存し、デプロイの解除時にそれらをストアから削除できます。
-
@Deploy修飾子を含むDeploymentEventは、ユニットのデプロイメントで実行されます。 -
@Undeploy修飾子を持つDeploymentEventは、ユニットのデプロイ解除で実行されます。
CDI オブザーバーメカニズムを使用して、これらのイベントの通知を取得できます。
以下の例は、ユニットのデプロイメントで通知を受け取り、デプロイメントを保存できます。
デプロイメントイベントの処理例
public void saveDeployment(@Observes @Deploy DeploymentEvent event) {
// Store deployed unit information
DeployedUnit deployedUnit = event.getDeployedUnit();
}
以下の例は、ユニットのデプロイメント時に通知を受け取り、デプロイメントをストレージから削除できます。
アンデプロイメントイベントの処理例
public void removeDeployment(@Observes @Undeploy DeploymentEvent event) {
// Remove deployment with the ID event.getDeploymentId()
}
DeploymentService サービスの複数の実装が可能であるため、修飾子を使用して CDI コンテナーに特定の実装を挿入するように指示する必要があります。DeploymentUnit の一致する実装は、DeploymentService の各実装に対して存在している必要があります。
プロセスエンジンは、KmoduleDeploymentService 実装を提供します。この実装は、KJAR ファイルに含まれる小規模記述子である KmoduleDeploymentUnits と連携するように設計されています。この実装は、ほとんどのユースケースの一般的なソリューションです。この実装の修飾子は @Kjar です。
68.2.2. CDI のフォームプロバイダーサービス
FormProviderService サービスは、フォーム表現へのアクセスを提供します。これは通常、プロセスフォームとユーザータスクフォームの両方のユーザーインターフェイスに表示されます。
サービスは、異なる機能を提供し、異なるテクノロジーでサポートされる、分離されたフォームプロバイダーの概念に依存します。FormProvider インターフェイスは、フォームプロバイダーの実装のコントラクトを記述します。
FormProvider インターフェイスの定義
public interface FormProvider {
int getPriority();
String render(String name, ProcessDesc process, Map<String, Object> renderContext);
String render(String name, Task task, ProcessDesc process, Map<String, Object> renderContext);
}
FormProvider インターフェイスの実装は、優先度の値を定義する必要があります。FormProviderService サービスがフォームをレンダリングする必要がある場合は、利用可能なプロバイダーを優先度順に呼び出します。
優先度の値が低いほど、プロバイダーが取得する優先度が高くなります。たとえば、優先度が 5 のプロバイダーは、優先度が 10 のプロバイダーの前に評価されます。必要な形式ごとに、サービスはいずれかのコンテンツを提供するまで、優先度順に利用可能なプロバイダーを繰り返し処理します。小文字のシナリオでは、単純なテキストベースのフォームが返されます。
プロセスエンジンは、FormProvider の以下の実装を提供します。
- Form Modeller ツールで作成されたフォームを提供するプロバイダー。優先度は 2 です。
- プロセスフォームおよびタスクフォームをサポートする FreeMarker ベースの実装 (優先度は 3)
- 単純なテキストベースのフォームを返すデフォルトのフォームプロバイダー。他のプロバイダーがコンテンツを配信しない場合の最後の手段として使用され、優先度は 1000 です。
68.2.3. CDI のランタイムデータサービス
RuntimeDataService サービスは、以下のデータを含め、ランタイム時に利用可能なデータへのアクセスを提供します。
- さまざまなフィルターで実行される利用可能なプロセス
- アクティブなプロセスインスタンス (さまざまなフィルターを含む)
- プロセスインスタンスの履歴
- プロセスインスタンス変数
- プロセスインスタンスのアクティブノードおよび完了ノード
RuntimeDataService のデフォルト実装はデプロイメントイベントを監視し、デプロイされたすべてのプロセスをインデックス化して呼び出しコンポーネントに公開します。
68.2.4. CDI の定義サービス
DefinitionService サービスは、BPMN2 XML 定義の一部として保存されるプロセスの詳細へのアクセスを提供します。
情報を提供するメソッドを使用する前に、buildProcessDefinition() メソッドを呼び出して、リポジトリーに BPMN2 コンテンツから取得したプロセス情報を設定します。
BPMN2DataService 実装は、以下のデータへのアクセスを提供します。
- 指定のプロセス定義のプロセス全体の説明
- プロセス定義にある全ユーザータスクのコレクション
- ユーザータスクノードに定義された入力に関する情報
- ユーザータスクノードに定義された出力に関する情報
- 特定のプロセス定義内で定義される再利用可能なプロセス (コールアクティビティー) の ID
- 指定のプロセス定義内で定義したプロセス変数に関する情報
プロセス定義に含まれるすべての組織エンティティー (ユーザーおよびグループ) に関する情報。特定のプロセス定義に応じて、ユーザーおよびグループに返される値には、以下の情報が含まれます。
- 実際のユーザーまたはグループの名前
-
ランタイム時に実際のユーザーもしくはグループの名前を取得するのに使用するプロセス変数 (例:
#{manager})
68.2.5. CDI 統合の設定
CDI フレームワークで jbpm-services-cdi モジュールを使用するには、追加するサービス実装の依存関係に対応するために Bean を提供する必要があります。
使用方法によっては、複数の Bean が必要になることがあります。
- エンティティーマネージャーおよびエンティティーマネージャーファクトリー
- ヒューマンタスクのユーザーグループコールバック
- 認証されたユーザー情報をサービスに渡すアイデンティティープロバイダー
Red Hat JBoss EAP などの JEE 環境で実行する場合、以下のプロデューサー Bean は jbpm-services-cdi モジュールの全要件を満たす必要があります。
JEE 環境内の jbpm-services-cdi モジュールの全要件を満たすプロデューサー Bean
public class EnvironmentProducer {
@PersistenceUnit(unitName = "org.jbpm.domain")
private EntityManagerFactory emf;
@Inject
@Selectable
private UserGroupInfoProducer userGroupInfoProducer;
@Inject
@Kjar
private DeploymentService deploymentService;
@Produces
public EntityManagerFactory getEntityManagerFactory() {
return this.emf;
}
@Produces
public org.kie.api.task.UserGroupCallback produceSelectedUserGroupCalback() {
return userGroupInfoProducer.produceCallback();
}
@Produces
public UserInfo produceUserInfo() {
return userGroupInfoProducer.produceUserInfo();
}
@Produces
@Named("Logs")
public TaskLifeCycleEventListener produceTaskAuditListener() {
return new JPATaskLifeCycleEventListener(true);
}
@Produces
public DeploymentService getDeploymentService() {
return this.deploymentService;
}
@Produces
public IdentityProvider produceIdentityProvider {
return new IdentityProvider() {
// implement IdentityProvider
};
}
}
アプリケーションの beans.xml ファイルは、ユーザーグループ情報コールバックの適切な代替を有効にする必要があります。この代替は @Selectable 修飾子に基づいて実行されます。
beans.xml ファイルのユーザーグループ情報コールバックの代替の定義
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://docs.jboss.org/cdi/beans_1_0.xsd">
<alternatives>
<class>org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer</class>
</alternatives>
</beans>
org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer はサンプル値です。この値は、サーバーが使用するセキュリティー方式 (LDAP やデータベースなど) に関係なく、アプリケーションサーバーのセキュリティー設定を再利用するため、通常は Red Hat JBoss EAP に適しています。
必要に応じて、他のプロデューサーを複数提供して、イベントリスナー WorkItemHandlers、Process、Agenda、および WorkingMemory を配信できます。以下のインターフェイスを実装して、これらのコンポーネントを提供できます。
CDI とのプロセスエンジン統合用のワークアイテムハンドラープロデューサーインターフェイス
/**
* Enables providing custom implementations to deliver WorkItem name and WorkItemHandler instance pairs
* for the runtime.
* <br/>
* This interface is invoked by the RegisterableItemsFactory implementation (in particular InjectableRegisterableItemsFactory
* in the CDI framework) for every KieSession. Always return new instances of objects to avoid unexpected
* results.
*
*/
public interface WorkItemHandlerProducer {
/**
* Returns map of work items(key = work item name, value = work item handler instance)
* to be registered on KieSession
* <br/>
* The following parameters might be given:
* <ul>
* <li>ksession</li>
* <li>taskService</li>
* <li>runtimeManager</li>
* </ul>
*
* @param identifier - identifier of the owner - usually the RuntimeManager. This parameter allows the producer to filter out
* and provide valid instances for a given owner
* @param params - the owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
* @return map of work item handler instances (always return new instances when this method is invoked)
*/
Map<String, WorkItemHandler> getWorkItemHandlers(String identifier, Map<String, Object> params);
}
CDI とのプロセスエンジン統合用のイベントリスナープロデューサーインターフェイス
/**
* Enables defining custom producers for known EventListeners. There might be several
* implementations that might provide a different listener instance based on the context in which they are executed.
* <br/>
* This interface is invoked by the RegisterableItemsFactory implementation (in particular, InjectableRegisterableItemsFactory
* in the CDI framework) for every KieSession. Always return new instances of objects to avoid unexpected results.
*
* @param <T> type of the event listener - ProcessEventListener, AgendaEventListener, WorkingMemoryEventListener
*/
public interface EventListenerProducer<T> {
/**
* Returns list of instances for given (T) type of listeners
* <br/>
* Parameters that might be given are:
* <ul>
* <li>ksession</li>
* <li>taskService</li>
* <li>runtimeManager</li>
* </ul>
* @param identifier - identifier of the owner - usually RuntimeManager. This parameter allows the producer to filter out
* and provide valid instances for given owner
* @param params - the owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
* @return list of listener instances (always return new instances when this method is invoked)
*/
List<T> getEventListeners(String identifier, Map<String, Object> params);
}
これらの 2 つのインターフェイスを実装する Bean はランタイム時に収集され、RuntimeManager クラスが KieSession インスタンスをビルドすると呼び出されます。
68.2.5.1. CDI Bean としてのランタイムマネージャー
RuntimeManager クラスを CDI Bean としてアプリケーション内の他の CDI Bean に挿入できます。RuntimeManager インスタンスを正しく初期化できるようにするには、RuntimeEnvironment クラスを適切に生成する必要があります。
以下の CDI 修飾子は既存のランタイムマネージャーストラテジーを参照します。
-
@Singleton -
@PerRequest -
@PerProcessInstance
ランタイムマネージャーの詳細は、「ランタイムマネージャー」を参照してください。
RuntimeManager クラスを直接挿入できますが、CDI、EJB、Spring などのフレームワークのほとんどのユースケースでは、サービスが使用されます。プロセスエンジンサービスは、ランタイムマネージャーを使用するための多くのベストプラクティスを実装します。
ランタイムマネージャーを使用するには、「CDI 統合の設定」セクションに定義されたプロデューサーに RuntimeEnvironment クラスを追加する必要があります。
RuntimeEnvironment クラスを提供するプロデューサー Bean
public class EnvironmentProducer {
//Add the same producers as for services
@Produces
@Singleton
@PerRequest
@PerProcessInstance
public RuntimeEnvironment produceEnvironment(EntityManagerFactory emf) {
RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.userGroupCallback(getUserGroupCallback())
.registerableItemsFactory(InjectableRegisterableItemsFactory.getFactory(beanManager, null))
.addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
.addAsset(ResourceFactory.newClassPathResource("BPMN2-UserTask.bpmn2"), ResourceType.BPMN2)
.get();
return environment;
}
}
この例では、メソッドレベルですべての修飾子を指定して、単一のプロデューサーメソッドがすべてのランタイムマネージャーストラテジーに RuntimeEnvironment クラスを提供できます。
完全なプロデューサーが利用できる状態になったら、RuntimeManager クラスはアプリケーションの CDI Bean に挿入できます。
RuntimeManager クラスの挿入
public class ProcessEngine {
@Inject
@Singleton
private RuntimeManager singletonManager;
public void startProcess() {
RuntimeEngine runtime = singletonManager.getRuntimeEngine(EmptyContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("UserTask");
singletonManager.disposeRuntimeEngine(runtime);
}
}
RuntimeManager クラスを挿入する場合は、アプリケーションに RuntimeManager のインスタンスが 1 つだけ存在する可能性があります。通常、必要に応じて RuntimeManager インスタンスを作成する DeploymentService サービスを使用します。
DeploymentService の代わりに、RuntimeManagerFactory クラスを挿入し、アプリケーションがそれを使用して RuntimeManager インスタンスを作成できます。この場合、EnvironmentProducer 定義は依然として必要になります。以下の例は、単純な ProcessEngine Bean を示しています。
ProcessEngine Bean の例
public class ProcessEngine {
@Inject
private RuntimeManagerFactory managerFactory;
@Inject
private EntityManagerFactory emf;
@Inject
private BeanManager beanManager;
public void startProcess() {
RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
.addAsset(ResourceFactory.newClassPathResource("BPMN2-UserTask.bpmn2"), ResourceType.BPMN2)
.registerableItemsFactory(InjectableRegisterableItemsFactory.getFactory(beanManager, null))
.get();
RuntimeManager manager = managerFactory.newSingletonRuntimeManager(environment);
RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("UserTask");
manager.disposeRuntimeEngine(runtime);
manager.close();
}
}