Tech Sketch Bucket of Technical Chips by TIS Inc.

ワークフローエンジン「Activiti」 チュートリアル

Pocket

前回、 『オープンソースのワークフローエンジン「Activiti」入門』 にて、Activitiの機能概要やActiviti Explorerの基本機能を中心に説明しました。
今回は、『Activiti Engineの"Java API"』を使って、簡単なワークフローアプリケーションを開発したいと思います。
チュートリアル形式で、実際に手を動かしながら進めていきましょう。


1.開発対象のアプリケーションについて

本チュートリアルでは、交通費申請アプリケーションを開発します。

交通費申請のワークフローは、以下のとおりです。

  1. 申請者が「利用した鉄道区間」「運賃」「利用日」などの情報をもとに申請を行ないます。
  2. 庶務担当者が「鉄道区間」「運賃」をチェックし、承認/却下を行ないます。
  3. マネージャーが、最終的に承認/却下の判断を行ないます。

process_flow.png

交通費申請アプリケーションの画面群

アプリケーション完成時の画面イメージは、以下のとおりです。
all_app.png

【補足】
今回のアプリケーションは、Webアプリケーション・フレームワークとして「SAStruts」と「S2JDBC」を利用しています。
SAStrutsおよびS2JDBCの詳細については、以下のサイトを参照ください。
SAStruts
S2JDBC

2.開発環境のセットアップ

『オープンソースのワークフローエンジン「Activiti」入門』 でインストールしたIDEに以下のEclipseプラグインを追加してください。

  • Tomcat Launcher
    tomcatPluginV33.zipをダウンロード後に解凍し、com.sysdeo.eclipse.tomcat_3.3.0フォルダをeclipseのpluginsに置いてください。
    ダウンロードサイト: http://www.eclipsetotale.com/tomcatPlugin.html

チュートリアル用のEclipseプロジェクトをお使いの開発環境へインポートしてください。
下記URLからzipファイルをダウンロードしてください。

3.アプリケーションの開発

1)まずは、交通費申請アプリケーション(未完成版)を動かしてみよう

インポートした交通費申請アプリケーションはワークフロー関連の処理が実装されていない状態です。
まずは、この状態でアプリケーションを動かしてみましょう。

  • データベースの起動
    このアプリケーションに必要なデータベースを起動します。
    H2データベースに必要なテーブル群を作成済みですので、これを使いましょう。

Eclipse内の「transportation」を右クリックし、[H2]-[H2 サーバーの開始]を選択します。
h2_start.png

コンソールへ以下のような表示がされたらデータベースの起動は成功です。
h2_start_ok.png

H2のデータファイルは、「src/main/resources/data」配下にあります。

では、今回のアプリケーションで必要なテーブル群を確認しましょう。
Eclipse内の「transportation」を右クリックし、[H2]-[データベース・マネージャーの表示]を選択します。
h2_console_start.png

h2_console_connect.png

ドライバクラス org.h2.Driver
JDBC URL jdbc:h2:tcp://localhost:9092/demo;
ユーザ名 sa
パスワード (未入力)

ACTIVITI_TUTORIALスキーマには、以下のテーブル群があります。
h2_console_tables.png

  • Tomcatの起動
    Tomcatランチャーを利用して起動してください。
    tomcat_start.png
  • ブラウザから下記URLへアクセス
    下記URLへアクセスし、交通費申請検索画面や交通費申請入力画面を実際に動かしてみてください。
    http://localhost:8080/tutorial/

以下のユーザーIDでログインしてください。(パスワードは未入力で構いません。)

申請者のユーザーID kagawa
庶務担当者のユーザーID messi
マネージャーのユーザーID xavi

【補足】
この時点では、以下の部分が未完成です。
以降の章で実装を行ない、交通費申請アプリケーションを完成させましょう。
□ 交通費申請入力画面の[申請]ボタン押下処理
□ 交通費申請照会画面の[承認]/[却下]ボタン押下処理
□ 交通費申請照会画面の[承認]/[却下]ボタン非表示処理

【補足】
SAStruts/S2JDBCを利用した場合のアプリケーションのクラス構成は、以下のようになります。
SAStruts_S2JDBC.png
transportationプロジェクト内のパッケージ構成は以下のとおりです。
package.png

2)プロセス定義ファイルの作成

「1.開発対象のアプリケーションについて」の章で説明したワークフローを「Activiti Designer(プロセスモデリングツール)」を使って描いてみましょう。

