jBPM PVM の使用
jBPM PVM(プロセス仮想マシン)を Groovy から使ってみる。
jBPM PVM は、グラフ構造を構築して実行するためのシンプルな Java ライブラリで、ワークフローや BPM を実現するための共通基盤となる。
使うための準備は以下のとおり。
- jBPM PVM のサイトから zip ファイル(現時点では jbpm-pvm-1.0.alpha2.zip)をダウンロード
- zip ファイルを適当なディレクトリに解凍
- 解凍先ディレクトリ内の jbpm-pvm.jar ファイルを CLASSPATH に追加
これで基本機能は利用できるようになる。
簡単なプロセス定義の作成と実行
プロセス定義は基本的に ProcessFactory のメソッドを使って構築する。実行するための処理は Activity インターフェースの実装クラスに定義する。
ProcessFactory の主なメソッドは以下のとおり。
- node() : ノードの定義
- initial() : カレントノードを初期ノードに設定
- behaviour() : カレントノードの振る舞い(処理)を設定
- transition() : カレントノードの状態を作成
- to() : 遷移先のノード指定
- done() : ProcessDefinition オブジェクトを生成
ここで、「first -> second -> third」 という単純な 3つの処理を順次実行するだけのスクリプトを作成してみた。
サンプルスクリプト sample.groovy
import org.jbpm.pvm.Activity import org.jbpm.pvm.Execution import org.jbpm.pvm.ProcessDefinition import org.jbpm.pvm.ProcessFactory //処理クラスの定義 class SampleActivity implements Activity { def name public void execute(Execution exec) { println "$name : node name=${exec.node.name}" } } //プロセス定義 pd = ProcessFactory.build() .node("first").initial().behaviour(new SampleActivity(name: "FirstAct")) .transition().to("second") .node("second").behaviour(new SampleActivity(name: "SecondAct")) .transition().to("third") .node("third").behaviour(new SampleActivity(name: "ThirdAct")) .done() //プロセスの実行 exec = pd.startExecution() //プロセスの状況を出力 println "node name = ${exec.node.name}, ended=${exec.ended}, active=${exec.active}, finished=${exec.finished}"
上記のスクリプトを実行すると以下のような結果となる。
実行結果
>groovy sample.groovy FirstAct : node name=first SecondAct : node name=second ThirdAct : node name=third node name = third, ended=true, active=false, finished=true
待機状態を用いたプロセス定義の作成と実行
ExternalActivity インターフェースの実装クラスを使うと、待機状態と待機からの復帰を実現するプロセスを実現することができる。
具体的には、execute メソッドの中で Execution オブジェクトの waitForSignal メソッドを呼び出すと、Execution の signal メソッドが呼ばれるまで待機状態になる。Execution の signal メソッドが呼ばれると ExternalActivity の signal メソッドが呼び出され、Execution の take メソッドで遷移先の状態を指定することができる。
そんなわけで、ExternalActivity を使って単純なループ処理を実装してみた。
サンプルスクリプト loop.groovy
import org.jbpm.pvm.Activity import org.jbpm.pvm.ExternalActivity import org.jbpm.pvm.Execution import org.jbpm.pvm.ProcessDefinition import org.jbpm.pvm.ProcessFactory import org.jbpm.pvm.SignalDefinition class NullActivity implements Activity { void execute(Execution exec) { } } class LoopCounter implements ExternalActivity { def loopCount void execute(Execution exec) { exec.waitForSignal(); } void signal(Execution exec, String signal, Map<String, Object> params) { def count = (exec.hasVariable("count"))? exec.getVariable("count"): 0; count++ exec.setVariable("count", count) if (count < loopCount) { //continue に移行(1stノードに戻る) exec.take("continue") } else { //next に移行(2ndノードに移る) exec.take("next") } } Set<SignalDefinition> getSignals(Execution exec) { } } printExec = { println "${exec.node.name} : ended=${exec.ended}, active=${exec.active}, finished=${exec.finished}" } //プロセス定義 pd = ProcessFactory.build() .node("1st").initial().behaviour(new LoopCounter(loopCount: 3)) .transition("continue").to("1st") .transition("next").to("2nd") .node("2nd").behaviour(new NullActivity()) .done() exec = pd.startExecution() while(exec.active) { printExec() exec.signal() } printExec()
上記のスクリプトを実行すると以下のような結果となる。
実行結果
>groovy loop.groovy 1st : ended=false, active=true, finished=false 1st : ended=false, active=true, finished=false 1st : ended=false, active=true, finished=false 2nd : ended=true, active=false, finished=true
JRuby で書いてみると
sample.groovy と同じような処理を JRuby 1.1.2 では以下のように書ける。(ブロックを使ったインターフェースの実装方法を使ってみた)
なお、プロセス定義の部分で . の後に改行している点に注意。(こうしないと SyntaxError が発生)
sample.rb
require "java" import org.jbpm.pvm.Activity import org.jbpm.pvm.Execution import org.jbpm.pvm.ProcessDefinition import org.jbpm.pvm.ProcessFactory def createActivity(name) #Activity インターフェースの execute メソッド実装オブジェクト生成 Activity.impl(:execute) do |method, exec| puts "#{name} : node name: #{exec.node.name} - call #{method}" end end pd = ProcessFactory.build. node("first").initial.behaviour(createActivity :FirstAct). transition.to("second"). node("second").behaviour(createActivity :SecondAct). transition.to("third"). node("third").behaviour(createActivity :ThirdAct). done exec = pd.startExecution puts "node name = #{exec.node.name}, ended=#{exec.ended}, active=#{exec.active}, finished=#{exec.finished}"