1. 概要

ルールエンジンを使用することは、ビジネスロジックをボイラープレートコードから分離し、アプリケーションコードをビジネスの変更から保護するための優れた方法です。

Java Rule Engines に関する前回の記事で、JSR94仕様について説明しました。 JessRuleEngineはJSR94の参照ルールドライバーの実装として特に重要ですので、それを見てみましょう。

2. ジェスルールエンジン

Jess は、Javaと簡単に統合できる最も初期のルールエンジンの1つです。 Jessは、非常に効率的な Reteアルゴリズムの拡張実装を使用しており、ほとんどのシナリオで単純なJavaループよりもはるかに高速です。

ルールは、ネイティブのJessルール言語で記述されたルールセット、拡張されたLispベースの構文、またはより詳細なXML形式から実行できます。 ネイティブ形式を使用します。

開発用のEclipseベースのIDE(古いバージョンのEclipse用)と、Jessの使用およびJavaとの統合に関する優れたドキュメントがあります。 ルールファイルを作成する前にアイデアを試すことができるREPLコマンドラインインターフェイスもあります。

JSR 94の参照ルールエンジンとして、Jessは定義上JSR 94に準拠していますが、現在は活発に開発されていません。

2.1. JSR94についての簡単な言葉

JSR 94は、選択したルールエンジンからの独立性を提供するために使用できるAPIを提供します。 JSR 94準拠のルールエンジンをコードにプラグインして、アプリケーションでのルールエンジンとの対話方法を変更することなく、いくつかのルールを実行できます。

これは、ルールエンジンの基礎となるルールが同じように見えることを意味するわけではありません。ルールエンジンを変更した場合、それらを書き直す必要があるかもしれませんが、新しいルールエンジンを使用するためにアプリケーションの一部を書き直す必要がないことを意味します。 必要なコードの変更は、ドライバーの名前といくつかのルールファイル名を更新することだけです。

2.2. ジェスJSR94ドライバー

JSR 94にはJess用のリファレンスルールエンジンドライバーが含まれていますが、Jess自体はライセンスされた商用製品であるため含まれていません。 参照ドライバーはorg.jcp.jsr94.jessパッケージで提供されますが、 Jess をダウンロードすると、jess.jsr94パッケージで新しいドライバーが利用可能になります。

JSR 94レイヤーがこれをどのように変更するかを確認する前に、JessのネイティブJava統合を確認することから始めましょう。

3. 提供された例

Jessをコードに統合する前に、Jessをダウンロードして、クラスパスで使用できるようにしたことを確認しましょう。 すでにライセンスを持っていない限り、30日間の無料試用ダウンロードに登録する必要があります。

それでは、 Jess をダウンロードし、ダウンロードした Jess71p2.jar を解凍し、その例の1つを実行して、動作するバージョンがあることを確認しましょう。

3.1. スタンドアロンジェス

Jess71p2 / examples ディレクトリを見てみましょう。ここで、jessディレクトリにはいくつかのルールセットの例があります。 priceing_engine ディレクトリは、 ant build.xmlスクリプトを介して実行できる統合を示しています。 ディレクトリを価格設定エンジンの例に変更し、 anttestを介してプログラムを実行してみましょう。

cd Jess71p2/examples/pricing_engine
ant test

これにより、価格設定ルールセットの例が作成され、実行されます。

Buildfile: Jess71p2\examples\pricing_engine\build.xml
...
test:
[java] Items for order 123:
[java] 1 CD Writer: 199.99
...
[java] Items for order 666:
[java] 1 Incredibles DVD: 29.99
[java] Offers for order 666:
[java] BUILD SUCCESSFUL
Total time: 1 second

3.2. JSR94のジェス

Jessが機能するようになったので、 JSR 94 をダウンロードして解凍し、ant、doc、lib、およびsrcディレクトリを含むjsr94-1.0ディレクトリを作成します。

unzip jreng-1_0a-fr-spec-api.zip

これにより、JSR 94 APIとJessリファレンスドライバーが提供されますが、ライセンスされたJess実装が付属していないため、ここで例を実行しようとすると、次のエラーが発生します。

Error: The reference implementation Jess could not be found.

それでは、以前にダウンロードしたJess71p2の一部として提供されたJessリファレンス実装 jess.jar を追加し、それをJSR 94 libディレクトリにコピーしてから、例を実行してみましょう。

cp Jess71p2/lib/jess.jar jsr94-1.0/lib/
java -jar jsr94-1.0/lib/jsr94-example.jar

この例では、請求書の支払い時に顧客の残りのクレジットを決定するためのいくつかのルールを実行します。

Administration API Acquired RuleAdministrator: org.jcp.jsr94.jess.RuleAdministratorImpl@63947c6b
...
Runtime API Acquired RuleRuntime: org.jcp.jsr94.jess.RuleRuntimeImpl@68fb2c38
Customer credit limit result: 3000
...
Invoice 2 amount: 1750 status: paid
Released Stateful Rule Session.

