1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Drools】ルールの実行制御機能を試す

Posted at

今回は、Drools のルール実行制御機能について紹介していきます。

ルールの実行制御

次のようなルールがある状況で、Rule_ARule_CRule_B の順番で動作することを保証したいとします。

rule "Rule_A"
  when    〇〇の条件をみたすとき
  then    XXを行う
end

rule "Rule_B"
  when    〇〇の条件をみたすとき
  then    XXを行う
end

rule "Rule_C"
  when    〇〇の条件をみたすとき
  then    XXを行う
end

ルールの動作順を制御するために、Drools には表のような機能が用意されています。

機能名 概要
Agenda group agenda-group という属性でルールをグループ分けし、実行順を制御する仕組み
Ruleflow group ruleflow-group という属性でルールをグループ分けし、実行順を制御する仕組み
Declarative agenda 「どのルールを動かすか」という判断自体もルールで記述する仕組み(実験的機能)

Agenda group, Ruleflow group について

最近の Drools (v8〜10) では、ルールのグループ分けは Rule Unit で行うことが推奨されています。1
ただ、長年メンテナンスされてきた機能を使いたいユーザもいると考え、本記事ではあえてレガシーな機能を検証します。

サンプルコード

ここからは、各実行制御機能のサンプル実装を一部抜粋する形で紹介していきます。

コード全量は GitHub にアップしており、それぞれの機能ごとにプロジェクトを分けています。

開発環境情報など

・openjdk 17
・Apache Maven 3.9.6
・Drools 10.0.0

検証用オブジェクト

ルールが意図した順序で動いているかを確認するため、以下の SampleData クラスを使用します。
「実行されたルール名リスト」、「最後に実行されたルール名」を保持することで制御順をトレスします。
Junit テストコードで値が想定通りかを確認します。

SampleData.java
public class SampleData {
    // これまで実行されたルール名のリスト
    private List<String> executedRules = new ArrayList<>();

    // 最後に実行されたルール名
    private String latestRule = "";