【補足】
Activiti Designerの基本的な使い方については、 『オープンソースのワークフローエンジン「Activiti」入門』 を参照してください。

src/main/resources/bpmn配下にプロセス定義ファイルを作成しますので、bpmnパッケージを右クリックし、下図のように[新規]-[その他]を選択してください。
bpmn_create_1.png

bpmn_create_2.png

bpmn_create_3.png

bpmn_create_4.png

以下のようにプロセスフローを完成させてください。
bpmn_create_6.png

  • プロパティ設定の補足
Idプロパティ プロセスフローを一意に特定するためのIDを設定します。
どのプロセスフローをもとにプロセスを開始するのかを指定する際に使います。

bpmn_create_5.png

  • プロパティ設定の補足
Assigneeプロパティ タスクを実行できるユーザーを設定します。
${generalAffairsId}という記述は、「プロセス開始時にgeneralAffairsIdという変数で動的にタスクを実行できるユーザーを割り当てること」を意味します。
条件プロパティ シーケンスフローの通過条件を設定します。
${flowCtrl=='APPROVE'}という記述は、「タスク完了時にflowCtrlという変数の値が"APPROVE"の場合、このシーケンスフローに流れること」を意味します。

3)プロセス定義ファイルのデプロイ

「Activiti Designer」で作成した「プロセス定義ファイル」をアプリケーションの実行時に利用するためには、予めActiviti側のテーブルへ登録しておく必要があります。

transportationプロジェクトに含まれるデプロイツール(DeploymentProcDef)を実行してください。
DeploymentProcDef.javaを右クリックし、[実行]-[Java アプリケーション]を選択します。
deploy_execute.png

デプロイが成功すると、コンソールへ以下のようなメッセージが表示されます。
deploy_ok.png

デプロイは、何度実行しても構いません。

4)交通費申請入力画面: [申請]ボタン押下処理の実装

入力された交通費申請データをDBに登録する処理は、予め実装されていますが、
ActivitiのAPIを使ってプロセスを開始する処理は、実装されていません。

tutorial.action.paperwork.TransportationInputAction#doApplyメソッド内を以下のようにコーディングしてください。

コードの解説

[STEP1]
ワークフロー上の「庶務確認」「マネージャー承認」タスクの実行者に割り当てるユーザーIDをMapに格納しています。
プロセス定義ファイルを作成した際、タスクのAssineeプロパティに「${generalAffairsId}」と「${managerId}」を設定しましが、このAssineeへ割り当てるユーザーIDをこのMapで設定しています。

[STEP2]
runtimeService(ActivitiのAPI)を使用して、プロセスを開始する処理を実装しています。
startProcessInstanceByKeyメソッドの第1引数は、「プロセス定義ファイルのID」です。
第2引数は、「アプリケーション側のデータを一意に特定するためのキー(交通費申請ID)」を渡しています。
第3引数は、[STEP1]で設定したMapを渡しています。
※Activiti APIの仕様は、以下のサイトを参照ください。
Activiti - Engine 5.13 API

【補足】
「アプリケーション側の申請データ」と「ワークフローエンジン側のデータ」を紐付けるためには、どちらかが相手のキーを保持する必要があります。
どちらがキーを保持するかは、利用するワークフローエンジンによって異なります。
Activitiは、ACT_HI_PROCINSTテーブルに"BUSINESS_KEY_"というカラムがあり、ここでアプリケーション側のキーを保持しています。

5)交通費申請照会画面: [承認]ボタン押下処理の実装

tutorial.action.paperwork.TransportationInquiryAction#doApproveメソッド内を以下のようにコーディングしてください。

コードの解説

