本記事では、DRL で実装されたプロジェクト内のルールのうち、一部を抽出して動かす方法について書いていきます。
サンプルコード
以下 2 パターンの実装例を紹介します。
- 複数ある DRL ファイルのうち、1 ファイルに含まれるルールのみ動作させる
- DRL ファイル内にある 1 つのルールのみ動作させる
コード全量は、GitHub にあげています。
データクラス
ルールに渡すデータクラスを定義します。
このクラスは、ルールの名称を Set で保持する構造にしています。
public class ExecutedRules {
// これまで実行されたルール名のリスト
private Set<String> result = new HashSet<>();
public void recordRuleName(String ruleName) {
this.result.add(ruleName);
}
public Set<String> getExecutedRules() {
return result;
}
...
DRL
検証に用いる DRL は、先述したExecutedRulesオブジェクトを受け取り、実行されたルール名をオブジェクトに記録する仕様とします。
決定表形式で仕様を表現すると、次のようになります。
| 条件 (when) | 実行処理 (then) | |
|---|---|---|
| Rule_A | ExecutedRules が存在 | ExecutedRules に Rule_A を記録する |
| Rule_B | ExecutedRules が存在 | ExecutedRules に Rule_B を記録する |
| Rule_C | ExecutedRules が存在 | ExecutedRules に Rule_C を記録する |
| Rule_D | ExecutedRules が存在 | ExecutedRules に Rule_D を記録する |
これを 2 つのDRL ファイルで実装します。
1 つ目には、表の 1 行目~ 3 行目のルールを定義します。
package org.example.partialexec;
rule "Rule_A"
when
$data : ExecutedRules()
then
$data.recordRuleName("Rule_A");
end
rule "Rule_B"
when
$data : ExecutedRules()
then
$data.recordRuleName("Rule_B");
end
rule "Rule_C"
when
$data : ExecutedRules()
then
$data.recordRuleName("Rule_C");
end
2 つ目の DRL ファイルには、4 行目のルールを定義します。
package org.example.partialexec;
rule "Rule_D"
when
$data : ExecutedRules()
then
$data.recordRuleName("Rule_D");
end
このルールをそのまま動かすと、ExecutedRulesオブジェクトには、Rule_A, Rule_B, Rule_C, Rule_D の 4 件のルール名が記録されます。
動作確認用のテストコードは次のとおりです。
@Test
public void test_全ルール実行() {
KieServices ks = KieServices.Factory.get();
KieContainer kieContainer = ks.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession();
try {
var data = new ExecutedRules();
kieSession.insert(data);
kieSession.fireAllRules();
// assert
assertEquals(Set.of("Rule_A", "Rule_B", "Rule_C", "Rule_D"), data.getExecutedRules());
} finally {
kieSession.dispose();
}
}
1 DRL ファイルに含まれるルールのみを動かす
まず、Rule_A_B_C.drl に含まれるルールのみが動くように制御してみます。
KieFileSystem という仕組みを用いると、特定の DRL ファイルを指定したうえでルールを動作させることが可能です。
以下が動作確認用のテストコードです。
@Test
public void test_指定したDRLファイルのみ動かす() {
// 動かしたいDRLファイル(1ファイルだけ)をクラスパスから読み込み、リソースとして追加
KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
kfs.write(ResourceFactory.newClassPathResource("org/example/partialexec/Rule_A_B_C.drl"));
ks.newKieBuilder(kfs).buildAll();
KieContainer kContainer = ks.newKieContainer(ks.getRepository().getDefaultReleaseId());
KieSession kieSession = kContainer.newKieSession();
try {
var data = new ExecutedRules();
kieSession.insert(data);
kieSession.fireAllRules();
// assert
assertEquals(Set.of("Rule_A", "Rule_B", "Rule_C"), data.getExecutedRules());
} finally {
kieSession.dispose();
}
}
本テストがパスするので、Rule_A_B_C.drl に含まれるルールのみ実行されたことがわかります。
DRL ファイル内のルール 1 件のみを動かす
次に、DRLファイルに含まれる 1 件のルールのみを抽出し動かしてみます。
これは、先述した決定表のうちの 1 行が相当します。
DRL ファイル内における「Rule_A」ルールのみを動かしたい場合、RuleNameEqualsAgendaFilterという仕組みを用い、動作させたいルール名称を指定します。
@Test
public void test_指定した単一ルールのみを動かす() {
KieServices ks = KieServices.Factory.get();
KieContainer kieContainer = ks.getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession();
try {
var data = new ExecutedRules();
kieSession.insert(data);
// 「Rule_A」のみを実行
kieSession.fireAllRules( new RuleNameEqualsAgendaFilter( "Rule_A" ) );
// assert
assertEquals(Set.of("Rule_A"), data.getExecutedRules());
} finally {
kieSession.dispose();
}
}
本テストがパスするので、「Rule_A」のみ実行されたことがわかります。
終わりに
本番環境で動かすルールのうち、一部を区分けして管理・実行したい場合は、過去記事で紹介したagenda-group, Rule Unitを使うのがよいと思います。
今回の方法は、プロジェクトが Rule Unit に移行しておらず、DRLの数も多い場合において、一部分のみを手元で検証したいときに役立つのではと思います。