4. JessとJavaの統合

JessとJSR94をダウンロードし、ネイティブとJSRの両方でいくつかのルールを実行したので、JessルールセットをJavaプログラムに統合する方法を見てみましょう。

この例では、Javaコードから単純なJessルールファイル hellojess.clp、を実行することから始め、次に別のルールファイルBonus.clpを確認します。これにより、一部のオブジェクトが使用および変更されます。

4.1. Mavenの依存関係

Jessに使用できるMaven依存関係がないため、まだ行っていない場合は、Jess jar( jess.jar )をダウンロードして解凍し、mvnインストールをローカルMavenにインストールします。リポジトリ:

mvn install:install-file -Dfile=jess.jar -DgroupId=gov.sandia -DartifactId=jess -Dversion=7.1p2 -Dpackaging=jar -DgeneratePom=true

次に、通常の方法で依存関係として追加できます。

<dependency>
    <groupId>gov.sandia</groupId>
    <artifactId>jess</artifactId>
    <version>7.1p2</version>
</dependency>

4.2. こんにちはジェスルールファイル

次に、メッセージを印刷するための最も単純なルールファイルを作成しましょう。 ルールファイルをhellojess.clpとして保存します。

(printout t "Hello from Jess!" crlf)

4.3. ジェスルールエンジン

次に、Jess Rete ルールエンジンのインスタンスを作成し、 reset()を初期状態に戻し、 hellojess.clp にルールをロードして、それらを実行します:

public class HelloJess {
    public static void main(String[] args) throws JessException {
    Rete engine = new Rete();
    engine.reset();
    engine.batch("hellojess.clp");
    engine.run();
}

この簡単な例では、潜在的なJessExceptionメインメソッドのthrows句に追加しました。

プログラムを実行すると、次の出力が表示されます。

Hello from Jess!

5. JessをJavaにデータで統合する

すべてが正しくインストールされ、ルールを実行できるようになったので、ルールエンジンが処理するデータを追加する方法と、結果を取得する方法を見てみましょう。

まず、使用するいくつかのJavaクラスが必要であり、次にそれらを使用する新しいルールセットが必要です。

5.1. モデル

いくつかの簡単なQuestionクラスとAnswerクラスを作成しましょう。

public class Question {
    private String question;
    private int balance;
    <i><span style="font-weight: 400">// getters and setters</span></i>

    public Question(String question, int balance) {
        this.question = question;
        this.balance = balance;
    }
}

public class Answer {
    private String answer;
    private int newBalance;
    <i><span style="font-weight: 400">// getters and setters</span></i>

    public Answer(String answer, int newBalance) {
        this.answer = answer;
        this.newBalance = newBalance;
    }
}

5.2. 入力と出力のあるジェスルール

次に、 Bonus.clp という単純なJessルールセットを作成します。このルールセットは、 Question を渡し、Answerを受け取ります。

まず、 import QuestionクラスとAnswerクラスをインポートし、次にJessのdeftemplate関数を使用してルールエンジンで使用できるようにします。

(import com.baeldung.rules.jsr94.jess.model.*)
(deftemplate Question     (declare (from-class Question)))
(deftemplate Answer       (declare (from-class Answer)))

Jess関数呼び出しを示す括弧の使用に注意してください。

次に、 defrule を使用して、Jessの拡張Lisp形式で単一のルール avoid-overdraft を追加します。これにより、Questionの残高がゼロ以下:

(defrule avoid-overdraft "Give $50 to anyone overdrawn"
    ?q <- (Question { balance < 0 })
    =>
    (add (new Answer "Overdrawn bonus" (+ ?q.balance 50))))

ここでは、「 ?」 オブジェクトを変数にバインドします q 「の右側の条件が <-“ マッチ。 この場合、ルールエンジンがバランスが0未満の質問を検出したときです。

その場合、「 =>」の右側のアクションがトリガーされ、エンジン add sa newAnswerオブジェクトが作業メモリーに追加されます。 answer パラメーターの「Overdrawnbonus」と、 newAmount パラメーターを計算するための(+)関数の2つの必須コンストラクター引数を指定します。

5.3. JessRuleEngineを使用したデータの操作

add()を使用して、一度に1つのオブジェクトをルールエンジンの作業メモリに追加したり、 addAll()を使用してデータのコレクションを追加したりできます。 add()を使用して1つの質問を追加しましょう。

Question question = new Question("Can I have a bonus?", -5);
engine.add(data);

すべてのデータが揃ったら、ルールを実行しましょう。

engine.run();

Jess Rete エンジンはその魔法を働かせ、関連するすべてのルールが実行されると戻ります。 この場合、検査するAnswerがあります。

jess.Filter を使用して、AnswerをルールエンジンからIterable結果オブジェクトに抽出してみましょう。

Iterator results = engine.getObjects(new jess.Filter.ByClass(Answer.class));
while (results.hasNext()) {
    Answer answer = (Answer) results.next();
    // process our Answer
}

単純な例には参照データはありませんが、参照データがある場合は、 WorkingMemoryMarkerおよびengine.mark()を使用して、ルールエンジンの動作状態をマークできます。データを追加した後のメモリ。 次に、マーカーを使用してengineresetToMarkを呼び出して、作業メモリーを「ロード済み」状態にリセットし、ルールエンジンを別のオブジェクトセットに効率的に再利用できます。

WorkingMemoryMarker marker;
// load reference data
marker = engine.mark();
// load specific data and run rules
engine.resetToMark(marker);

それでは、JSR94を使用してこれと同じルールセットを実行する方法を見てみましょう。

6. JSR94を使用してJessルールエンジンを統合する

JSR 94は、コードがルールエンジンと相互作用する方法を標準化します。 これにより、より良い代替案が出てきた場合に、アプリケーションを大幅に変更することなく、ルールエンジンを簡単に変更できます。

JSR 94 APIには、次の2つの主要なパッケージがあります。