[STEP1]
taskService(ActivitiのAPI)を使用して、ログインしているユーザーが実行可能かつアクティブなタスクを取得しています。
TaskQueryというタスクに関する情報を様々な条件で取得することができるAPIを使用しています。
「流れるようなインターフェース」と呼ばれる"文章を記述していくようにメソッドを呼び出していく手法"でコーディングします。
.taskAssignee(userLoginDto.userId)=「ログインユーザーが実行可能なタスク」という条件
.processInstanceBusinessKey(transportationInquiryForm.transportationId)=「この申請データと紐づくタスク」という条件
.active()=「現在アクティブなタスク」という条件
.singleResult()=「条件に一致するタスクを1件取得する」というメソッドの実行
「if (task == null) { ・・・」の部分は、別画面で承認された場合にアクティブなタスクを取得できない可能性があることを考慮しています。

[STEP2]
「承認」「却下」によってシーケンスフローが分岐しますので、どちらのシーケンスフローに流れるべきかの情報をflowCtrlという変数名で設定しています。
プロセス定義ファイルの条件プロパティ(シーケンスフロー)に「${flowCtrl='APPROVE'}」と「${flowCtrl='REJECT'}」を設定しましたが、この条件をMapで設定しています。
taskService#completeは、タスクを完了させるメソッドです。
第2引数に、上で設定したMapを渡しています。

[STEP3]
交通費申請テーブルにある「承認状況」のカラムを適切に更新するために、historyService(ActivitiのAPI)を使用して、この交通費申請プロセスの完了状態をチェックしています。
庶務確認タスクで承認されても、マネージャー承認待ちの時は、「申請中」にしたいです。
ここでは、HistoricProcessInstanceQueryのAPIを使って、Activiti側で管理されているプロセスの情報を取得し、完了状態をチェックしています。
.processInstanceId(task.getProcessInstanceId())=「今承認したタスクのプロセスID」を指定
.finished()=「完了したプロセス」という条件
.singleResult()=「条件に一致するタスクを1件取得する」というメソッドの実行

6)交通費申請照会画面: [却下]ボタン押下処理の実装

tutorial.action.paperwork.TransportationInquiryAction#doRejectメソッド内を以下のようにコーディングしてください。

コードの解説

[STEP1]
taskService(ActivitiのAPI)を使用して、ログインしているユーザーが実行可能かつアクティブなタスクを取得しています。
.taskAssignee(userLoginDto.userId)=「ログインユーザーが実行可能なタスク」という条件
.processInstanceBusinessKey(transportationInquiryForm.transportationId)=「この申請データと紐づくタスク」という条件
.active()=「現在アクティブなタスク」という条件
.singleResult()=「条件に一致するタスクを1件取得する」というメソッドの実行

[STEP2]
ここでは、「却下」のシーケンスフローに流れたいため、flowCtrl='REJECT'をMapに設定しています。

7)交通費申請照会画面: [承認][却下]ボタン非表示処理の実装

交通費申請データの承認/却下を実行できるユーザーは、申請データごと異なります。
承認/却下を実行できないユーザーが本画面を表示した時は、[承認][却下]ボタンを非表示にする処理を実装してみましょう。

tutorial.action.paperwork.TransportationInquiryAction#indexメソッド内を以下のようにコーディングしてください。

コードの解説

画面表示が行われる前に、ログインユーザーが[承認][却下]を実行可能かをチェックし、ボタンの表示/非表示を制御しています。
transportationInquiryForm.approvableのプロパティは、JSP側で参照しています。

  • /transportation/src/main/webapp/WEB-INF/view/paperwork/transportationInquiry/transportationInquiry.jsp

8)最後に動作確認をしましょう

以下のテストケースをもとに動作確認をしましょう。

  • kagawaで申請 → messiで承認 → xaviで承認
    • □承認状況は、「申請中」→「承認済み」と遷移する
    • □[承認][却下]ボタンの表示制御が正しい
  • kagawaで申請 → messiで承認 → xaviで却下
    • □承認状況は、「申請中」→「却下」と遷移
    • □[承認][却下]ボタンの表示制御が正しい
  • kagawaで申請 → messiで却下
    • □承認状況は、「申請中」→「却下」と遷移
    • □[承認][却下]ボタンの表示制御が正しい

4.おわりに

今回は、「交通費申請」という単純で分かりやすい業務を対象にワークフローアプリケーションを開発してみました。
Activitiで用意されているAPIを利用すると、「プロセス定義ファイル」どおりにフローの制御が簡単に行えることをお分かり頂けたと思います。

但し、今回開発したアプリケーションは、まだまだ実運用を考えると解決しなければならない課題が残っています。
例を挙げると、以下のようなものです。

  1. 業務データとActiviti側で管理されているデータを跨いだトランザクション管理の実施
  2. 自分に承認依頼が届いている申請データのみを表示させるなどの画面の実装
  3. 「差戻し」「取下げ」など、ワークフローアプリにおいて一般的に登場しそうな機能の実装

上記課題に対する具体的な解決策は、また別の機会に掲載したいと考えていますので、ご期待ください。

エンジニア採用中!私たちと一緒に働いてみませんか?