    public void addRuleName(String ruleName) {
        this.executedRules.add(ruleName);
    }
    public void setLatestRule(String ruleName) {
        this.latestRule = ruleName;
    }
    // 他メソッドは省略

Agenda group

サンプルプロジェクト

agenda-group 属性でルールをグループ分けすることができます。
以下が DRL で、Rule_A, Rule_B, Rule_C という3つのルールを定義し、それぞれ Phase_1~3の 3 グループに分けています。

Sample.drl
rule "Rule_A"
    agenda-group "Phase_1"

    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_A");
        $data.setLatestRule("Rule_A");
        System.out.println($data);
end

rule "Rule_B"
    agenda-group "Phase_2"

    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_B");
        $data.setLatestRule("Rule_B");
        System.out.println($data);
end

rule "Rule_C"
    agenda-group "Phase_3"

    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_C");
        $data.setLatestRule("Rule_C");
        System.out.println($data);
end

Java コード上でグループ名を指定し活性化することで、当該グループ内のルールのみ動作させることができます。
以下テストコードで、Rule_ARule_CRule_B の順に実行されたことを確認できます。

AgendaGroupTest.java
    @Test
    public void test_実行順検証() {

        var data = new SampleData();
        kieSession.insert(data);

        // Phase_1
        kieSession.getAgenda().getAgendaGroup("Phase_1").setFocus();
        kieSession.fireAllRules();

        // Phase_3
        kieSession.getAgenda().getAgendaGroup("Phase_3").setFocus();
        kieSession.fireAllRules();

        // Phase_2
        kieSession.getAgenda().getAgendaGroup("Phase_2").setFocus();
        kieSession.fireAllRules();

        // assert
        assertEquals(Arrays.asList("Rule_A", "Rule_C", "Rule_B"), data.getExecutedRules());
        assertEquals("Rule_B", data.getLatestRule());
    }

Ruleflow group

サンプルプロジェクト

Agenda group と同様、DRL に属性( ruleflow-group )を指定してルールをグループ分けする仕組みです。
Ruleflow group はもともと、DRL を BPMN2 ワークフローと組み合わせ、実行順を制御する機能でした。

BPMN モデルを記述した XML ファイルをプロジェクトに追加して、jbpm-bpmn2というライブラリで Drools と連携する仕掛けのようです。

ただし、公式のIssueをおったところ、この方式は Drools v10 ではサポートされてないようでした。

Agenda group 同様に Java コードで活性化するとしたら、下記のようになります。

Sample.drl
rule "Rule_A"
    ruleflow-group "Phase_1"

    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_A");
        $data.setLatestRule("Rule_A");
        System.out.println($data);
end
// Rule_B, Rule_C も同様
RuleflowGroupTest.java
    @Test
    public void test_実行順検証() {

        var data = new SampleData();
        kieSession.insert(data);

        // Phase_1
        ((InternalAgenda) kieSession.getAgenda()).activateRuleFlowGroup("Phase_1");
        kieSession.fireAllRules();

        // Phase_3
        ((InternalAgenda) kieSession.getAgenda()).activateRuleFlowGroup("Phase_3");
        kieSession.fireAllRules();

        // Phase_2
        ((InternalAgenda) kieSession.getAgenda()).activateRuleFlowGroup("Phase_2");
        kieSession.fireAllRules();

        // assert
        assertEquals(Arrays.asList("Rule_A", "Rule_C", "Rule_B"), data.getExecutedRules());
        assertEquals("Rule_B", data.getLatestRule());
    }

キャストで内部APIを呼ぶ特殊な方式になるため、おとなしく Agenda group を使ったほうがよさそうです..。

Declarative agenda

サンプルプロジェクト

他ルールの動作をブロック、もしくはブロック解除するルール(※)を定義することで、実行制御する仕組みです。

(※) 以降、「制御用ルール」と呼びます

公式ドキュメント3で実験的機能として紹介されており、将来的に大きく変更される可能性があります。

デフォルトでは本機能はオフになっているため、kmodule.xmlで有効化する必要があります。

kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="DeclarativeKBase" declarativeAgenda="enabled">
        <ksession name="DeclarativeKSession"/>
    </kbase>
</kmodule>

以下が DRL です。

Sample.drl
rule "Rule_A" @Eager @phase("Phase_1")
    lock-on-active true

    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_A");
        modify ($data) { setLatestRule("Rule_A") };
        System.out.println($data);
end

rule "Rule_B" @Eager @phase("Phase_2")
    lock-on-active true
    
    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_B");
        modify ($data) { setLatestRule("Rule_B") };
        System.out.println($data);
end

rule "Rule_C" @Eager @phase("Phase_3")
    lock-on-active true

    when
        $data : SampleData()
    then
        $data.addRuleName("Rule_C");
        modify ($data) { setLatestRule("Rule_C") };
        System.out.println($data);
end

rule "Phase_2 ルールをブロック" @Direct

    when
        $m: Match(rule.metaData["phase"] == "Phase_2")
        $d: SampleData(latestRule == "Rule_A")
    then
        System.out.println("ブロック");
        kcontext.blockMatch($m);
end

rule "Phase_2 ルールをブロック解除" @Direct

    when
        $m: Match(rule.metaData["phase"] == "Phase_2")
        $d: SampleData(latestRule == "Rule_C")
    then
        System.out.println("ブロック解除");
        kcontext.unblockAllMatches($m);
end

DRL では下記のアノテーションを使用しています。

annotation 説明
@Eager 実行順を制御される可能性のあるルールに付与
@Direct 制御用ルールに付与
条件をみたした時点で即座に発動することを許可する
@phase ルールにメタデータを付与し、制御用ルールの条件で使用可能
名前は「phase」以外でもよい

@Direct を付けた 2 ルールが制御用ルールです。
Phase_2 メタデータが付与されたルールの動きを監視し、Rule_C が完了するまでは実行をブロックします。

以下が動作確認用のテストコードです。

DeclarativeAgendaTest.java
    @Test
    public void test_実行順検証() {

        var data = new SampleData();
        kieSession.insert(data);

        // execute
        kieSession.fireAllRules();

        // assert
        assertEquals(Arrays.asList("Rule_A", "Rule_C", "Rule_B"), data.getExecutedRules());
        assertEquals("Rule_B", data.getLatestRule());
    }

終わりに

Drools ルール実行制御機能を 3 つ紹介しました。
個人的には Agenda group が最もシンプルで使いやすそうです。

  1. 公式ドキュメント Legacy rule attributes を参照

  2. Business Process Model and Notation

  3. 公式ドキュメント Declarative agenda を参照

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?