  • javax.rules.admin –ドライバーとルールをロードするため
  • javax.rules –ルールを実行して結果を抽出します

これらの両方でクラスを使用する方法を見ていきます。

6.1. Mavenの依存関係

まず、jsr94のMaven依存関係を追加しましょう。

<dependency>
    <groupId>jsr94</groupId>
    <artifactId>jsr94</artifactId>
    <version>1.1</version>
</dependency>

6.2. 管理API

JSR 94の使用を開始するには、RuleServiceProviderをインスタンス化する必要があります。 1つ作成して、Jessルールドライバーを渡します。

String RULE_SERVICE_PROVIDER="jess.jsr94";
Class.forName(RULE_SERVICE_PROVIDER + ".RuleServiceProviderImpl");
RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);

次に、JessのJSR 94 RuleAdministrator を取得し、サンプルのルールセットをJSR 94 RuleExecutionSet、にロードして、選択したURIで実行できるように登録します。

RuleAdministrator ruleAdministrator = serviceProvider.getRuleAdministrator();

InputStream ruleInput = JessRunner.class.getResourceAsStream(rulesFile);
HashMap vendorProperties = new HashMap();

RuleExecutionSet ruleExecutionSet = ruleAdministrator
  .getLocalRuleExecutionSetProvider(vendorProperties)
  .createRuleExecutionSet(ruleInput, vendorProperties);

String rulesURI = "rules://com/baeldung/rules/bonus";
ruleAdministrator.registerRuleExecutionSet(rulesURI, ruleExecutionSet, vendorProperties);

Jessドライバーは、RuleAdministratorに提供したvendorPropertiesマップを必要としませんが、インターフェースの一部であり、他のベンダーが必要とする場合があります。

ルールエンジンプロバイダーであるJessが初期化され、ルールセットが登録されたので、ルールを実行する準備がほぼ整いました。

それらを実行する前に、それらを実行するためのランタイムインスタンスとセッションが必要です。 魔法が発生する場所のプレースホルダーcalculateResults()、も追加して、セッションを解放しましょう。

RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();
StatelessRuleSession statelessRuleSession
  = (StatelessRuleSession) ruleRuntime.createRuleSession(rulesURI, new HashMap(), RuleRuntime.STATELESS_SESSION_TYPE);
calculateResults(statelessRuleSession);
statelessRuleSession.release();

6.3. 実行API

これですべての準備が整いました。calculateResultsを実装して初期データを提供し、ステートレスセッションでルールを実行して、結果を抽出しましょう。

List data = new ArrayList();
data.add(new Question("Can I have a bonus?", -5));
List results = statelessRuleSession.executeRules(data);

JSR94はJDK5が登場する前に作成されたため、APIはジェネリックを使用しないため、Iteratorを使用して結果を確認してみましょう。

Iterator itr = results.iterator();
while (itr.hasNext()) {
    Object obj = itr.next();
    if (obj instanceof Answer) {
        int answerBalance = ((Answer) obj).getCalculatedBalance());
    }
}

この例ではステートレスセッションを使用しましたが、呼び出し間で状態を維持したい場合は、StatefuleRuleSessionを作成することもできます。

7. 結論

この記事では、Jessのネイティブクラスを使用し、もう少し努力してJSR 94を使用して、Jessルールエンジンをアプリケーションに統合する方法を学びました。 アプリケーションの実行時にルールエンジンによって処理される個別のファイルにビジネスルールを分離する方法を見てきました。

同じビジネスロジックのルールがあり、別のJSR 94準拠のルールエンジン用に記述されている場合は、代替ルールエンジンのドライバーを追加し、アプリケーションで使用するドライバー名を更新するだけで、コードを変更する必要はありません。必要。

JavaアプリケーションへのJessの埋め込みの詳細については、jess.sandia.govを参照してください。また、Oracleには、 Java Rule Engine API(JSR 94)の開始に関する便利なガイドがあります。

いつものように、この記事で見たコードは、GitHubから入手できます。