Red Hat Training

A Red Hat training course is available for JBoss Enterprise SOA Platform

JBoss Rules 5 リファンレンスガイド

JBoss Enterprise SOA Platform 5

このガイドは開発者向けです。

概要

本ガイドでは、開発者向けに JBoss Rules の使用方法について説明します。

はじめに

1. ドキュメント規則

本ガイドでは、いくつかの規則を使用して特定の単語やフレーズを強調表示し、特定の情報への注意を促しています。

1.1. 表記規則

特定の単語や句への注意を促すために 4 つの表記慣習を使用しています。これらの規則や、これらが適用される状況は以下のとおりです。
等幅ボールド
シェルコマンド、ファイル名、パスなど、システム入力を強調表示するために使用されます。キーとキーの組み合わせを強調表示するためにも使用されます。以下に例を示します。
現在の作業ディレクトリーのファイル my_next_bestselling_novel の内容を表示するには、シェルプロンプトで cat my_next_bestselling_novel コマンドを入力し、Enter を押してコマンドを実行します。
上記には、ファイル名、シェルコマンドおよびキーが含まれます。これはすべて等幅ボールドで表示され、コンテキストにより区別可能なものになります。
キーの組み合わせは、各部分をつなぐプラス記号によって、個別のキーと区別できます。以下に例を示します。
Enter を押してコマンドを実行します。
Ctrl+Alt+F2 を押して、仮想ターミナルに切り替えます。
最初の例では、押す特定のキーを強調表示しています。2 つ目の例は、同時に押す 3 つのキーのセットというキーの組み合わせを強調表示しています。
ソースコードの場合、段落内で記述されるクラス名、メソッド、関数、変数名、および戻り値は、上記のように 等幅ボールド で示されます。以下に例を示します。
ファイル関連のクラスには、ファイルシステムの filesystem、ファイルの file、ディレクトリーの dir が含まれます。各クラスには、独自の関連付けられたパーミッションセットがあります。
プロポーショナルボールド
これは、アプリケーション名、ダイアログボックステキスト、ラベルが付いたボタン、チェックボックスおよびラジオボタン、メニュータイトルおよびサブメニュータイトルなど、システムで発生した単語またはフレーズを示します。以下に例を示します。
メインメニューバーから SystemPreferencesMouse を選択し、Mouse Preferences を起動します。Buttons タブで、Left-handed mouse チェックボックスを選択し、Close をクリックしてメインのマウスボタンを左から右に切り替えます (マウスを左手で使い易くします)。
特殊文字を gedit ファイルに挿入するには、メインメニューバーから ApplicationsAccessoriesCharacter Map を選択します。次に、Character Map メニューバーから SearchFind… を選択し、Search フィールドに文字の名前を入力して Next をクリックします。目的の文字が Character Table で強調表示されます。この強調表示した文字をダブルクリックして Text to copy フィールドに配置し、Copy ボタンをクリックします。ここでドキュメントに戻り、gedit メニューバーから EditPaste を選択します。
上記のテキストにはアプリケーション名、システム全体のメニュー名および項目、アプリケーション固有のメニュー名、GUI インターフェイス内のボタンおよびテキストなどがあります。すべては proportional bold で示され、コンテキストと区別できます。
等幅ボールドイタリック または プロポーショナルボールドイタリック
等幅ボールドまたはプロポーショナルボールドのいずれでも、イタリックが追加されると、置換または変数テキストを意味します。イタリックは、状況に応じて変化するテキストや、文字を入力しないテキストを表します。以下に例を示します。
ssh を使用してリモートマシンに接続するには、シェルプロンプトで ssh username@domain.name を入力します。リモートマシンが example.com で、そのマシン上でのユーザー名が john の場合は、ssh john@example.com と入力します。
mount -o remount file-system コマンドにより、指定したファイルシステムが再マウントされます。たとえば、/home ファイルシステムを再マウントする場合、コマンドは mount -o remount /home となります。
現在インストールされているパッケージのバージョンを表示するには、rpm -q package コマンドを使用します。これにより、package-version-release のような結果が返されます。
上記の太字のイタリック体の用語、username、domain.name、file-system、package、version、および release に注意してください。各単語はプレースホルダーで、コマンドの発行時に入力するテキストまたはシステムによって表示されるテキストのどちらかになります。
作業のタイトルを示す標準的な使用法のほかに、イタリックは新用語と重要な用語の最初の使用を示します。以下に例を示します。
Publican は DocBook 公開システムです。

1.2. 引用規則

端末の出力およびソースコードの一覧は、周りのテキストから視覚的に表示されます。
ターミナルに送信される出力は mono-spaced roman に設定され、以下のように表示されます。
books        Desktop   documentation  drafts  mss    photos   stuff  svn
books_tests  Desktop1  downloads      images  notes  scripts  svgs
ソースコードの一覧も mono-spaced roman に設定されますが、以下のように構文の強調表示が追加されます。
static int kvm_vm_ioctl_deassign_device(struct kvm *kvm,
                 struct kvm_assigned_pci_dev *assigned_dev)
{
         int r = 0;
         struct kvm_assigned_dev_kernel *match;

         mutex_lock(&kvm->lock);

         match = kvm_find_assigned_dev(&kvm->arch.assigned_dev_head,
                                       assigned_dev->assigned_dev_id);
         if (!match) {
                 printk(KERN_INFO "%s: device hasn't been assigned before, "
                   "so cannot be deassigned\n", __func__);
                 r = -EINVAL;
                 goto out;
         }

         kvm_deassign_device(kvm, match);

         kvm_free_assigned_device(kvm, match);

out:
         mutex_unlock(&kvm->lock);
         return r;
}

1.3. 注記および警告

最後に、3 つの視覚的スタイルを使用して、見落とす可能性のある情報に注意を促します。
注記
注記とは、タスクへのヒント、ショートカット、または代替アプローチです。注意を無視しても悪い結果を招くことはありませんが、便利なヒントを見逃してしまう可能性があります。
重要
見落としやすい詳細のある重要なボックス: 現行セッションにのみ適用される設定変更や、更新を適用する前に再起動が必要なサービスなどです。Important というラベルが付いたボックスを無視しても、データが失われることはありませんが、スムーズな操作が行えないことがあります。
警告
警告は無視すべきではありません。警告を無視すると、データが失われる可能性があります。

2. ヘルプの利用とフィードバック提供

2.1. ヘルプが必要ですか ?

本ガイドで説明されている手順で問題が発生した場合は、Red Hat カスタマーポータル http://access.redhat.com にアクセスしてください。カスタマーポータルでは、以下を行うことができます。
  • Red Hat 製品に関する技術サポート記事の知識ベースの検索または閲覧。
  • Red Hat グローバルサポートサービス (GSS) へのサポートケースの送信。
  • その他の製品ドキュメントへのアクセス。
Red Hat は、Red Hat のソフトウェアおよびテクノロジーについて、多くの電子メーリングリストも提供しています。一般に公開されているメーリングリストの一覧は、https://www.redhat.com/mailman/listinfoを参照してください。メーリングリストの名前をクリックして、その一覧をサブスクライブするか、またはメーリングリストのアーカイブにアクセスします。

2.2. ご意見をお寄せください

本ガイドで誤字脱字を発見されたり、このマニュアルを改善するための提案をお持ちの場合は、弊社までご連絡ください。JBoss Enterprise SOA Platform の製品に対して、http://bugzilla.redhat.com/ から Bugzilla レポートを送信してください。
バグレポートを送信する場合は、マニュアル識別子 『JBoss_Rules_5_Reference_Guide』を指定してください。
本ガイドを改善するためのご意見やご提案をお寄せいただく場合は、できるだけ具体的にご説明ください。エラーが見つかった場合は、簡単に確認できるように、セクション番号と前後のテキストを含めてください。

第1章 はじめに

1.1. ビジネス統合

動的かつアジャイルなビジネスインフラストラクチャーを提供するためには、異なるアプリケーションとデータソースが最小限のオーバーヘッドで相互に通信できるように、サービス指向のアーキテクチャーを用意することが重要です。
JBoss Enterprise SOA Platform は、ビジネスプロセスの変更に対応するために、継続的に再利用することなく、ビジネスサービスをオーケストレーションできるフレームワークです。JBoss Enterprise SOA Platform では、ビジネスルールとメッセージの変換およびルーティング機能を使用することで、複数のソースからビジネスデータを操作できます。

1.2. サービス指向アーキテクチャーとは

はじめに

SOA (Service Oriented Architecture) は単一のプログラムまたはテクノロジーではありません。ソフトウェア設計パラダイムと見なします。

すでに分かっているように、ハードウェアバス は、複数のシステムとサブシステムを結び付ける物理コネクターです。これを使用する場合は、システムのペア間で多数のポイントツーポイントコネクターを使用する代わりに、各システムを中央バスに接続するだけです。エンタープライズサービスバス (ESB)は、ソフトウェアとまったく同じことを行います。
アーキテクトは、メッセージングシステムの上記のアーキテクチャー層にあります。このメッセージングシステムは、このメッセージングシステムを使用することでサービス間の 非同期通信 を容易にします。実際、アーキテクトを使用している場合、すべてを概念的に、サービス (このコンテキストではアプリケーションソフトウェア)またはサービス間で送信される メッセージ のいずれかです。サービスは接続アドレスとして一覧表示されます (エンドポイント参照 と呼ばれています)。
このコンテキストでは、サービスは必ずしも Web サービスであるとは限らないことに注意することが重要です。File Transfer Protocol や Java Message Service などのトランスポートを使用する他のタイプのアプリケーションもサービスになる可能性があります。
注記
この時点で、エンタープライズサービスバスがサービス指向のアーキテクチャーと同じ場合は、妨げられる場合があります。回答は Not exactly です。 アーキテクトは、サービス指向のアーキテクチャーを形成しません。代わりに、ツールを多数使用して構築できます。特に、SOA が必要とする loose-coupling および 非同期メッセージを渡すこと が容易になります。SOA は常にソフトウェア以外のものと考えてください。これは一連の原則、パターン、およびベストプラクティスです。

1.3. サービス指向アーキテクチャーの重要なポイント

以下は、サービス指向のアーキテクチャーの主要なコンポーネントです。
  1. 交換される メッセージ
  2. サービスリクエスターおよびプロバイダーとして動作する エージェント
  3. メッセージを送受信できるようにする 共有トランスポートメカニズム

1.4. JBoss Enterprise SOA Platform とは

JBoss Enterprise SOA Platform は、エンタープライズアプリケーションインテグレーション (EAI) およびサービス指向アーキテクチャー (SOA) ソリューションを開発するためのフレームワークです。これは、エンタープライズサービスバス (JBoss Warehouse) およびビジネスプロセス自動化インフラストラクチャーで設定されます。これにより、ビジネスサービスの構築、デプロイ、統合、オーケストレーションを行うことができます。

1.5. Service-Oriented Architecture Paradigm

SOA (Service-ient Architecture)は、リクエスター、プロバイダー、およびブローカーの 3 つのロールで設定されます。
サービスプロバイダー
サービスプロバイダーはサービスへのアクセスを許可し、サービスの説明を作成し、サービスブローカーに公開します。
サービスリクエスター
サービスリクエスターは、サービスブローカーが提供するサービスの説明を検索して、サービスを検出します。リクエスターはサービスプロバイダーが提供するサービスにバインドするロールも果たします。
サービスブローカー
サービスブローカーは、サービスの説明のレジストリーをホストします。リクエスターをサービスプロバイダーにリンクします。

1.6. コアおよびコンポーネント

JBoss Enterprise SOA Platform は、データ統合のニーズに対応する包括的なサーバーを提供します。基本的なレベルでは、Enterprise Service Bus を介してビジネスルールを更新し、メッセージをルーティングすることができます。
JBoss Enterprise SOA Platform の中心となるのは、Enterprise Service Bus です。JBoss (ESB) はメッセージの送受信を行う環境を作成します。メッセージにアクションを適用して変換し、サービス間でルーティングすることができます。
JBoss Enterprise SOA Platform を設定するコンポーネントは複数あります。ESB とともに、レジストリ (jUDDI)、変換エンジン (Smooks)、メッセージキュー (HornetQ)、BPEL エンジン (Riftsaw) が用意されています。

1.7. JBoss Enterprise SOA Platform のコンポーネント

  • 完全な Java EE 準拠のアプリケーションサーバー (JBoss Enterprise Application Platform)
  • Enterprise Service Bus (JBoss ESB)
  • ビジネスプロセス管理システム (jBPM)
  • ビジネスルールエンジン (JBoss ルール)
  • オプションの JBoss Enterprise Data Services (EDS) 製品のサポート。

1.8. JBoss Enterprise SOA Platform の機能

JBoss Enterprise Service Bus (ESB)
ドメインはサービス間でメッセージを送信し、それらを変換して、異なるタイプのシステムで処理できるようにします。
Business Process Execution Language (BPEL)
Web サービスを使用して、この言語を使用してビジネスルールをオーケストレーションできます。これは、ビジネスプロセス命令を簡単に実行するために SOA に含まれています。
Java Universal Description、Discovery and Integration (jUDDI)
これは SOA のデフォルトサービスレジストリーです。ここで説明する内容は、インストラクター上のサービスに関する情報をすべて保管する場所です。
Smooks
この変換エンジンは SOA と組み合わせて使用してメッセージを処理できます。また、メッセージを分割して正しい宛先に送信するためにも使用できます。
JBoss ルール
これは、SOA にパッケージ化されたルールエンジンです。受信するメッセージからデータを推測して、実行する必要があるアクションを判別できます。

1.9. JBoss Enterprise SOA Platform の JBossESB コンポーネントの機能

JBoss Enterprise SOA Platform の JBossESB コンポーネントは以下をサポートします。
  • 複数のトランスポートおよびプロトコル
  • リスナーアクションモデル (これにより、サービスを相互に選択可能)
  • コンテンツベースのルーティング (JBoss Rules エンジン、XPath、Regex、および Smooks 経由)
  • サービスオーケストレーション機能を提供するために JBoss Business Process Manager (jBPM) との統合
  • ビジネスルールの開発機能を提供するために、JBoss ルールとの統合
  • BPEL エンジンとの統合。
さらに、新しいデプロイメントにレガシーシステムを統合し、同期または非同期で通信できるようにします。
さらに、エンタープライズサービスバスは、以下を可能にするインフラストラクチャーおよびツールセットを提供します。
  • さまざまなトランスポートメカニズム (電子メールや JMS など) と連携するよう設定されます。
  • 汎用オブジェクトリポジトリーとして使用します。
  • プラグ可能なデータ変換メカニズムを実装できるようにします。
  • 対話のロギングをサポートします。
重要
ソースコードには、org.jboss.internal.soa.esborg.jboss.soa.esb の 2 つのツリーがあります。org.jboss.internal.soa.esb パッケージの内容をそのまま使用します。これは、通知なしに変更される可能性があるためです。これとは対照的に、org.jboss.soa.esb パッケージ内のすべての内容は、Red Hat の非推奨ポリシーでカバーされます。

1.10. タスク管理

JBoss SOA は、影響を受けるすべてのシステムで汎用的に実行するタスクを指定することにより、タスクを簡素化します。つまり、ユーザーは各ターミナルで個別に実行するようにタスクを設定する必要はありません。ユーザーは、Web サービスを使用してシステムを簡単に接続できます。
JBoss SOA を使用して各マシンに複数回ではなく、ネットワーク全体でトランザクションを 1 回委譲することで、時間を節約できます。これにより、エラーが発生する可能性も低くなります。

1.11. 統合のユースケース

ACME Equity は、大規模な経理サービスです。会社には多くのデータベースやシステムがあります。旧式の COBOL ベースのレガシーシステムや、近年で小規模な企業で取得されるデータベースもあります。ビジネスルールが頻繁に変化するため、これらのデータベースを統合することは困難でコストがかかります。会社は、クライアント向け e-commerce の Web サイトを新たに開発することを希望していますが、現在使用している既存のシステムとは同期しない場合があります。
会社は、安価なソリューションを希望していますが、企業セクターの厳密な規制およびセキュリティー要件に準拠するソリューションを検討しています。会社が望ましくないことは、レガシーデータベースおよびシステムを接続するために glob コードを作成および維持する必要があることです。
JBoss Enterprise SOA Platform は、これらのレガシーシステムを新しいお客様の Web サイトに統合するためにミドルウェアレイヤーとして選択されました。フロントエンドシステムとバックエンドシステムの間のブリッジを提供します。JBoss Enterprise SOA Platform で実装されたビジネスルールは、すぐに簡単に更新できます。
その結果、SOA の統合方法により、古いシステムは新しいシステムと同期できるようになりました。1 カ月あたり数万のトランザクションであっても、ボトルネックはありません。XML、JMS、FTP などのさまざまな統合タイプは、システム間でデータを移動するために使用されます。数多くのエンタープライズ標準のメッセージングシステムの 1 つを JBoss Enterprise SOA Platform にプラグインすることで、柔軟性を高めることができます。
さらに利点は、既存のインフラストラクチャーにより多くのサーバーやデータベースが追加されると、システムを簡単にスケールアップできることです。

1.12. ビジネス環境での JBoss Enterprise SOA Platform の使用

エラーメッセージが発生する可能性が低いサービスの実装により、コストを削減できます。生産性とソーシングオプションの強化により、継続的なコストを削減できます。
情報およびビジネスプロセスは、接続が増加するため、迅速に共有できます。これは Web サービスによって強化され、クライアントを簡単に接続するために使用できます。
レガシーシステムは Web サービスと組み合わせて使用して、異なるシステムが同じ言語にピークにすることができます。これにより、システムの同期に必要なアップグレードおよびカスタムコードの量が減ります。

第2章 はじめに

2.1. 対象読者

本書は、JBoss Rules でツールを使用する方法を学習したいシステム管理者を対象としています。新しいプロジェクトの作成方法、プロジェクトのデバッグ方法、およびエディターの使用方法について説明します。

2.2. ガイドの目的

本ガイドは、JBoss ルールの使用方法をユーザーに概要を説明することを目的としています。ユーザーは基本的な用語を取り、最初からルールを作成する方法を学びます。プロジェクトのルールの作成に役立つチュートリアルも複数あります。

第3章 クイックスタート

3.1. JBoss Rules

JBoss Rules は、JBoss Enterprise SOA Platform 製品の一部として提供されるビジネスルールエンジンの名前です。

3.2. JBoss Rules Engine

JBoss Rules エンジンは、ルールを適用し、ナレッジ表現および理由(KRR)機能を開発者に提供するコンピュータープログラムです。

3.3. 実稼働ルール

実稼働ルール は、ナレッジを表すために最初の順序ロジックを使用する 2 つの構造です。以下の形式を取ります。
when
	<conditions>
then
	<actions>

3.4. 推論エンジン

推論エンジン は、実稼働ファクトとデータをルールにマッチする JBoss Rules エンジンの一部です。その後、情報からの推測に基づいてアクションを実行します。実稼働環境のルールシステムの推論エンジンは ステートフル であり、真理維持 を行います。

3.5. ReteOO

JBoss ルールで使用される Rete 実装は ReteOO と呼ばれます。これは、オブジェクト指向システム専用の Rete アルゴリズムの拡張および最適化された実装です。

3.6. 実稼働メモリー

実稼働メモリー は、ルールが保存される場所です。

3.7. ワーキングメモリー

ワーキングメモリー は、ファクトがアサートされる JBoss Rules エンジンの一部です。ここから、ファクトを変更または取り消すことができます。

3.8. 競合解決ストラテジー

アジェンダに複数のルールがある場合は、競合解決が必要です。ルールを発行するとワーキングメモリーに悪影響を及ぼす可能性があるため、ルールエンジンはルールが実行される順序を認識する必要があります(たとえば、ruleA を実行すると、ruleB がアジェンダから削除される可能性があります)。

3.9. ハイブリッドルールシステム

ハイブリッドルールシステムは、前向き連鎖のルールシステムと後向き連鎖のルールシステムの両方を使用してルールを処理することに関連します。

3.10. forward-Chaining

forward-chaining は、実稼働環境のルールシステムです。データ駆動型であるため、提示されるデータに反応します。ファクトは作業メモリーに挿入され、1 つ以上のルールが true になります。その後、アジェンダによって実行されるスケジュールに配置されます。
JBoss ルールは、前向き連鎖のエンジンです。

3.11. 後向き連鎖

後向き連鎖のルールシステムは、目的駆動型です。これは、システムがエンジンが満たそうとする 結論 から開始することを意味します。これを行わないと、サブとなる目的(つまり、現在の目的の一部を完了する結論)を検索します。最初の結論が満たされるか、サブゴールが満たされなくなるまでこのプロセスが続行されます。Prolog は、後向き連鎖エンジンの例です。
後向き連鎖
後向き連鎖は JBoss BRMS 5.2 に実装されました。

3.12. 推論機能

JBoss ルールは後向き 連鎖の理由付け機能 を使用して、データから適用するルールを推測できるようにします。

3.13. エキスパートシステム

エキスパートシステム は、フレームワークが実際のモデルを使用してドメインを表し、知識と説明のための機能が含まれている場合に形成されます。

3.14. Rete Root ノード

Rete00 を使用する場合、ルートノードはすべてのオブジェクトがネットワークに入る場所です。そこから、ObjectTypeNode に即座に移動します。

3.15. ObjectTypeNode

ObjectTypeNode は、ルールエンジンのワークロードを減らすのに役立ちます。複数のオブジェクトがあり、ルールエンジンがすべてのオブジェクトに対してすべてのノードを評価しようとすると、多くのサイクルが無駄になります。効率を高めるために、ObjectTypeNode を使用して、エンジンがオブジェクトのタイプに一致するノードにのみオブジェクトを渡すようにします。これにより、アプリケーションが新しいアカウントをアサートしても、Order オブジェクトのノードに伝播されません。
JBoss Rules では、アサートされたオブジェクトは、オブジェクトのクラスから HashMap の検索を介して有効な ObjectTypesNodes のリストを取得します。このリストが存在しない場合は、リスト内のキャッシュする有効な一致を見つけたすべての ObjectTypeNodes をスキャンします。これにより、JBoss ルールは、instanceof チェックと一致するすべてのクラス型と照合できます。

3.16. AlphaNodes

AlphaNodes は、リテラル条件を評価するために使用されます。ルールに、単一のオブジェクトタイプに対して複数のリテラル条件がある場合、それらは相互にリンクされます。つまり、アプリケーションが Account オブジェクトをアサートする場合は、次の AlphaNode に進む前に、最初に最初のリテラル条件を満たす必要があります。
AlphaNodes は ObjectTypeNodes を使用して伝播されます。

3.17. ハッシュ

JBoss ルールは ハッシュ を使用して、ObjectTypeNode から AlphaNode への伝搬を最適化することで Rete を拡張します。AlphaNode が ObjectTypeNode に追加されるたびに、AlphaNode を値として持つ HashMap にリテラル値をキーとして追加します。新規インスタンスが各 AlphaNode に伝播するのではなく、ObjectType ノードに入ると、代わりに HashMap から正しい AlphaNode を取得できるため、不要なリテラルチェックを回避できます。

3.18. BetaNodes

BetaNodes は、2 つのオブジェクトとそのフィールドを比較するために使用されます。オブジェクトは同じタイプまたは異なるタイプである可能性があります。

3.19. アルファメモリー

アルファメモリー は、BetaNode の正しい入力を指します。JBoss ルールでは、この入力はすべての受信オブジェクトを記憶します。

3.20. ベータ版メモリー

ベータ版メモリー は、BetaNode の左入力を参照するために使用される用語です。すべての着信タプルを記憶します。

3.21. BetaNodes でのルックアップ

ファクトが 1 側から入力すると、有効な候補(インデックス と呼ばれる)を返すハッシュルックアップを実行できます。有効な参加が見つかると、Tuple はオブジェクト(部分的な一致 と呼ばれます)に参加し、次のノードに伝播します。

3.22. LeftInputNodeAdapters

LeftInputNodeAdapter はオブジェクトを入力として取得し、単一の Object Tuple を伝播します。

3.23. ターミナルノード

ターミナルノード は、1 つのルールがすべての条件に一致するかを示すために使用されます(つまり、ルールは完全に一致しています)。または条件和の結合を持つルールは、論理的なブランチごとにサブルールが生成されます。このため、1 つのルールに複数のターミナルノードを持たせることができます。

3.24. ノードの共有

ノードの共有 は、不要な冗長性を防ぐために使用されます。多くのルールは同じパターンを繰り返すため、ノード共有により、ユーザーはこれらのパターンを折りたたむことができるため、1 つのインスタンスごとに再評価する必要はありません。

3.25. ノードの共有の例

以下の 2 つのルールは最初のパターンを共有しますが、最後には共有されません。
rule
when
    vehicle( $car : name == "car" )
    $driver: Driver( typeCar == $sedan )
then
    System.out.println( $driver.getName() + " drives sedan" );
end
rule
when
    Vehicle( $sedan : name == "sedan" )
    $driver : Driver( typeCar != $sedan )
then
    System.out.println( $driver.getName() + " does not drive sedan" );
end

3.26. ルーズカップリング

大まかな結合 には大まかなリンクルールが含まれるため、1 つのルールを実行しても別のルールが実行されなくなります。
通常、より柔軟性が高いため、大まかな結合を示唆する設計が推奨されます。ルールがすべて強力な場合、それらは柔軟ではない可能性があります。さらに大幅に、ルールエンジンのデプロイが状況に過剰であることを示します。

3.27. 強力な結合

強力な結合 は、ルールをリンクする方法です。ルールが厳密に分離されている場合は、1 つのルールを実行すると、別のルールが直接実行されることになります。つまり、ロジックの明確なチェーンがあります。(明確なチェーンは、デシジョンツリーを使用してハードコーディングされたり、実装したりできます。)

3.28. 宣言型プログラミング

宣言型プログラミング は、ルールエンジンがその実行方法ではなく、ユーザーが行うものを宣言できるようにする方法を指します。この時点の主な利点は、ルールを使用すると、困難な問題へのソリューションを簡単に表現し、その結果、それらのソリューションが検証される可能性があることです。ルールの読み取りはコードよりもはるかに簡単です。

3.29. ロジックとデータの分離

ロジックとデータの分離 とは、ロジックとデータコンポーネントを切り離すプロセスを指します。この方法では、ロジックを多くのドメインオブジェクトまたはコントローラーに分散でき、1 つ以上の個別のルールファイルにまとめることができます。

3.30. ナレッジベース

ナレッジベース は、KnowledgeBuilder でコンパイルしたルールのコレクションです。これは、すべてのアプリケーションのナレッジ定義のリポジトリーです。ルール、プロセス、関数、タイプモデルを含めることができます。ナレッジベース自体には、インスタンスデータ(ファクトと呼ばれる)は含まれません。代わりに、セッションは、データを挿入できるナレッジベースから作成され、プロセスインスタンスを開始できる場所です。セッションを繰り返し作成できるように、可能な場合はナレッジベースをキャッシュすることが推奨されます。

第4章 ユーザーガイド

4.1. ステートレスナレッジセッション

ステートレスなナレッジセッション は、推論のないセッションです。ステートレスセッションは、データを渡して結果を受信できるように、関数のように呼び出すことができます。
ステートレスなナレッジセッションは、検証、計算、ルーティング、およびフィルターリングが必要な状況で役に立ちます。

4.2. ステートレスセッションでのルールの設定

手順4.1 タスク

  1. 以下のドライバーのライセンス例のようなデータモデルを作成します。
    public class Applicant {
        private String name;
        private int age;
        private boolean valid;
        // getter and setter methods here
    }
    
  2. 最初のルールを書き込みます。この例では、18 を超える申請者が解消するルールが追加されます。
    package com.company.license
    
    rule "Is of valid age"
    when
        $a : Applicant( age < 18 )
    then
        $a.setValid( false );
    end
    
  3. Applicant オブジェクトがルールエンジンに挿入されると、各ルールの制約がそれを評価し、一致を検索します。(その後にオブジェクトタイプの暗黙的な制約が常にあり、その後に明示的なフィールド制約はいくつでも存在できます。)
    Is of valid age ルールには、以下の 2 つの制約があります。
    • 一致するファクトのタイプは Applicant である必要があります。
    • Age の値は 8 未満である必要があります。
    $a はバインディング変数です。(オブジェクトのプロパティーを更新できるように)ルールの結果で一致したオブジェクトへの参照を可能にするためにあります。
    注記
    ドル記号($)の使用は任意です。これは、変数名とフィールド名を区別するのに役立ちます。
    注記
    ルールがクラスと同じフォルダーにある場合、クラスパスリソースローダーを使用して最初のナレッジベースを構築できます。
  4. KnowledgeBuilder を使用して、以下のようにルールのリストをナレッジベースにコンパイルします。
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    kbuilder.add( ResourceFactory.newClassPathResource( "licenseApplication.drl", 
                  getClass() ), ResourceType.DRL );
    if ( kbuilder.hasErrors() ) {
        System.err.println( kbuilder.getErrors().toString() );
    }
    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
    kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
    
    上記のコードスニペットは、newClassPathResource() メソッドを使用して licenseApplication.drl ファイルのクラスパスを検索します。(リソースタイプは DRL で、Drools Rule Language に短縮されます。)
  5. KnowledgeBuilder でエラーの有無を確認します。セッションがない場合には、セッションをビルドできます。
  6. ルールに対してデータを実行します。(申請者が 8 歳未満であるため、アプリケーションは invalid とマークされます。)
    StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
    Applicant applicant = new Applicant( "Mr John Smith", 16 );
    assertTrue( applicant.isValid() );
    ksession.execute( applicant );
    assertFalse( applicant.isValid() );
    

結果

上記のコードは、ルールに対してデータを実行します。申請者が 18 歳未満であるため、アプリケーションは無効としてマークされます。

4.3. 複数のオブジェクトを使用したルールの設定

手順4.2 タスク

  1. (コレクションなど) 反復可能な オブジェクトにルールを実行するには、以下のコード例のように別のクラスを追加します。
    public class Applicant {
        private String name;
        private int age;
        // getter and setter methods here
    }
    
    public class Application {
        private Date dateApplied;
        private boolean valid;
        // getter and setter methods here
    }
    
  2. アプリケーションが正当な時間枠内で行われたことを確認するには、次のルールを追加します。
    package com.company.license
    
    rule "Is of valid age"
    when
        Applicant( age < 18 )
        $a : Application()     
    then
        $a.setValid( false );
    end
    
    rule "Application was made this year"
    when
        $a : Application( dateApplied > "01-jan-2009" )     
    then
        $a.setValid( false );
    end
    
  3. JDK コンバーターを使用して、反復可能なインターフェイスを実装します。(このメソッドは Arrays.asList (...) の行で始まります) 以下のコードは、反復可能なリストに対してルールを実行します。すべてのコレクション要素は、一致したルールが実行される前に挿入されます。
    StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
    Applicant applicant = new Applicant( "Mr John Smith", 16 );
    Application application = new Application();
    assertTrue( application.isValid() );
    ksession.execute( Arrays.asList( new Object[] { application, applicant } ) );
    assertFalse( application.isValid() );
    
    注記
    execute (Object object) および execute (Iterable objects) メソッドは、実際には BatchExecutor インターフェイスから取得される execute (Command command) という追加メソッドのラッパーです。
  4. CommandFactory を使用して命令を作成し、以下が (反復可能な)実行 と同じになるようにします。
    ksession.execute( CommandFactory.newInsertIterable( new Object[] { application, applicant } ) );
    
  5. 多くの異なるコマンドまたは結果出力識別子を使用する場合は、BatchExecutorCommandFactory を使用します。
    List<Command> cmds = new ArrayList<Command>();
    cmds.add( CommandFactory.newInsert( new Person( "Mr John Smith" ), "mrSmith" );
    cmds.add( CommandFactory.newInsert( new Person( "Mr John Doe" ), "mrDoe" );
    BatchExecutionResults results = ksession.execute( CommandFactory.newBatchExecution( cmds ) );
    assertEquals( new Person( "Mr John Smith" ), results.getValue( "mrSmith" ) );
    
    注記
    CommandFactory は、BatchExecutor で使用できる他の多くのコマンドをサポートしています。これらの一部は、StartProcessQuery、および SetGlobal です。

4.4. ステートフルセッション

ステートフルセッション を使用すると、時間の経過とともにファクトを繰り返し変更できます。StatelessKnowledgeSession と同様に、StatefulKnowledgeSessionBatchExecutor インターフェイスをサポートします。唯一の違いは、FireAllRules コマンドは最後に自動的に呼び出されません。
警告
ステートフルセッションの実行後に dispose () メソッドが呼び出されていることを確認します。これにより、メモリーリークが発生しないようにします。これは、ナレッジベースの作成時に、ステートフルなナレッジセッションへの参照を取得するためです。

4.5. ステートフルセッションの一般的なユースケース

モニタリング
たとえば、株式市場を監視し、購入プロセスを自動化できます。
診断
ステートフルセッションを使用して、障害検出プロセスを実行できます。また、これらの診断をマネージメントプロセスに使用することもできます。
Logistical
たとえば、parcel の追跡および配信プロビジョニングに関連する問題に適用できます。
コンプライアンスの確保
たとえば、市場取引における法的性を検証するには、以下を行います。

4.6. ステートフルセッションの監視例

手順4.3 タスク

  1. モニターするモデルを作成します。たとえば、火災のアラームに関するこの例では、家の部屋がリストされています。それぞれにスプリンクラーが 1 つあります。実行はどの部屋でも開始できます。
    public class Room 
    {
    	private String name
    		// getter and setter methods here
    }
    
    public class Sprinkler 
    {
    	private Room room;
    	private boolean on;
    	// getter and setter methods here
    }
    
    public class Fire 
    {
    	private Room room;
    	// getter and setter methods here
    }
    
    public class Alarm 
    {
    }
    
  2. ルールは、複数のオブジェクト間の関係を表す必要があります(特定の部屋にスプリンクラーの存在などを定義するため)。これを行うには、バインディング変数 をパターンの制約として使用します。これにより、製品がまたがります。
  3. Fire クラスのインスタンスを作成し、セッションに挿入します。
    以下のルールは、Fire オブジェクトの room フィールドにバインディングを追加して、一致を制限します。これにより、その部屋のスプリンクラーのみがチェックされます。このルールが実行され、結果が実行されると、スプリンクラーが有効になります。
    rule "When there is a fire turn on the sprinkler"
    when
        Fire($room : room)
        $sprinkler : Sprinkler( room == $room, on == false )
    then
        modify( $sprinkler ) { setOn( true ) };
        System.out.println("Turn on the sprinkler for room "+$room.getName());
    end
    
    ステートレスセッションは標準の Java 構文を使用してフィールドを変更しましたが、上記のルールは modify ステートメントを使用します。(with ステートメントとよく似ています。)

4.7. 最初の順序ログ

最初の順序ロジック を使用すると、個々のインスタンスの代わりに一連のデータを確認することができます。

4.8. 最初の順序ログを使用したルールの設定

手順4.4 タスク

  1. Not キーワードが含まれるパターンを設定します。最初の順序ロジックにより、他のキーワードが存在しない場合のみルールがマッチします。この例では、実行が拡張されると、ルールはスプリンクラーをオフにします。
    rule "When the fire is gone turn off the sprinkler"
    when
        $room : Room( )
        $sprinkler : Sprinkler( room == $room, on == true )
        not Fire( room == $room )
    then
        modify( $sprinkler ) { setOn( false ) };
        System.out.println("Turn off the sprinkler for room "+$room.getName());
    end
    
  2. Alarm オブジェクトは、実行がある場合に作成されますが、実行回数に関係なく、ビルド全体には Alarm が 1 つだけ必要です。は補完され が存在 するようになりました。カテゴリーの 1 つまたは複数のインスタンスと一致します。
    rule "Raise the alarm when we have one or more fires"
    when
        exists Fire()
    then
        insert( new Alarm() );
        System.out.println( "Raise the alarm" );
    end
    
  3. これ以上発生しない場合は、アラームを非アクティブにする必要があります。これを無効にするには、再度 Not を使用します。
    rule "Cancel the alarm when all the fires have gone"
    when
        not Fire()
        $alarm : Alarm()
    then
        retract( $alarm );
        System.out.println( "Cancel the alarm" );
    end
    
  4. このコードを使用して、アプリケーションの初回起動時に、アラームとスプリンクラーがすべて非アクティブ化されたときにも一般的なヘルスステータスメッセージを出力します。
    rule "Status output when things are ok"
    when
        not Alarm()
        not Sprinkler( on == true ) 
    then
        System.out.println( "Everything is ok" );
    end
    
  5. ルールを fireAlarm.drl という名前のファイルに保存します。このファイルをクラスパス上のサブディレクトリーに保存します。
  6. 最後に、新しい名前 fireAlarm.drl を使用して、ナレッジベース を構築します。
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    kbuilder.add( ResourceFactory.newClassPathResource( "fireAlarm.drl", 
    										getClass() ), ResourceType.DRL );
    										
    if ( kbuilder.hasErrors() )
    	System.err.println( kbuilder.getErrors().toString() );
    
    StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
    

4.9. ルールシステムサンプルの設定

手順4.5 タスク

  1. Insert ksession.fireAllRules().これにより、実行するルールパーミッションが付与されますが、この例では fire がないため、正常性メッセージが生成されます。
    String[] names = new String[]{"kitchen","bedroom","office","livingroom"};
    Map<String,Room> name2room = new HashMap<String,Room>();
    
    for( String name: names )
    {
    	Room room = new Room( name );
    	name2room.put( name, room );
    	ksession.insert( room );
    	Sprinkler sprinkler = new Sprinkler( room );
    	ksession.insert( sprinkler );
    }
    
    ksession.fireAllRules();
    
    生成されるメッセージは以下を読み取ります。
    > Everything is okay
    
  2. 2 つの実行を作成して挿入します。(ファクトハンドルは保持されます。)
  3. エンジンで fireAllRules ()を呼び出すと、fireAllRules () を呼び出します。アラームが発生し、それぞれのスプリンクラーが有効になります。
    Fire kitchenFire = new Fire( name2room.get( "kitchen" ) );
    Fire officeFire = new Fire( name2room.get( "office" ) );
    
    FactHandle kitchenFireHandle = ksession.insert( kitchenFire );
    FactHandle officeFireHandle = ksession.insert( officeFire );
    
    ksession.fireAllRules();
    
    生成されるメッセージは以下を読み取ります。
    > Raise the alarm
    > Turn on the sprinkler for room kitchen
    > Turn on the sprinkler for room office
    
  4. 実行が拡張されると、fire オブジェクトは取り消され、スプリンクラーはオフになります。この時点で、アラームはキャンセルされ、ヘルスメッセージが複数回表示されます。
    ksession.retract( kitchenFireHandle );
    ksession.retract( officeFireHandle );
    
    ksession.fireAllRules();
    
    生成されるメッセージは以下を読み取ります。
    > Turn off the sprinkler for room office
    > Turn off the sprinkler for room kitchen
    > Cancel the alarm
    > Everything is okay       
    
    

4.10. JBoss ルールのメソッド

メソッド はルールによって異なります。これらは直接呼び出され、特定のインスタンスを渡すために使用されます。1 回の呼び出しを行うと、1 回の実行になります。

4.11. メソッドの例

メソッドは以下のようになります。
public void helloWorld(Person person) {
    if ( person.getName().equals( "Chuck" ) ) {
        System.out.println( "Hello Chuck" );
    }
}

4.12. ルールの例

ルールは以下のようになります。
rule "Hello World"
    when
        Person( name == "Chuck" )
    then
        System.out.println( "Hello Chuck" );
end

4.13. クロス製品

複数のデータセットが組み合わされると、結果は cross-product と呼ばれます。

4.14. クロス製品コンセント

手順4.6 タスク

  • ルールが大量のクロス製品を出力しないようにするには、複数の製品自体を制限する必要があります。これは、以下に示す変数制約を使用して行います。
    rule
    when
        $room : Room()
        $sprinkler : Sprinkler( room == $room )
    then
        System.out.println( "room:" + $room.getName() +
                            " sprinkler:" + $sprinkler.getRoom().getName() );
    end
    
    以下の出力が表示されます。
    room:office sprinkler:office
    room:kitchen sprinkler:kitchen
    room:livingroom sprinkler:livingroom
    room:bedroom sprinkler:bedroom

結果

各部屋に正しいスリンクラーを持つ 4 つの行のみが出力されます。この変数がないと、Room テーブル内のすべての行がスプリンクラーテーブルの各行に参加し、大量の出力になります。

4.15. 推論エンジン

推論エンジン は、実稼働ファクトとデータをルールにマッチする JBoss Rules エンジンの一部です。その後、情報からの推測に基づいてアクションを実行します。実稼働環境のルールシステムの推論エンジンは ステートフル であり、真理維持 を行います。

4.16. 推論の例

この例では、age フィールドのある Person ファクトと、年齢ポリシー制御を提供するルールが使用されます。推論は、Person が大人かマイナーかを決定するために使用され、その結果に対応します。
rule "Infer Adult"
when
  $p : Person( age >= 18 )
then
  insert( new IsAdult( $p ) )
end
上記のスニペットでは、18 以上のすべての Person には IsAdult のインスタンスを挿入します。このファクトは関係として知られているという点で特殊です。この推論関係は、どのルールでも使用することができます。
$p : Person()
IsAdult( person == $p )

4.17. 論理アサーション

標準のオブジェクト挿入の後に、ファクトを明示的に取り消す必要があります。論理 アサーションでは、アサートされたファクトは、最初の場所でアサートされた条件が true でなくなると自動的に取り消されます。論理アサーションをサポートする単一の条件がない場合にのみ取り消されます。

4.18. 記述挿入

通常の挿入は、ファクトを示す のように、記述されます。HashMap およびカウンターを使用すると、特定の等価性が 記載される 回数を追跡できます。つまり、等しい異なるインスタンスをカウントできます。

4.19. 正当化挿入

オブジェクトが論理的に挿入されると、は 正当 化された と言われます。これは、起動ルールによって正当化されていると見なされます。論理挿入ごとに、等しいオブジェクトは 1 つだけあり、後続の等しい論理挿入はそれぞれ、この論理アサーションの正当化カウンターを増やします。ルールの LHS が true になり、それに応じてカウンターが減少しると、正当化は削除されます。正当化がなくなったら、論理オブジェクトは自動的に取り消されます。

4.20. WM_BEHAVIOR_PRESERVE 設定

等しいオブジェクトがある場合にオブジェクトを 論理 的に挿入しようとすると、失敗し、null を返します。正当 れた既存のオブジェクトを持つ オブジェクトを記述する場合は、Fact を上書きします。このオーバーライドの仕組みは、設定設定 WM_BEHAVIOR_PRESERVE によって異なります。プロパティーが discard に設定されている場合は、既存のハンドルを使用して、既存のインスタンスを新しいオブジェクト(デフォルトの動作)に置き換えることができます。それ以外の場合は、指定 に合わせて上書きする必要がありますが、FactHandle を新たに作成します。

4.21. 記述挿入フローチャート

4.22. 論理挿入フローチャート

4.23. The Truth Maintenance System

JBoss Rules Truth Maintenance System (TMS)は、ナレッジベースの信頼性と関連する依存関係/調整を表す方法です。
重要
Truth Maintenance (および論理アサーション)が機能するには、Fact オブジェクトは equals メソッドおよび hashCode メソッドを正しく上書きする必要があります。真理維持システムは、Java 標準に従って、2 つの異なる物理オブジェクトが同じであるかどうかを知る必要があるため、equals と hashCode の 両方 を正しく上書きする必要があります。
2 つのオブジェクトは、equals メソッドが互いに true を返し、hashCode メソッドが同じ値を返す場合にのみ等しくなります。equals と hashCode の両方を上書きする必要があります。

4.24. insertLogical ファクト

insertLogical ファクトは JBoss Rules TMS の一部です。ルールが動作し、状況に応じて変更されるように、ロジックを挿入します。たとえば、insertLogical ファクトを一連のルールに追加して、ルールが false になると、ファクトは自動的に取り消されます。

4.25. 推論および TMS の使用

手順4.7 タスク

  1. この例では、バスパススルー発行システムを使用します。以下のコードスニペットを参照してください。
    rule "Issue Child Bus Pass" when
      $p : Person( age < 16 )
    then
      insert(new ChildBusPass( $p ) );
    end
     
    rule "Issue Adult Bus Pass" when
      $p : Person( age >= 16 )
    then
      insert(new AdultBusPass( $p ) );
    end
  2. insertLogical プロパティーを挿入して推論を提供します。
    rule "Infer Child" when
        $p : Person( age < 16 )
    then
        insertLogical( new IsChild( $p ) )
    end
    rule "Infer Adult" when
        $p : Person( age >= 16 )
    then
        insertLogical( new IsAdult( $p ) )
    end
  3. コードを再入力してパスを発行します。TMS は取り消しセットのカスケードに対する論理挿入の連鎖をサポートするため、これらの 2 つの設定は論理的に挿入することもできます。
    rule "Issue Child Bus Pass" when
      $p : Person( )
           IsChild( person == $p )
    then
        insertLogical(new ChildBusPass( $p ) );
    end
     
    rule "Issue Adult Bus Pass" when
      $p : Person( age >= 16 )
           IsAdult( person =$p )
    then
        insertLogical(new AdultBusPass( $p ) );
    end
    ユーザーが 15 から 16 に変更されると、IsChild ファクトとユーザーの ChildBusPass ファクトの両方が自動的に取り消されるようになりました。
  4. 必要に応じて、not 条件要素を挿入して通知を処理します。(この例では、パスを返すリクエストです。) TMS が ChildBusPass オブジェクトを自動的に取り消すと、このルールはトリガーして要求を人に送信します。
    rule "Return ChildBusPass Request "when
      $p : Person( )
           not( ChildBusPass( person == $p ) )
    then
        requestChildBusPass( $p );
    end

第5章 デシジョンテーブル

5.1. デシジョンテーブル

デシジョンテーブル は、条件付きロジックを表す手段です。これらは、ビジネス レベルのルールに適しています。

5.2. スプレッドシートのデシジョンテーブル

JBoss ルールは、スプレッドシート形式のルールの管理をサポートします。サポートされる形式は、JBDS (XLS)および CSV です。つまり、さまざまなスプレッドシートプログラム(Microsoft Excel、OpenOffice.org Calc など)を使用できます。

5.3. オープンオフィスの例

図5.1 Open Office Screenshot

Open Office Screenshot
上記の例では、(標準のスプレッドシート機能を使用して)デシジョンテーブルの技術的な側面が折りたたまれています。
ルールは行 17 で始まり、各行がルールになります。条件は C、D、E などにあり、アクションはオフスクリーンです。値の意味は、Row 16 のヘッダーによって示されます。(列 B は説明にすぎません。)
注記
デシジョンテーブルはトップダウンの処理と同様ですが、必ずしもそうとは限りません。理想的には、ルールは行の順序に関係なく作成されます。これにより、常に行を移動する必要がないため、メンテナーンスが容易になります。

5.4. ルールおよびスプレッドシート

行に挿入されるルール
各行がルールであるため、書き込まれたコードと同じ原則が適用されます。ルールエンジンがファクトを処理すると、一致するすべてのルールが実行される可能性があります。
Agendas
ルールが実行され、最初の一致だけがアクションに影響する、非常に単純なデシジョンテーブルをシミュレートする際にアジェンダをクリアできます。
複数テーブル
1 つのスプレッドシートに複数のテーブルを設定できます。これにより、ルールは共通のテンプレートを共有する場所をグループ化できますが、すべてが 1 つのルールパッケージに統合されます。

5.5. RuleTable キーワード

デシジョンテーブルを使用する場合、スプレッドシートは RuleTable キーワードを検索して、ルールテーブルの開始を示します(開始行と列の両方)。
重要
キーワードはすべて同じ列である必要があります。

5.6. RuleSet キーワード

RuleSet キーワードは、すべてのルールを含む ルールパッケージ で使用される名前を示します。この名前はオプションで あり、デフォルトですが、そのセルにはすぐに右側に RuleSet キーワードが必要です。

5.7. ルールテンプレートの例

図5.2 ルールテンプレート

ルールテンプレート
  • RuleSet キーワードは、すべてのルールを含む ルールパッケージ で使用される名前を示します。この名前はオプションで あり、デフォルトですが、そのセルにはすぐに右側に RuleSet キーワードが必要です。Column C に表示される他のキーワードは Import および Sequential です。
  • RuleTable キーワードの後に名前があり、生成されたルールの名前の接頭辞を付けるために使用されます。一意のルール名を保証するために、行番号が追加されます。
  • RuleTable の列は、ルールが開始する列で、左側の列は無視されます。
  • 行 14 (RuleTable の直後の行)を参照すると、以下の列のデータが、ルールの LHS または RHS 部分のいずれかに対するものであることを示します。ルールには他の属性があり、オプションでこの方法を設定することもできます。
  • 行 15 には ObjectTypes の宣言が含まれます。この行の内容は任意ですが、このオプションが使用されていない場合は、行を空白のままにする必要があります。この行を使用すると、下のセル(行 16)の値がそのオブジェクトタイプの制約になります。上記の例では、Person(age=="42") および Cheese(type=="stilton") が生成されます。42 と "stilton" は行 18 から取得されます。上記の例では、==" は暗黙です。フィールド名のみを指定すると、トランスレーターは完全一致を生成することを前提としています。
  • 行 16 には、ルールテンプレート自体が含まれます。"$param" プレースホルダーを使用して、下のセルからのデータを補間する場所を指定できます。(複数の挿入の場合は "$1", "$2" などを使用し、下のセルのコンマ区切りリストのパラメーターを示します。) 行 17 は無視されます。列の目的に関するテキスト説明が含まれる可能性があります。
  • 行 18 および 19 は、ルールを生成するために、行 15 のテンプレートと組み合わせ(補間)されるデータを表示します。セルにデータが含まれていない場合には、そのテンプレートは無視されます。(これは、一部の条件またはアクションがそのルール行に適用されないことを意味します。) ルール行は、空の行が存在するまで読み取られます。シートに RuleTables を複数存在させることができます。
  • 行 20 には別のキーワードと値が含まれます。このようなキーワードの行の位置は重要ではありません(ほとんどの場合、最上部に配置します)。ただし、その列は、RuleTable キーワードまたは RuleSet キーワードが表示されるものと同じである必要があります。ケース列 C は重要になるように選択されていますが、代わりに他の列を使用できます。
注記
ObjectType 宣言は、(結合したセルを介して)列にまたがることができます。つまり、マージされた範囲の下にあるすべての列は、同じ ObjectType を含むマージされていないセルではなく、1 つのパターン内の 1 つの制約セットに統合されるため、異なるファクトまたは同一のファクトに一致する可能性があります。
上記の例では、(ObjectType 行を使用するので)ルールが以下のようにレンダリングされます。
//row 18
rule "Cheese_fans_18"
when
    Person(age=="42")
    Cheese(type=="stilton")
then
    list.add("Old man stilton");
end
注記
上記のセルのそれぞれの ObjectType に追加される制約 age=="42" および type=="stilton" は単一の制約として解釈されます。上記のセルがスパンされた場合は、1 つの列に複数の制約がある可能性があります。
警告
非常に大きなデシジョンテーブルには、非常に大きなメモリー要件がある場合があります。

5.8. データ定義セル

DRL ファイルの生成に使用される データの定義 には、長方形領域が 2 種類あります。1 つ(セルラベルの付いた RuleSet )は、ルールを除くすべての DRL アイテムを定義します。もう 1 つは繰り返し発生する可能性があり、右側と、コンテンツが RuleTable で始まるセルの下に発生します。これらのエリアは実際のデシジョンテーブルを表し、各領域により同様の構造のルールセットが作成されます。
ルールセットエリアには、セルのペアを含めることができます。1 つ下のセルセルと、同じ行に続く他の値に含まれる値を指定するキーワードが含まれます。RuleSet

5.9. ルールテーブル列

Rule Table 領域の列は、ルールから派生するルールの左側、ルールの結果のアクション、および個々のルール属性の値に対するパターンと制約を定義します。Rule Table エリアには、1 つ以上の列(条件とアクションの両方)と、ルール属性の任意の列選択(それぞれ最大 1 列)が含まれている必要があります。RuleTable のマークが付いたセルの行に続く最初の 4 つの行は、ヘッダー領域として表示されます。主に、コードの定義にルールを設定するために使用されます。別のルールを生成する 4 つのヘッダー行の下に追加行があり、そのデータは Rule Table ヘッダーで定義されたコードのバリエーションを提供します。
注記
すべてのキーワードは大文字と小文字を区別しません。
デシジョンテーブルについては、最初のワークシートのみが検証されます。

5.10. ルールセットエントリー

Rule Set 領域のエントリーは、DRL コンストラクト(ルールを除く)を定義し、ルール属性を指定できます。コンストラクトのエントリーは繰り返し使用できますが、各ルール属性は最大 1 回指定でき、Rule Table 領域内で定義されている属性でオーバーライドされない限り、すべてのルールに適用されます。
エントリーは、垂直に積み重ねたセルペアで指定する必要があります。最初のキーワードにはキーワードと、その値の右側にあるキーワードが含まれます。このセルペアのシーケンスは、キーワードを含む列が upheld である限り、空白行または Rule Table で中断する可能性があります。RuleSet

5.11. ルールセットエリアのエントリー

表5.1 Rule Set エリアのエントリー

キーワード 使用方法
RuleSet 生成した DRL ファイルのパッケージ名。任意設定、デフォルトは rule_table です。 First エントリー でなければなりません。
Sequential true または false。true の場合、ルールを上から適用する優先順位が使用されます。 任意。1 つまで指定可能。1 つまで指定可能。省略すると、適用順は指定されません。
EscapeQuotes true または false。true の場合は、引用符がエスケープされ、DRL に文字通り表示されます。 任意。1 つまで指定可能。1 回まで指定可能。省略すると、引用符がエスケープされます。
インポート インポートする Java クラスのコンマ区切りリスト。 任意。繰り返して使用可能。
Variables DRL グローバルの宣言。つまり、型の後に変数名が続きます。グローバル定義が複数になる場合は、コンマで区切る必要があります。 任意。繰り返して使用可能。
関数 DRL 構文に準拠している 1 つまたは複数の関数定義。 任意。繰り返して使用可能。
Queries DRL 構文に準拠している 1 つまたは複数のクエリー定義。 任意。繰り返して使用可能。
Declare DRL 構文に準拠している 1 つまたは複数の宣言型。 任意。繰り返して使用可能。

5.12. ルールセットエリアのルール属性エントリー

表5.2 ルールセットエリアのルール属性エントリー

キーワード 初期
PRIORITY P ルールのsalience 値を定義する整数。Sequential フラグで上書きされます。
DURATION D ルールの "duration" 値を定義する長い整数値。
TIMER T タイマー定義。タイマーおよびカレンダーを参照してください。
CALENDARS E カレンダーの定義。タイマーおよびカレンダーを参照してください。
NO-LOOP U ブール値。"true" は、結果による変更によりルールのループを抑制します。
LOCK-ON-ACTIVE L ブール値。"true" は、同じルールフローまたはアジェンダグループ内にこのフラグが設定されたすべてのルールの追加アクティベーションを禁止します。
AUTO-FOCUS F ブール値。アジェンダグループ内のルールに対して "true" を指定すると、ルールのアクティベーションが自動的にグループにフォーカスされます。
ACTIVATION-GROUP X アクティベーション(または XOR)グループを指定する文字列。アクティベーショングループ内のルールが 1 つだけ実行されます。つまり、実行する最初のルールは、同じグループ内の他のルールの既存のアクティベーションを取り消します。
AGENDA-GROUP G アジェンダグループを指定する文字列。"focus" を指定してアクティブにする必要があります。これは、ルールのグループ間のフローを制御する 1 つの方法です。
RULEFLOW-GROUP R ルールフローグループを指定する文字列。

5.13. RuleTable セル

すべてのルールテーブルは、RuleTable を含むセルで始まり、任意で同じセル内の文字列が続きます。この文字列は、区別のために追加したこの Rule Table から派生するすべてのルールの名前の最初の部分として使用されます。(この自動命名は、NAME 列を使用して上書きできます。) このルールテーブルのルールを定義する他のすべてのセルは、以下と、このセルの右側にあるものです。

5.14. 列タイプ

RuleTable セルの後の次の行は、列タイプを定義します。各列は条件または結果の一部になるか、ルール属性、ルール名、またはコメントを提供します。各属性列は、最大 1 回使用できます。

5.15. ルールテーブルの列ヘッダー

表5.3 ルールテーブルの列ヘッダー

キーワード 初期 使用方法
NAME N その行で生成したルールの名前を提供します。デフォルトは、RuleTable タグと行番号に続くテキストから作成されます。 最大 1 列
説明 I 生成されたルール内のコメントとなるテキスト。 最大 1 列
CONDITION C 条件内のパターンに制約を構築するコードスニペットおよび補間値。 ルールテーブルごとに少なくとも 1 つ
ACTION A ルールの結果に対するアクションを構築するコードスニペットおよび補間値。 ルールテーブルごとに少なくとも 1 つ
METADATA @ ルールに対するメタデータエントリーを構築するコードスニペットおよび補間値。 任意。任意の数の列

5.16. 条件要素

CONDITION ヘッダーの列を指定すると、連続した行のセルが条件要素になります。
  • CONDITION のすぐ下のセルのテキストは、ルール条件のパターンに開発され、次の行のスニペットは制約になります。セルが 1 つ以上のニービーと結合されている場合は、複数の制約を持つパターンが 1 つ形成されます。すべての制約は括弧で結合され、このセルのテキストに追加されます。セルは空白のままにすることができます。つまり、次の行のコードスニペットが自動的に有効な条件要素となります。
    制約のないパターンを含めるには、別のパターンのテキストの前にパターンを記述します。
    パターンは、括弧の空のペアで記述することも、なしで記述することもできます。パターンに from 句を追加できます。
    パターンが eval で終了すると、コードスニペットは、eval の後に括弧のペアに含めるブール値式を生成することになります。
  • CONDITION の 2 つ下のセルのテキストは、2 つの手順で処理されます。
    1. このセルのコードスニペットは、その列のさらに下にあるセルから値が補間されます。以下のセルからの値で "==" を使用した比較で設定される制約を作成する場合は、フィールドセレクターだけで十分です。その他の比較演算子は、スニペットの最後に指定する必要があり、値は下のセルから追加されます。その他の制約形式については、$param の記号でセルの内容を追加する場所を指定する必要があります。シンボル $1$2 などを使用し、下のセルの値のコンマ区切りリストを使用すると、複数の挿入が可能です。
      パターン forall(区切り文字){スニペット} に従ってテキストが展開されます。これは、以下の各セルで、コンマ区切りの値リストの値ごとに スニ ペットを 1 回繰り返し実行し、シンボル $ の代わりに値を挿入し、これらの拡張を指定の 区切り文字 で結合します。forall コンストラクトは他のテキストで囲むことができることに注意してください。
    2. 上記の行のセルが空でない場合は、そのセルから条件要素に完全なコードスニペットが追加されます。括弧のペアと、(結合したセルのパターンに複数の制約が追加されている場合は) 区切り文字のコンマが自動的に提供されます。
      上記のセルが空の場合は、挿入の結果がそのまま使用されます。
  • CONDITION の 3 つ下のセルのテキストは、ドキュメントのみ用です。列の目的を人間のリーダーに示すために使用する必要があります。
  • 4 行目から、空白以外のエントリーは、上記のように挿入用のデータを提供します。セルが空の場合は、このルールで条件付き要素または制約が省略されます。

5.17. アクションステートメント

ACTION ヘッダーの列を指定すると、連続した行のセルは action ステートメントになります。
  • ACTION の下の最初のセルのテキストは任意です。存在する場合は、オブジェクト参照として解釈されます。
  • ACTION の 2 つ下のセルのテキストは、2 つの手順で処理されます。
    1. このセルのコードスニペットは、その列のさらに下にあるセルから値が補間されます。挿入(singular insert)の場合は、$param の記号でセルの内容を追加する場所をマークします。シンボル $1$2 などを使用し、下のセルの値のコンマ区切りリストを使用すると、複数の挿入が可能です。
      挿入のないメソッド呼び出しは、マーカーシンボルのないテキストで実現できます。この場合は、以下の行に空白以外のエントリーを使用して ステートメントを追加します。
      forall コンストラクトもここで利用できます。
    2. 1 つ下のセルが空でない場合は、そのテキストの後にピリオドが続くと、2 つ下のセルのテキストと終了セミコロンが 1 つの文字列で囲まれ、その結果のアクションステートメントとして追加されるメソッド呼び出しが作成されます。
      上記のセルが空の場合は、挿入の結果がそのまま使用されます。
  • ACTION の 3 つ下のセルのテキストは、ドキュメントのみ用です。列の目的を人間のリーダーに示すために使用する必要があります。
  • 4 行目から、空白以外のエントリーは、上記のように挿入用のデータを提供します。セルが空の場合は、このルールの action ステートメントが省略されます。
注記
代替テキストにコンマが含まれている場合、$param の代わりに $1 を使用すると失敗します。

5.18. メタデータステートメント

連続した行のセルが METADATA の列を指定すると、生成されたルールのメタデータアノテーションが生成されます。
  • METADATA の下の最初のセルのテキストは無視されます。
  • 上記のように METADATA の 2 つ下のセルのテキストは、ルール行のセルの値を使用して補間されます。メタデータマーカー文字 @ は自動的に接頭辞として付加されるため、このセルのテキストに含めないでください。
  • METADATA の 3 つ下のセルのテキストは、ドキュメントのみを目的としています。列の目的を人間のリーダーに示すために使用する必要があります。
  • 4 行目から、空白以外のエントリーは、上記のように挿入用のデータを提供します。セルが空の場合は、このルールでメタデータアノテーションが省略されます。

5.19. セルデータの交差例

  • テンプレートが Foo(bar == $param) で、セルが 42 の場合、結果は Foo(bar == 42) になります。
  • テンプレートが Foo(bar < $1, baz == $2) で、セルに 42,43 が含まれている場合、結果は Foo(bar < 42, baz ==43) になります。
  • 42,43 が含まれるセルを含むテンプレート forall(&&){bar != $} は、bar != 42 && bar != 43 になります。

5.20. セルでの作業に関するヒント

  • 同じセル内に複数のパッケージ名をコンマで区切る必要があります。
  • タイプ名と変数名のペアはコンマで区切る必要があります。
  • 関数は、DRL ファイルに表示されるように記述する必要があります。これは、RuleSet キーワードと同じ列に表示されるはずです。これは、すべてのルール行間または下に置くことができます。
  • 複数の定義を 1 つのセルにパックする代わりに、Import、Variables、functions、および queryueries を繰り返し使用できます。
  • 末尾の挿入マーカーは省略できます。
  • バインディング変数の定義を指定できます。
  • オブジェクトタイプの行には、すべて配置できます。バインディング変数の定義以外に、リテラル的に挿入される追加のパターンを使用することもできます。
  • ACTION ヘッダーの下のセルは空白のままにすることができます。このスタイルを使用すると、単一のメソッド呼び出しだけでなく、すべてのものを結果に配置できます。(同じ手法は CONDITION 列内で適用されます。)

5.21. SpreadsheetCompiler クラス

SpreadsheetCompiler クラスは、drools-decisiontables モジュールの API スプレッドシートベースのデシジョンテーブルで使用されるメインクラスです。このクラスは、さまざまな形式のスプレッドシートを取得し、DRL にルールを生成します。
SpreadsheetCompiler を使用すると、部分的なルールファイルを生成し、ファクトの後に完全なルールパッケージにアセンブルできます。これにより、必要に応じてルールの技術と技術以外の側面の分離が可能になります。

5.22. スプレッドシートベースのデシジョンテーブルの使用

手順5.1 タスク

  1. ベースとして使用するサンプルスプレッドシートを生成します。
  2. Rule Workbench IDE プラグインが使用されている場合は、ウィザードを使用してテンプレートからスプレッドシートを生成します。
  3. XSL 互換のスプレッドシートエディターを使用して XSL を変更します。

5.23. リスト

xPaaS では、値の lists を作成できます。これらは他のワークシートに保存し、セルの有効な値のリストを提供できます。

5.24. リビジョン制御

時間の経過とともにルールに変更が加えられると、古いバージョンがアーカイブされます。JBoss ルールの一部のアプリケーションでは、変更の履歴を限定的に保持できますが、リビジョン制御の代替手段を使用することが推奨されます。

5.25. tabular Data Sources

テーブル形式のデータソースは、ルールデータのソースとして使用できます。テンプレートを設定して、多くのルールを生成できます。これにより、より柔軟なスプレッドシートの両方が許可されますが、たとえば既存のデータベースのルールも可能になります(テンプレートを事前に開発してルールを生成します)。

5.26. ルールテンプレートの機能

Rule Templates では、データはルールから分離され、ルールのどの部分がデータ駆動型であるかに制限はありません。したがって、デシジョンテーブルで実行できることはすべてできますが、以下を実行することもできます。
  • データをデータベース(またはその他の形式)に保存します。
  • データの値に基づいて条件付きでルールを生成します。
  • ルールの一部(条件演算子、クラス名、プロパティー名など)にデータを使用します。
  • 同じデータで異なるテンプレートを実行します。

5.27. ルールテンプレートの例

以下は、ルールテンプレートの例です。
1  template header
2  age
3  type
4  log
5
6  package org.drools.examples.templates;
7
8  global java.util.List list;
9
10 template "employees"
11
12 rule "Current employee_@{row.rowNumber}"
13 when
14    Name(location == @{location})
15    role(type == "@{type}")
16 then
17    list.add("@{log}");
18 end
19
20 end template
上記の例は、以下で設定されています。
  • 行 1: すべてのルールテンプレートは template header で始まります。
  • 2 行目:ヘッダーのフォローは、データに表示される順序で列のリストになります。ここでは、最初の列 location、2 番目の type と 3 番目の log を呼び出します。
  • 5 行目:空の行は、列定義の最後を示します。
  • 6-9 行目:標準のルールヘッダーテキスト。これは標準的なルール DRL で、生成された DRL の上部に表示されます。package ステートメントと、インポート、グローバル、および関数の定義をこのセクションに配置します。
  • 10 行目:キーワード template は、ルールテンプレートの開始を通知します。テンプレートファイルには複数のテンプレートが存在する可能性がありますが、各テンプレートには一意の名前を付ける必要があります。
  • 11-18 行目:ルールテンプレート。
  • 20 行目:キーワード end template は、テンプレートの最後を示します。
このサンプルテンプレートは、以下のルールを生成します。
package org.drools.examples.templates;

global java.util.List list;

rule "Current employee_1"
when
  Person(location == Melbourne)
  role(type == "receptionist")
then
  list.add("melbourne admin");
end

rule "Current employee_2"
when
  Person(location == Sydney)
  Cheese(type == "recruiter")
then
  list.add("sydney HR");
end

5.28. ルールテンプレートの実行

手順5.2 タスク

  • このコードを実行して、ルールテンプレートを実行します。
    DecisionTableConfiguration dtableconfiguration =
        KnowledgeBuilderFactory.newDecisionTableConfiguration();
    dtableconfiguration.setInputType( DecisionTableInputType.XLS );
    
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    
    kbuilder.add( ResourceFactory.newClassPathResource( getSpreadsheetName(),
                                                        getClass() ),
                  ResourceType.DTABLE,
                  dtableconfiguration );
    

5.29. 拡張された変更セットの例

以下の例は、http URL の場所からルールをロードするように展開されており、クラスパスから decision table (http URL)デシジョンテーブルを読み込むように展開されています。
<change-set xmlns='http://drools.org/drools-5.0/change-set'
             xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
             xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >
   <add>
       <resource source='http:org/domain/myrules.drl' type='DRL' />
       <resource source='classpath:data/IntegrationExampleTest.xls' type="DTABLE">
           <decisiontable-conf input-type="XLS" worksheet-name="Tables_2" />
       </resource>
   </add>
 </change-set>

5.30. Changesets および directories の例

コンテンツを配置するディレクトリーを指定できます。タイプがまだファイル名の拡張子から推測されていないため、すべてのファイルが指定されたタイプであることが期待されます。
<change-set xmlns='http://drools.org/drools-5.0/change-set'
             xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
             xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >
   <add>
       <resource source='file://myfolder/' type='DRL' />
   </add>
 </change-set>

5.31. ナレッジエージェント

ナレッジ ストアはリソースの自動読み込み、キャッシュ、およびリロードを提供し、ナレッジベースプロパティーファイルから設定されます。ナレッジストアは、使用するリソースが変更されるため、ナレッジベースを更新または再構築できます。このストラテジーはファクトリーに指定された設定によって決定されますが、通常は通常のポーリングを使用してプルベースになります。

5.32. ナレッジエージェントの例

以下は、ナレッジエージェント の例です。
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "MyAgent" );
kagent.applyChangeSet( ResourceFactory.newUrlResource( url ) );
KnowledgeBase kbase = kagent.getKnowledgeBase();

5.33. KnowledgeAgent オブジェクト

KnowledgeAgent オブジェクトは、デフォルトのポーリング間隔 60 秒を使用して、すべてのリソースを継続的にスキャンします。変更日を更新すると、新しいリソースを使用してキャッシュされたナレッジベースに変更を適用します。以前の KnowledgeBase 参照は引き続き存在し、新たにビルドされた KnowledgeBase にアクセスするには getKnowledgeBase() を呼び出す必要があります。ディレクトリーが変更セットの一部として指定されている場合、そのディレクトリーの内容全体が変更に対してスキャンされます。変更の適用方法は、エージェントに渡される KnowledgeAgentConfiguration オブジェクトに存在する drools.agent.newInstance プロパティーによって異なります。

5.34. ポーリングサービスの起動

手順5.3 タスク

  • ポーリングを実行するには、ポーリングおよび通知機能サービスを開始する必要があります。このコードを使用します。
    ResourceFactory.getResourceChangeNotifierService().start();
    ResourceFactory.getResourceChangeScannerService().start();

5.35. KnowledgeBuilder のカスタム ClassLoaders

手順5.4 タスク

  1. KnowledgeBuilderConfiguration を開き、カスタムクラ出力ダーを指定します。
  2. カスタム設定をこれらのコンパイラーに渡す必要がある場合は、KnowledgeBuilderConfiguration オブジェクトを KnowledgeAgentFactory.newKnowledgeAgent ()に送信します。

5.36. ナレッジベース ClassLoader の再利用

多くの場合、リモートリソースのコンパイルプロセスで使用されるクラ出力ダーは、ルールを実行できるようにエージェントの kbase に必要なものと同じです。
このアプローチを使用する場合は、必要な ClassLoader をエージェント kbase に設定し、KnowledgeAgentConfiguration オブジェクトの drools.agent.useKBaseClassLoaderForCompiling プロパティーを使用する必要があります。
この方法では、エージェントの kbase が使用するクラ出力ダーを変更することにより、エージェントの kbuilder クラ出力ダーをランタイムで変更できます。これは、増分変更セット処理を使用していない場合にも機能します。kbase が再作成されるとその設定が再利用されるため、クラ出力ダーは維持されます。

5.37. KnowledgeAgentConfiguration の例

以下は、KnowledgeAgentConfiguration プロパティーの例になります。
KnowledgeBaseConfiguration kbaseConfig =
    KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, customClassLoader);
KnowledgeBase kbase =
    KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig); //kbase with custom classloader
KnowledgeAgentConfiguration aconf =
    KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
aconf.setProperty("drools.agent.newInstance", "false"); //incremental change set processing enabled
aconf.setProperty("drools.agent.useKBaseClassLoaderForCompiling", "true");
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent(
                "test agent", kbase, aconf);

5.38. newInstance プロパティー

newInstance プロパティーは、変更セットの処理に役立ちます。
ナレッジストアは、新しい変更セットが処理されるたびにナレッジベースを再作成するか、キャッシュしたナレッジベースに変更を破棄せずに適用するという、2 つの異なる方法で変更を処理できます。この動作は、Agent のコンストラクターに渡される際に KnowledgeAgentConfiguration オブジェクトの newInstance プロパティーによって制御されます。

5.39. newInstance プロパティーの使用

newInstance が true (デフォルト値)に設定されている場合、エージェントはそれに含まれるキャッシュされたナレッジベースを破棄し、変更セットの変更を含む新しいものを追加します。newInstance を false に設定すると、変更セットがキャッシュされたナレッジベースに直接適用されます。変更セットのリソースで変更されなかったルールは、ナレッジベースでは置き換えられず、修正または削除されたルールは、キャッシュされたナレッジベースから変更または削除されます。関数、クエリー、および定義タイプは、変更されているかどうかにかかわらず、キャッシュされたナレッジベースで常に置き換えられます。

5.40. newInstance の例

以下のコードスニペットは、newInstance プロパティーを false に設定して、新しい Knowledge Agent を作成します。
KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
aconf.setProperty("drools.agent.newInstance", "false");
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent("test agent", null, aconf);

5.41. リモート HTTP リソースキャッシング

ナレッジエージェントは、http (s) URL からリモートでリソースをプルします。

5.42. 再起動後のリソースキャッシングの復元

手順5.5 タスク

  • リソースがリモートで利用できない場合に再起動後も存続するには(リモートサーバーの再起動など)、システムプロパティー( drools.resource.urlcache )を設定します。(アプリケーションの書き込みパーミッションを持つディレクトリーに設定されていることを確認します。) ナレッジストアは、そのディレクトリー内のリモートリソースのコピーをキャッシュします。
    たとえば、java コマンドライン -Ddrools.resource.urlcache=/users/someone/KnowledgeCache を使用すると、エージェントが再起動する際に使用するリソース(ルール、パッケージなど)のローカルコピーをそのディレクトリーに保持します。(リモートリソースが利用可能になり、更新されると、ローカルキャッシュコピーが自動的に更新されます。)

第6章 処理

6.1. アジェンダ

Agenda は Rete 機能です。WorkingMemory のアクション中にルールが完全に一致し、実行の対象となる場合があります。単一の作業メモリーアクションにより、複数のルールが適用される場合があります。ルールが完全に一致すると、アクティベーションが作成され、ルールと一致したファクトを参照し、Agenda に配置されます。アジェンダは、競合解決ストラテジーを使用して、これらのアクティベーションの実行順序を制御します。

6.2. アジェンダ処理

エンジンは、以下の 2 つのフェーズを繰り返し循環します。
  1. ワーキングメモリーアクション。これは、Consequence (RHS 自体)またはメインの Java アプリケーションプロセスのいずれかで、ほとんどの作業が行われる場所です。Consequence が終了するか、またはメインの Java アプリケーションプロセスが fireAllRules() を呼び出すと、エンジンは Agenda Evaluation フェーズに切り替わります。
  2. アジェンダ評価。これにより、実行するルールの選択が試行されます。ルールが見つからない場合は終了し、見つからない場合は、検出ルールが実行され、フェーズを Working Memory Actions に切り替えます。
このプロセスは、アジェンダが明確になるまで繰り返されます。この場合、制御は呼び出しアプリケーションに戻ります。作業メモリーアクションが行われても、ルールは実行されていません。

6.3. デフォルトの競合解決ストラテジー

顕著性(優先度)
ユーザーは、特定のルールの優先度を(数値で)他のルールよりも高く指定できます。この場合、顕著性が高いルールが優先されます。
LIFO (last in, first out)
LIFO 優先度は、割り当てられた Working Memory Action カウンター値に基づいています。同じ優先度値を持つ一連の起動の実行順序は任意です。
注記
一般的なルールとして、特定の順番で発生するルールにカウントせず、フローを気にせずにルールを作成することが推奨されます。ただし、フローが必要な場合は、アジェンダグループ、ルールフローグループ、アクティベーショングループ、制御/セマフォファクトなど、多くの可能性があります。これらについては後のセクションで説明します。

6.4. AgendaGroup

アジェンダグループは、アジェンダでルールをパーティション分割する方法です。常に 1 つのグループのみが focus を持ちます。つまり、そのグループ内のルールのアクティベーションのみが有効になります。また、"auto focus" があるルールもあります。これは、ルールの条件が true の場合に、アジェンダグループにフォーカスが取られることを意味します。
アジェンダグループは、CLIPS の用語ではモジュールと呼ばれます。アジェンダグループは、グループ化されたルール間でフローを作成する方法を提供します。ルールエンジン内から、または API を介してフォーカスしたグループを切り替えることができます。ルールに複数の "phases" または "sequences" の処理が必要な場合は、この目的のために agenda-groups を使用することを検討してください。

6.5. setFocus()

setFocus() が呼び出されるたびに、指定されたアジェンダグループをスタックにプッシュします。フォーカスグループが空の場合は、スタックからポップアップされ、上位評価上にあるフォーカスグループになります。アジェンダグループは、スタックの複数の場所に表示されます。デフォルトの Agenda Group は "MAIN" で、このグループにアジェンダグループを指定しないすべてのルールは MAIN です。これは、デフォルトで最初にフォーカスされるスタックの最初のグループでもあります。

6.6. setFocus() Example

setFocus ()要素は以下のようになります。
ksession.getAgenda().getAgendaGroup( "Group A" ).setFocus();

6.7. ActivationGroup

アクティベーショングループは、同じ "activation-group" ルール属性によってバインドされている一連のルールです。このグループでは、実行できるルールは 1 つだけで、そのルールの実行後に、その他のルールはすべてアジェンダからキャンセルされます。clear() メソッドはいつでも呼び出すことができます。これにより、アクティベーションが実行される前にすべてのアクティベーションが取り消されます。

6.8. ActivationGroup の例

ActivationGroup は次のようになります。
ksession.getAgenda().getActivationGroup( "Group B" ).clear();

6.9. RuleFlowGroup

ルールフローグループは、"ruleflow-group" ルール属性に関連付けられたルールのグループです。これらのルールは、グループがアクティベートされた場合にのみ実行できます。グループ自体は、ルールフローの図の詳細がグループを表すノードに到達してからでないと、アクティブになりません。また、clear() メソッドはいつでも呼び出すことで、アジェンダに残っているすべてのアクティベーションをキャンセルできます。

6.10. RuleFlowGroup Example

RuleFlowGroup プロパティーは以下のようになります。
ksession.getAgenda().getRuleFlowGroup( "Group C" ).clear();

6.11. ルールとメソッドの違い

  • メソッドは直接呼び出されます。
  • 特定のインスタンスが渡されます。
  • 1 回の呼び出しを行うと、1 回の実行になります。
  • ルールは、エンジンに挿入されている限り、任意のデータに対して一致して実行されます。
  • ルールは直接呼び出すことはできません。
  • 特定のインスタンスをルールに渡すことはできません。
  • 一致によっては、ルールは 1 回または複数回実行されるか、まったく実行されない場合があります。

6.12. クロス製品の例

以下は、未制約の火災を生じさせないアラーム状況で設定されるルールを示しています。
rule
when
    $room : Room()
    $sprinkler : Sprinkler()
then
    System.out.println( "room:" + $room.getName() +
                        " sprinkler:" + $sprinkler.getRoom().getName() );
end
SQL では、これは select * from Room, Sprinkler であり、Room テーブルのすべての行は、Sprinkler テーブルのすべての行に参加して、以下の出力になります。
room:office sprinkler:office
room:office sprinkler:kitchen
room:office sprinkler:livingroom
room:office sprinkler:bedroom
room:kitchen sprinkler:office
room:kitchen sprinkler:kitchen
room:kitchen sprinkler:livingroom
room:kitchen sprinkler:bedroom
room:livingroom sprinkler:office
room:livingroom sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:livingroom sprinkler:bedroom
room:bedroom sprinkler:office
room:bedroom sprinkler:kitchen
room:bedroom sprinkler:livingroom
room:bedroom sprinkler:bedroom
これらのクロス製品は大きくなり、誤ったデータが含まれる可能性があります。これは、変数制約で行われるクロス製品を制限することで元に戻すことができます。
rule
when
    $room : Room()
    $sprinkler : Sprinkler( room == $room )
then
    System.out.println( "room:" + $room.getName() +
                        " sprinkler:" + $sprinkler.getRoom().getName() );
end
これにより、Room ごとに正しいスプリンクラーを持つ 4 行のデータのみが生成されます。SQL (実際 HQL)では、対応するクエリーは select * from Room, Sprinkler where Room == Sprinkler.room になります。
room:office sprinkler:office
room:kitchen sprinkler:kitchen
room:livingroom sprinkler:livingroom
room:bedroom sprinkler:bedroom

6.13. アクティベーション、アジェンダ、および競合セットの例

この例では、cashflow 計算システムが機能しています。以下は、実装された 3 つのクラスです。
public class CashFlow {
    private Date   date;
    private double amount;
    private int    type;
    long           accountNo;
    // getter and setter methods here
}

public class Account {
    private long   accountNo;
    private double balance;
    // getter and setter methods here
}

public AccountPeriod {
    private Date start;
    private Date end;
    // getter and setter methods here
}
2 つのルールを使用して、その四半期のデビットとクレジットを決定し、Account balance を更新できます。以下の 2 つのルールは、特定の期間アカウントの cashflows を制限します。フィールド名が 2 回繰り返されるのを避けるために、短いカット構文を使用する&& に注意してください。
rule "increase balance for credits"
when
  ap : AccountPeriod()
  acc : Account( $accountNo : accountNo )
  CashFlow( type == CREDIT,
            accountNo == $accountNo,
            date >= ap.start && <= ap.end,
            $amount : amount )
then
  acc.balance  += $amount;
end
rule "decrease balance for debits" 
when 
  ap : AccountPeriod() 
  acc : Account( $accountNo : accountNo ) 
  CashFlow( type == DEBIT, 
            accountNo == $accountNo,
            date >= ap.start && <= ap.end, 
            $amount : amount ) 
then 
  acc.balance -= $amount; 
end
AccountPeriod が最初の年に設定され、"increase balance for credits" のルールが 2 つのデータ行で実行され、1 行のデータに対応するように derease balance for debits のルールを制限します。
データは挿入段階で照合され、fireAllRules() が呼び出された後にのみ実行されます。一方で、ルールとその一致したデータは Agenda に配置され、Activation と呼ばれます。Agenda は、fireAllRules ()が呼び出されるとすぐに実行できるアクティベーションの表で、結果が実行されます。アジェンダでのアクティベーションは順番に実行されます。これまでの実行の順序は任意とみなされることに注意してください。
上記のすべてのアクティベーションが実行されると、アカウントのバランスは -25 になります。
AccountPeriod が 2 年 2 年に更新されると、一致するデータの行が 1 つだけであるため、Agenda の 1 つの Activation のみになります。
そのアクティベーションを実行すると、25 のバランスになります。

6.14. 競合リゾルバーストラテジー

アジェンダにアクティベーションキーが 1 つ以上ある場合は、競合していると言われ、競合リゾルバーストラテジーを使用して実行の順番が決定されます。最も単純なレベルでは、デフォルトのストラテジーでは顕著性を使用してルールの優先度を決定します。

6.15. 競合リゾルバーストラテジーの例

各ルールのデフォルト値は 0 で、優先度が高い値が高くなります。これを説明するために、アカウントバランスを出力するルールが追加されます。この目的は、すべてのアカウントに対してすべてのデビットとクレジットが適用された後にルールが実行されることです。これは、デフォルトの顕著性が 0 のすべてのルールの後に実行されるように、このルールに負の顕著性を割り当てることによって行われます。
rule "Print balance for AccountPeriod"
        salience -50
    when
        ap : AccountPeriod()
        acc : Account()        
    then
        System.out.println( acc.accountNo + " : " + acc.balance );    
end

6.16. トリガーの例

表6.1 トリガーの例

ルールビュー トリガーの表示
select * from Account acc,
              Cashflow cf,
              AccountPeriod ap      
where acc.accountNo == cf.accountNo and 
      cf.type == CREDIT and
      cf.date >= ap.start and 
      cf.date <= ap.end
select * from Account acc, 
              Cashflow cf,
              AccountPeriod ap 
where acc.accountNo == cf.accountNo and 
      cf.type == DEBIT and
      cf.date >= ap.start and 
      cf.date <= ap.end
trigger : acc.balance += cf.amount
trigger : acc.balance -= cf.amount

6.17. ruleflow-group の例

ルールでの ruleflow-group 属性の使用を以下に示します。
rule "increase balance for credits"
  ruleflow-group "calculation"
when
  ap : AccountPeriod()
  acc : Account( $accountNo : accountNo )
  CashFlow( type == CREDIT,
            accountNo == $accountNo,
            date >= ap.start && <= ap.end,
            $amount : amount )
then
  acc.balance  += $amount;
end
rule "Print balance for AccountPeriod"
  ruleflow-group "report"
when
  ap : AccountPeriod()
  acc : Account()
then
  System.out.println( acc.accountNo +
                      " : " + acc.balance );    
end

6.18. 推論の例

以下の例では、IsAdult プロパティーを使用してユーザーの年齢を推測します。
rule "Infer Adult"
when
  $p : Person( age >= 18 )
then
  insert( new IsAdult( $p ) )
end
この推論関係は、どのルールでも使用することができます。
$p : Person()
IsAdult( person == $p )
さらに、ナレッジプロセスを分離すると、データ漏えいやサードパーティーの変更が情報にさらされる可能性が低くなります。

6.19. Inference および TruthMaintenance の実装

手順6.1 タスク

  1. 一連のルールを開きます。この例では、バスが発行システムを使用します。
    rule "Issue Child Bus Pass" when
      $p : Person( age < 16 )
    then
      insert(new ChildBusPass( $p ) );
    end
     
    rule "Issue Adult Bus Pass" when
      $p : Person( age >= 16 )
    then
      insert(new AdultBusPass( $p ) );
    end
  2. insertLogical ファクトを挿入し、推測する用語を追加します。
    rule "Infer Child" when
      $p : Person( age < 16 )
    then
        insertLogical( new IsChild( $p ) )
    end
    rule "Infer Adult" when
        $p : Person( age >= 16 )
    then
        insertLogical( new IsAdult( $p ) )
    end
    ファクトが論理的に挿入されている。このファクトは、when 句の真理によって異なります。これは、ルールが false になると、ファクトは自動的に取り消されることを意味します。これは特に 2 つのルールは相互に排他的です。上記のルールでは、子が 16 未満の場合に IsChild ファクトが挿入されます。ユーザーが 16 を超え、IsAdult ファクトが挿入されると、自動的に取り消されます。
  3. コードを入力してパスを発行します。TMS は取り消しセットのカスケードに対する論理挿入の連鎖をサポートするため、これらは論理的に挿入することもできます。
    rule "Issue Child Bus Pass" when
        $p : Person( )
        IsChild( person == $p )
    then
        insertLogical(new ChildBusPass( $p ) );
    end
     
    rule "Issue Adult Bus Pass" when
        $p : Person( age >= 16 )
        IsAdult( person =$p )
    then
        insertLogical(new AdultBusPass( $p ) );
    end
    現在、ユーザーが 15 から 16 に変更すると、IsChild ファクトは自動的に取り消されるだけでなく、はユーザーの ChildBusPass ファクトになります。
  4. not 条件要素を挿入して通知を処理します。(この場合、パスを返すリクエスト。) TMS が ChildBusPass オブジェクトを自動的に取り消すと、このルールはトリガーして要求を人に送信します。
    rule "Return ChildBusPass Request "when
      $p : Person( )
           not( ChildBusPass( person == $p ) )
    then
        requestChildBusPass( $p );
    end

第7章 ルール言語

7.1. KnowledgeBuilder

KnowledgeBuilder は、DRL ファイルや xPaaS ファイルなどのソースファイルを取得し、ナレッジベースが消費できるルールおよびプロセス定義のナレッジパッケージに変換します。ResourceType クラスのオブジェクトは、ビルダーが処理を求められているリソースのタイプを示します。

7.2. ResourceFactory

ResourceFactory は、java.io.Reader、クラスパス、URL、java.io.File、またはバイト配列など、複数のソースからリソースを読み込む機能を提供します。デシジョンテーブル(Excel の .xls ファイル)などのバイナリーファイルを Reader で渡すことはできません。これは、テキストベースのリソースにのみ適しています。

7.3. 新しい KnowledgeBuilder の作成

手順7.1 タスク

  1. KnowledgeBuilderFactory を開きます。
  2. 新しいデフォルト設定を作成します。
  3. 以下を設定に入力します。
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    最初のパラメーターはプロパティー用で、は任意です。空白のままにすると、デフォルトのオプションが使用されます。options パラメーターは、ダイアレクトの変更や新しいアクセラレーター関数の登録などに使用できます。
  4. カスタム ClassLoader を使用して KnowledgeBuilder を追加するには、以下のコードを使用します。
    KnowledgeBuilderConfiguration kbuilderConf = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, classLoader );
    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(kbuilderConf);
    

7.4. DRL リソースの追加

手順7.2 タスク

  1. 任意のタイプのリソースは繰り返し追加できます。以下に DRL ファイルを追加します。ナレッジストアは複数の namespace を処理できるため、namespace に関係なくリソースを組み合わせることができます。
    kbuilder.add( ResourceFactory.newFileResource( "/project/myrules.drl" ),
                  ResourceType.DRL);
    
  2. 各リソースの追加後にコンパイル結果を確認します。KnowledgeBuilder は、ERROR、WARNING、および INFO の 3 つの異なる重大度のコンパイル結果を報告できます。
    • ERROR は、リソースのコンパイルが失敗したことを示します。エラーが発生する場合は、リソースを追加したり、ナレッジパッケージを取得したりしないでください。getKnowledgePackages() エラーがある場合は、空の一覧を返します。
    • WARNING および INFO の結果は無視できますが、検査にも使用できます。

7.5. KnowledgeBuilder Result Inspection メソッド

KnowledgeBuilder API には、重大度の一覧のビルド結果をチェックおよび取得するメソッドが複数あります。
    /**
     * Return the knowledge builder results for the listed severities.
     * @param severities
     * @return
     */
    KnowledgeBuilderResults getResults(ResultSeverity... severities);
    
    /**
     * Checks if the builder generated any results of the listed severities
     * @param severities
     * @return
     */
    boolean hasResults(ResultSeverity... severities ;
KnowledgeBuilder API には、エラーのみを検査するヘルパーメソッドがあります( hasErrors() getErrors()
if( kbuilder.hasErrors() ) {
    System.out.println( kbuilder.getErrors() );
    return;
}

7.6. KnowledgePackages の取得

全リソースが追加され、エラーがない場合は、ナレッジパッケージのコレクションを取得できます。これは、パッケージ名前空間ごとに 1 つの KnowledgePackage があるため、java.util.Collection です。これらのナレッジパッケージはシリアライズ可能で、多くの場合、デプロイメント単位として使用されます。
以下のようになります。
Collection<KnowledgePackage> kpkgs = kbuilder.getKnowledgePackages();

7.7. 拡張ナレッジBuilder の例

以下は、完全な KnowledgeBuilder パッケージの例です。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
if( kbuilder.hasErrors() ) {
    System.out.println( kbuilder.getErrors() );
    return;
}

KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newFileResource( "/project/myrules1.drl" ),
              ResourceType.DRL);
kbuilder.add( ResourceFactory.newFileResource( "/project/myrules2.drl" ),
              ResourceType.DRL);

if( kbuilder.hasErrors() ) {
    System.out.println( kbuilder.getErrors() );
    return;
}

Collection<KnowledgePackage> kpkgs = kbuilder.getKnowledgePackages();

7.8. バッチモードでの KnowledgeBuilder の使用

KnowledgeBuilder には、Fluent インターフェイスを持つバッチモードがあります。以下の例のように、複数の DRL を一度にビルドできます。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.batch()
        .add( ResourceFactory.newFileResource( "/project/myrules1.drl" ), ResourceType.DRL )
        .add( ResourceFactory.newFileResource( "/project/myrules2.drl" ), ResourceType.DRL )
        .add( ResourceFactory.newFileResource( "/project/mytypes1.drl" ), ResourceType.DRL )
        .build();

7.9. 最後に追加した DRL のビルドの破棄

KnowledgeBuilder (バッチモードを使用している場合は通常)、最後の DRL ビルドで追加されたものを破棄することもできます。これは、以下のように、ナレッジBuilder に誤った DRL を追加しないように復元するのに役立ちます。
kbuilder.add( ResourceFactory.newFileResource( "/project/wrong.drl" ), ResourceType.DRL );
if ( kbuilder.hasErrors() ) {
    kbuilder.undo();
}

7.10. 設定および ChangeSet XML を使用したビルド

ChangeSet XML 内で設定を使用して定義を作成できます。シンプルな XML ファイルは、add、remove、および modify の 3 つの要素をサポートします。それぞれには、設定エンティティーを定義する <resource> サブ要素のシーケンスがあります。

7.11. ChangeSet XML の XML スキーマ(正規化ではありません)

これは、非正規化的な ChangeSet のスキーマです。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="http://drools.org/drools-5.0/change-set"
           targetNamespace="http://drools.org/drools-5.0/change-set">

  <xs:element name="change-set" type="ChangeSet"/>

  <xs:complexType name="ChangeSet">
    <xs:choice maxOccurs="unbounded">
      <xs:element name="add"    type="Operation"/>
      <xs:element name="remove" type="Operation"/>
      <xs:element name="modify" type="Operation"/>
    </xs:choice>
  </xs:complexType>

  <xs:complexType name="Operation">
    <xs:sequence>
      <xs:element name="resource" type="Resource"
                  maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="Resource">
    <xs:sequence>
      <!-- To be used with <resource type="DTABLE"...> -->
      <xs:element name="decisiontable-conf" type="DecTabConf"
                  minOccurs="0"/>
    </xs:sequence>
    <!-- java.net.URL, plus "classpath" protocol -->
    <xs:attribute name="source" type="xs:string"/>
    <xs:attribute name="type"   type="ResourceType"/>
  </xs:complexType>

  <xs:complexType name="DecTabConf">
    <xs:attribute name="input-type"     type="DecTabInpType"/>
    <xs:attribute name="worksheet-name" type="xs:string"
                  use="optional"/>
  </xs:complexType>

  <!-- according to org.drools.builder.ResourceType -->
  <xs:simpleType name="ResourceType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="DRL"/>
      <xs:enumeration value="XDRL"/>
      <xs:enumeration value="DSL"/>
      <xs:enumeration value="DSLR"/>
      <xs:enumeration value="DRF"/>
      <xs:enumeration value="DTABLE"/>
      <xs:enumeration value="PKG"/>
      <xs:enumeration value="BRL"/>
      <xs:enumeration value="CHANGE_SET"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- according to org.drools.builder.DecisionTableInputType -->
  <xs:simpleType name="DecTabInpType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="XLS"/>
      <xs:enumeration value="CSV"/>
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

第8章 Changesets

8.1. Changesets

changeset 機能は、API を使用せずにナレッジベースの構築を容易にします。Changesets には、任意の数のリソースを含めることができます。また、デシジョンテーブルにのみ必要な設定情報をサポートすることもできます。
changeset.xml ファイルには、このファイルのリソース一覧が含まれます。別の changeset XML ファイルに再帰的にポイントすることもできます。

8.2. 変更セットの例

接頭辞を使用してプロトコルを示すリソースアプローチが使用されます。ファイルや http など、java.net.URL が提供するすべてのプロトコルと、追加のクラスパスがサポートされています。現在、ファイル名拡張子から推測されないため、type 属性はリソースに対して常に指定する必要があります。これは以下の例で示されています。
<change-set xmlns='http://drools.org/drools-5.0/change-set'
             xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
             xs:schemaLocation='http://drools.org/drools-5.0/change-set http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >
   <add>
       <resource source='http:org/domain/myrules.drl' type='DRL' />
   </add>
 </change-set>
上記の例は、リソースタイプを CHANGE_SET に変更することで使用できます。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newClasspathResource( "myChangeSet.xml", getClass() ),
              ResourceType.CHANGE_SET );
if ( kbuilder.hasErrors() ) {
    System.err.println( builder.getErrors().toString() );
}

8.3. Changeset XML の例

以下は、基本的な ChangeSet XML スキーマの例です。
<change-set xmlns='http://drools.org/drools-5.0/change-set'
             xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
             xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >
   <add>
      <resource source='file:/project/myrules.drl' type='DRL' />
   </add>
</change-set>

8.4. Changeset Protocols

ChangeSet は、file や http などの java.net.URL が提供するすべてのプロトコルと、追加のクラスパスをサポートします。type 属性は、ファイル名の拡張子から推測されないため、リソースに対して常に指定する必要があります。
file: 接頭辞を使用して、リソースのプロトコルを示します。
注記
XML スキーマを使用する場合、ClassPath リソースによって ChangeSet XML 自体がロードされない限り、クラ出力ダーはデフォルトで Knowledge Builder が使用するスキーマになります。この場合、そのリソースに指定されたクラ出力ダーを使用します。

8.5. ChangeSet XML の読み込み

手順8.1 タスク

  1. API を使用して ChangeSet をロードします。
  2. このコードを使用して ChangeSet XML にアクセスします。
    kbuilder.add( ResourceFactory.newUrlResource( url ), ResourceType.CHANGE_SET );
    

8.6. リソース設定による XML の変更例

この例は、ChangeSet にリソース設定を組み込む方法を示しています。
 <change-set xmlns='http://drools.org/drools-5.0/change-set'
             xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
             xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/drools/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >
  <add>
       <resource source='http:org/domain/myrules.drl' type='DRL' />
       <resource source='classpath:data/IntegrationExampleTest.xls' type="DTABLE">
           <decisiontable-conf input-type="XLS" worksheet-name="Tables_2" />
       </resource>
   </add>
 </change-set>

8.7. Changeset XML およびディレクトリー

以下のコードを使用すると、ディレクトリーの内容を ChangeSet に追加できます。
<change-set xmlns='http://drools.org/drools-5.0/change-set'
            xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
            xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/drools/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >
   <add>
      <resource source='file:/projects/myproject/myrules' type='DRL' />
   </add>
</change-set>
注記
現在、そのフォルダー内のすべてのリソースが同じタイプであることが予想されます。ナレッジストアを使用する場合は、リソースを追加、変更、または削除するための継続的なスキャンを提供し、キャッシュされたナレッジベースを再ビルドします。

第9章 ビルド

9.1. ビルド結果の重大度

ビルド結果のタイプのデフォルトの重大度を変更できます。これは、既存のルールの重複名を持つ新しいルールがパッケージに追加されている場合に便利です。(この場合、デフォルトの動作では古いルールを新しいルールに置き換え、INFO として報告します。) 一部のデプロイメントでは、ユーザーはルールの更新を防ぎ、エラーとして報告する必要がある場合があります。

9.2. デフォルトのビルド結果の重大度の設定

手順9.1 タスク

  1. システムプロパティーまたは設定ファイルを使用して設定するには、以下のプロパティーを挿入します。
    // sets the severity of rule updates
    drools.kbuilder.severity.duplicateRule = <INFO|WARNING|ERROR>
    // sets the severity of function updates
    drools.kbuilder.severity.duplicateFunction = <INFO|WARNING|ERROR>
    
  2. API を使用して重大度を変更するには、以下のコードを使用します。
    KnowledgeBuilderConfiguration kbconf = ...
    
    // sets the severity of rule updates to error
    kbconf.setOption( KBuilderSeverityOption.get( "drools.kbuilder.severity.duplicateRule", ResultSeverity.ERROR ) ); 
    // sets the severity of function updates to error
    kbconf.setOption( KBuilderSeverityOption.get( "drools.kbuilder.severity.duplicateFunction", ResultSeverity.ERROR ) );
    

9.3. KnowledgePackage

ナレッジストアは、ルールやプロセスなどのナレッジ定義のコレクションです。ナレッジストアは Knowledge Builder により作成されます。ナレッジパッケージは自己完結型でシリアライズ可能で、現在基本的なデプロイメントユニットを形成します。
注記
ナレッジベースに追加されたら、ナレッジパッケージインスタンスを再利用できません。別のナレッジベースに追加する必要がある場合は、これをシリアライズし、クローン結果を使用します。

9.4. 新規ナレッジベースの作成

手順9.2 タスク

  1. このデフォルト設定を使用して、新しいナレッジベースを作成します。
    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
  2. カスタムクラ出力ダーが KnowledgeBuilder で使用され、デフォルトのクラ出力ダーにないタイプを解決する場合は、KnowledgeBase にも設定する必要があります。この手法は KnowledgeBuilder と同じですが、以下のようになります。
    KnowledgeBaseConfiguration kbaseConf =
        KnowledgeBaseFactory.createKnowledgeBaseConfiguration( null, cl );
    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase( kbaseConf );

9.5. プロセスの構築およびデプロイメント

インプロセスの構築は、最も単純なデプロイメント形式です。ナレッジ定義をコンパイルし、同じ JVM の ナレッジベース に追加します。この方法では、drools-core.jar および drools-compiler.jar がクラスパス上になければなりません。

9.6. ナレッジベースへの KnowledgePackages の追加

手順9.3 タスク

  • KnowledgePackages を KnowledgeBase に追加するには、以下のコードを使用します。
    Collection<KnowledgePackage> kpkgs = kbuilder.getKnowledgePackages();
    
    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
    kbase.addKnowledgePackages( kpkgs );
    addKnowledgePackages(kpkgs) メソッドを繰り返し呼び出すと、さらに知識を追加できます。

9.7. 別のプロセスでのビルドとデプロイメント

KnowledgeBaseKnowledgePackage はどちらもデプロイメント単位であり、シリアライズが可能です。つまり、1 台のマシンに必要なビルドを実行し、drools-compiler.jar を必要とし、別のマシンが drools-core.jar のみを必要とするものをすべてデプロイおよび実行できます。

9.8. KnowledgePackage の OutputStream への書き込み

これは、KnowledgePackage を OutputStream に記述するためのコードです。
 ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream( fileName ) );
out.writeObject( kpkgs );
out.close();

9.9. InputStream からの KnowledgePackage の読み取り

このコードを使用して、InputStream から KnowledgePackage を読み取ります。
ObjectInputStream in = new ObjectInputStream( new FileInputStream( fileName ) );
// The input stream might contain an individual
// package or a collection.
@SuppressWarnings( "unchecked" )
Collection<KnowledgePackage> kpkgs =
    ()in.readObject( Collection<KnowledgePackage> );
in.close();

KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kpkgs );

9.10. JBoss Rules Management System

JBoss Rules Management システムは、ナレッジベースのリポジトリーです。大規模なルールセットを維持するのに役立ちます。
さらに、システムはシリアライズされたナレッジパッケージを URL にコンパイルして公開し、その URL はパッケージのロードに使用されます。

9.11. StatefulknowledgeSessions および KnowledgeBase Modifications

KnowledgeBase は、StatefulKnowledgeSession オブジェクトを作成および返し、オプションでそれらへの参照を保持することができます。
KnowledgeBase の変更が発生すると、セッションのデータに対して適用されます。この参照は弱い参照であり、オプションでもあります。これはブール値フラグによって制御されます。

9.12. 新しい KnowledgeAgents

これは、新しい KnowledgeAgent を作成するコードです。
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "MyAgent" );

9.13. KnowledgePackage の OutputStream への書き込み

これは、KnowledgePackage を OutputStream に書き込む方法です。
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "MyAgent" );
kagent.applyChangeSet( ResourceFactory.newUrlResource( url ) );
KnowledgeBase kbase = kagent.getKnowledgeBase();
注記
リソーススキャンは、デフォルトではオンではありません。起動する必要があります。これは通知にも適用されます。いずれも ResourceFactory を介して実行できます。

9.14. スキャンおよび通知サービスの起動

これは、スキャンおよび通知サービスを開始するためのコードです。
ResourceFactory.getResourceChangeNotifierService().start();
ResourceFactory.getResourceChangeScannerService().start();

9.15. ResourceChangeScanner

ResourceChangeScanner は、サービスをスキャンするために使用されます。デフォルトのリソーススキャン期間は、ResourceChangeScannerService から変更できます。適切に更新される ResourceChangeScannerConfiguration オブジェクトはサービスの configure() メソッドに渡されます。これにより、サービスがオンデマンドで再設定されます。

9.16. スキャン間隔の変更

これは、スキャン間隔を変更するために使用するコードです。
ResourceChangeScannerConfiguration sconf =
    ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
// Set the disk scanning interval to 30s, default is 60s.
sconf.setProperty( "drools.resource.scanner.interval", "30" ); 
ResourceFactory.getResourceChangeScannerService().configure( sconf );

9.17. ナレッジベースとナレッジベース間の対話

ナレッジエージェントは、空のナレッジベースまたは設定されたナレッジベースの両方を処理できます。設定されたナレッジベースが提供されると、ナレッジエージェントは ナレッジベース からイテレーターを実行し、見つかったリソースにサブスクライブします。ナレッジストアはディレクトリーにある全リソースを構築することは可能ですが、ナレッジビルダーがその情報が失われるため、これらのディレクトリーは継続的にスキャンされません。applyChangeSet(Resource) メソッドの一部として指定されたディレクトリーのみが監視されます。
KnowledgeBase を開始点として提供する利点の 1 つは、KnowledgeBaseConfiguration を提供することです。リソースの変更が検出され、新しい KnowledgeBase オブジェクトがインスタンス化されると、以前の KnowledgeBase オブジェクトの KnowledgeBaseConfiguration が使用されます。

9.18. 既存のナレッジベースの使用

これは、既存のナレッジベースを使用するためのコードです。
KnowledgeBaseConfiguration kbaseConf =
    KnowledgeBaseFactory.createKnowledgeBaseConfiguration( null, cl );
KnowledgeBase kbase KnowledgeBaseFactory.newKnowledgeBase( kbaseConf );
// Populate kbase with resources here.

KnowledgeAgent kagent =
    KnowledgeAgentFactory.newKnowledgeAgent( "MyAgent", kbase );
KnowledgeBase kbase = kagent.getKnowledgeBase();
上記の例では、getKnowledgeBase() は、リソースの変更が検出され、新しいナレッジベースがビルドされるまで、同じ提供される kbase インスタンスを返します。新しいナレッジベースを構築すると、前の KnowledgeBase に提供された KnowledgeBaseConfiguration で実行されます。

9.19. applyChangeSet ()メソッド

ChangeSet XML を applyChangeSet() メソッドで使用すると、ディレクトリーがスキャンプロセスに追加されます。ディレクトリースキャンが追加のファイルを検出すると、ナレッジベースに追加されます。削除されたファイルは ナレッジベース から削除され、変更されたファイルは ナレッジベース から削除されます。

9.20. ディレクトリーコンテンツを追加するように XML を変更します。

この XML を使用して、ディレクトリーの内容を ChangeSet に追加します。
<change-set xmlns='http://drools.org/drools-5.0/change-set'
            xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
            xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd' >
   <add>
      <resource source='file:/projects/myproject/myrules' type='PKG' />
   </add>
</change-set>
注記
リソースタイプ PKG の場合、drools-compiler 依存関係は必要ありません。ナレッジストアは drools-core のみを持つものを処理できます。

9.21. KnowledgeAgentConfiguration プロパティー

KnowledgeAgentConfiguration を使用して、ナレッジエージェントのデフォルトの動作を変更できます。これを使用して、そのディレクトリーの変更を継続的にスキャンしないようにしながら、ディレクトリーからリソースを読み込むことができます。

9.22. スキャン動作の変更

このコードを使用してスキャンの動作を変更します。
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();

KnowledgeAgentConfiguration kaconf =
    KnowledgeAgentFactory.newKnowledgeAgentConfiguation();
// Do not scan directories, just files.
kaconf.setProperty( "drools.agent.scanDirectories", "false" );
KnowledgeAgent kagent =
    KnowledgeAgentFactory.newKnowledgeAgent( "test agent", kaconf );

第10章 Sessions

10.1. JBoss ルールのセッション

セッション は、KnowledgeBase から作成されます。データは挿入でき、どのプロセスインスタンスを開始できるか。KnowledgeBase を作成すると、リソースを大量に消費できますが、セッションの作成はできません。このため、セッションを繰り返し作成できるように、可能な限りナレッジベースのキャッシュを行うことが推奨されます。

10.2. ナレッジベースから StatefulKnowledgeSession を作成する

これは、ナレッジベースから新しい StatefulKnowledgeSession を作成するためのコードです。
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

10.3. WorkingMemoryEntryPoint メソッド

WorkingMemoryEntryPoint は、ファクトの挿入、更新、および取得を行うメソッドを提供します。エントリーポイント という用語は、作業メモリーに複数のパーティションがあり、どのパーティションに挿入するかを選択できます。ほとんどのルールベースのアプリケーションは、デフォルトのエントリーポイントのみで動作します。

10.4. KnowledgeRuntime インターフェイス

KnowledgeRuntime インターフェイスは、エンジンとの主な対話を提供します。これは、ルール結果およびプロセスアクションで利用できます。KnowledgeRuntime は、WorkingMemoryProcessRuntime の両方からメソッドを継承するため、プロセスおよびルールを操作するための統合 API が提供されます。ルールを使用する場合には、KnowledgeRuntimeWorkingMemoryEntryPoint、および WorkingMemory 自体の 3 つのインターフェイスが形成されます。KnowledgeRuntime

10.5. ファクトの挿入

挿入は、WorkingMemory にファクトを伝える動作です。これは、ksession.insert(yourObject) などを使用して実行できます。ファクトを挿入すると、ルールに対する一致について検査されます。つまり、ルールの実行または実行を決定する作業は すべて 挿入時に実行されます。ただし、fireAllRules() を呼び出すまでルールは実行されません。このルールは、ファクトの挿入を終了した後に呼び出します。

10.6. FactHandle トークン

オブジェクトが挿入されると、FactHandle を返します。この FactHandle は、WorkingMemory 内に挿入されたオブジェクトを表すために使用されるトークンです。また、オブジェクトを取り消したり変更したりする場合に WorkingMemory との対話にも使用されます。

10.7. FactHandle の例

Job accountant = new Job("accountant");
FactHandle accountantHandle = ksession.insert( accountant );

10.8. Identity および Equality

以下は、作業メモリーで使用される 2 つのアサートノードです。
アイデンティティー
これは、Working Memory が IdentityHashMap を使用してアサートされたすべてのオブジェクトを保存することを意味します。新しいインスタンスアサーションは常に新しい FactHandle を返しますが、インスタンスが再度アサートされると、元のファクトハンドルが返されます(つまり、同じオブジェクトに対して繰り返し挿入を無視します)。
等号
これは、Working Memory が HashMap を使用してアサートされたすべてのオブジェクトを保存することを意味します。オブジェクトインスタンスのアサーションは、( equal メソッドにより)挿入されたオブジェクトが既存のファクトと等しくない場合のみ新しい FactHandle を返します。
注記
新しいインスタンスアサーションは常に新しい FactHandle を返しますが、インスタンスが再度アサートされると、元のファクトハンドルが返されます(つまり、同じオブジェクトに対して繰り返し挿入を無視します)。

10.9. Retraction

取り消しは、作業メモリーからファクトを削除します。つまり、そのファクトを追跡し、一致しなくなり、そのファクトに依存し、そのファクトに依存するルールはキャンセルされます。ファクトのノッキネスに依存するルールを指定できます。この場合、ファクトを取り消しると、ルールがアクティベートされる可能性があります。取り消しは、挿入呼び出しによって返された FactHandle を使用して実行できます。ルールの右側にある retract ステートメントが使用されます。これは、単純なオブジェクト参照で動作します。

10.10. 取り消しの例

Job accountant = new Job("accountant");
FactHandle accountantHandle = ksession.insert( accountant );
....
ksession.retract( accountantHandle );

10.11. update ()メソッド

ルールエンジンは、修正したファクトを再処理できるように通知する必要があります。update() メソッドを使用して、WorkingMemory 自体に通知することができないオブジェクトの変更されたオブジェクトを WorkingMemory に通知できます。update() メソッドは、変更されたオブジェクトを常に 2 番目のパラメーターとして取ります。これにより、イミュータブルなオブジェクトに新しいインスタンスを指定できます。
注記
ルールの右側には、modify ステートメントが推奨されます。これは、変更を加え、エンジンを単一のステートメントで通知するためです。または、セッターメソッドの呼び出しを介してファクトオブジェクトのフィールド値を変更したら、すぐに update を呼び出すか、別のファクトを変更する前にイベントを呼び出す必要があります。そうしないと、ルールエンジン内のインデックスで問題が発生する可能性があります。modify ステートメントにより、この問題を回避できます。

10.12. update ()の例

Job accountant = new Job("accountant");
FactHandle accountantHandle = workingMemory.insert( accountant );
...
accountant.setSalary( 45000 );
workingMemory.update( accountantHandle, accountant );

10.13. クエリー

クエリー はルールで使用されるパターンに基づいてファクトセットを取得するために使用されます。パターンはオプションのパラメーターを使用できます。クエリーは ナレッジベース で定義でき、そこから一致する結果を返すことができます。結果コレクションを繰り返し処理しますが、クエリーにバインドされている識別子を使用して、バインディング変数の名前を引数として get メソッドを呼び出すことで、対応するファクトまたはファクトフィールドにアクセスできます。バインディングがファクトオブジェクトを参照する場合、FactHandle は getFactHandle を呼び出して、変数名をパラメーターとして使用して取得できます。

10.14. クエリーの例

QueryResults results =
    ksession.getQueryResults( "my query", new Object[] { "string" } );
for ( QueryResultsRow row : results ) {
    System.out.println( row.get( "varName" ) );
}

10.15. ライブクエリー

クエリーを呼び出し、返されたセットを反復して結果を処理することは、時間の経過とともに変更を監視するのには適していません。
これを軽減するため、JBoss ルールは、反復可能な結果セットを返す代わりにリスナーがアタッチされたライブクエリーを提供します。これらのライブクエリーは、ビューを作成し、このビューのコンテンツ用に変更イベントを公開することで、オープンのままになります。有効にするには、パラメーターを使用してクエリーを開始し、結果ビューの変更をリッスンします。dispose メソッドはクエリーを終了し、このリアクティブシナリオを終了します。

10.16. ViewChangedEventListener 実装の例

final List updated = new ArrayList();
final List removed = new ArrayList();
final List added = new ArrayList();
 
ViewChangedEventListener listener = new ViewChangedEventListener() {           
 public void rowUpdated(Row row) {
  updated.add( row.get( "$price" ) );
 }
  
 public void rowRemoved(Row row) {
  removed.add( row.get( "$price" ) );
 }
  
 public void rowAdded(Row row) {
  added.add( row.get( "$price" ) );
 }
};       
 
// Open the LiveQuery
LiveQuery query = ksession.openLiveQuery( "cars",
                                          new Object[] { "sedan", "hatchback" },
                                          listener );
...
...
query.dispose() // calling dispose to terminate the live query
注記
ライブクエリーの Glazed Lists 統合の例は、にアクセスしてください。 http://blog.athico.com/2010/07/glazed-lists-examples-for-drools-live.html

10.17. KnowledgeRuntime

KnowledgeRuntime は、グローバルの設定やチャネルの登録など、ルールとプロセスの両方に適用されるメソッドを提供します。(Exit point は、チャネルの古い同意語です。)

第11章 オブジェクトおよびインターフェイス

11.1. グローバル

グローバル は、ルールエンジンに表示される名前付きオブジェクトですが、ファクトとは異なり、グローバルをサポートするオブジェクトの変更は、ルールの再評価をトリガーしません。グローバルは、ルールの RHS で使用されるサービスを提供するオブジェクトとして、またはルールエンジンからオブジェクトを返す手段として静的情報を提供するのに役立ちます。ルールの LHS でグローバルを使用する場合は、それが不変であることを確認してください。そうしないと、ルールの動作には影響がありません。

11.2. グローバルの使用

手順11.1 タスク

  1. グローバルの作業メモリーへの実装を開始するには、ルールファイルでグローバルを宣言し、Java オブジェクトでバックアップします。
    global java.util.List list
  2. ナレッジベースはグローバル識別子とそのタイプを認識するように、グローバルの名前とオブジェクト(任意のセッション)で ksession.setGlobal() を呼び出して、オブジェクトをグローバルに関連付けることができます。
    List list = new ArrayList();
    ksession.setGlobal("list", list);
    重要
    DRL コードでグローバルタイプと識別子を宣言しないと、この呼び出しから例外が出力されます。
  3. ルールの評価で使用される前にグローバルを設定します。これを行わないと、NullPointerException が作成されます。

11.3. グローバルの解決

グローバルは、以下の 3 つの方法で解決できます。
getGlobals()
ステートレスナレッジセッションメソッド getGlobals() は、セッションのグローバルへのアクセスを提供する Globals インスタンスを返します。これらは、すべての実行呼び出しで共有されます。実行呼び出しは異なるスレッドで同時に実行できるため、変更可能なグローバルには注意が必要です。
delegates
委譲の使用は、グローバル解決を提供するもう 1 つの方法です。グローバル( setGlobal(String, Object)を使用)に値を割り当てると、値は内部コレクションマッピング識別子の値に保存されます。この内部コレクションの識別子は、提供されたデリゲートよりも優先されます。この内部コレクションで識別子が見つからない場合は、デリゲートグローバル(存在する場合)が使用されます。
実行
実行スコープのグローバルは Command を使用して、CommandExecutor に渡されるグローバルを設定します。

11.4. セッションスコープのグローバル例

セッションスコープのグローバルは以下のようになります。
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
// Set a global hbnSession, that can be used for DB interactions in the rules.
ksession.setGlobal( "hbnSession", hibernateSession );
// Execute while being able to resolve the "hbnSession" identifier.  
ksession.execute( collection );

11.5. StatefulRuleSessions

StatefulRuleSession プロパティーは StatefulKnowledgeSession によって継承され、エンジン外に関連するルール関連のメソッドを提供します。

11.6. AgendaFilter オブジェクト

AgendaFilter オブジェクトは、アクティベーションの実行を許可または拒否するために使用されるフィルターインターフェイスのオプションの実装です。フィルターの対象は、実装によって異なります。

11.7. AgendaFilter の使用

手順11.2 タスク

  • フィルターを使用するには、fireAllRules() の呼び出し中に指定します。以下の例では、文字列 "Test" で終わるルールのみを許可します。その他はすべてフィルターリングされます。
    ksession.fireAllRules( new RuleNameEndsWithAgendaFilter( "Test" ) );

11.8. ルールエンジンフェーズ

エンジンは、以下の 2 つのフェーズを繰り返し循環します。
ワーキングメモリーアクション
これは、Consequence (RHS 自体)またはメインの Java アプリケーションプロセスのいずれかで、ほとんどの作業が行われる場所です。Consequence が終了するか、またはメインの Java アプリケーションプロセスが fireAllRules() を呼び出すと、エンジンは Agenda Evaluation フェーズに切り替わります。
アジェンダ評価
これにより、実行するルールの選択が試行されます。ルールが見つからない場合は終了します。それ以外の場合は、見つかったルールを実行し、フェーズを Working Memory Actions に戻します。
このプロセスは、アジェンダが明確になるまで繰り返されます。この場合、制御は呼び出しアプリケーションに戻ります。作業メモリーアクションが行われても、ルールは実行されていません。

11.9. イベントモデル

イベントパッケージは、ルールの発生、アサートされるオブジェクトなど、ルールエンジンイベントの通知を行う手段を提供します。これにより、たとえば、ロギングアクティビティーと監査アクティビティーをアプリケーション(およびルール)から分離することができます。

11.10. The KnowlegeRuntimeEventManager

KnowlegeRuntimeEventManager インターフェイスは KnowledgeRuntime によって実装され、WorkingMemoryEventManagerProcessEventManager の 2 つのインターフェイスを提供します。

11.11. The WorkingMemoryEventManager

WorkingMemoryEventManager では、リスナーを追加および削除できるため、ワーキングメモリーとアジェンダのイベントをリッスンできます。

11.12. Adding an AgendaEventListener

以下のコードスニペットは、簡単なアジェンダリスナーを宣言してセッションに割り当てる方法を示しています。アクティベーションが実行された後、アクティベーションが出力されます。
ksession.addEventListener( new DefaultAgendaEventListener() {
   public void afterActivationFired(AfterActivationFiredEvent event) {
       super.afterActivationFired( event );
       System.out.println( event );
   }
});

11.13. 作業メモリーイベントの印刷

このコードを使用すると、リスナーを追加することで、すべての作業メモリーイベントを出力できます。
ksession.addEventListener( new DebugWorkingMemoryEventListener() );

11.14. KnowlegeRuntimeEvents

出力されたすべてのイベントは KnowlegeRuntimeEvent インターフェイスを実装し、イベントの発信元の実際の KnowlegeRuntime の取得に使用できます。

11.15. KnowledgeRuntimeEvent インターフェイスでサポートされるイベント

現在サポートされているイベントは以下のとおりです。
  • ActivationCreatedEvent
  • ActivationCancelledEvent
  • BeforeActivationFiredEvent
  • AfterActivationFiredEvent
  • AgendaGroupPushedEvent
  • AgendaGroupPoppedEvent
  • ObjectInsertEvent
  • ObjectRetractedEvent
  • ObjectUpdatedEvent
  • ProcessCompletedEvent
  • ProcessNodeLeftEvent
  • ProcessNodeTriggeredEvent
  • ProcessStartEvent

11.16. The KnowledgeRuntimeLogger

KnowledgeRuntimeLogger は、JBoss ルールの包括的なイベントシステムを使用して、Eclipse 監査ビューアーなどのツールを使用して、後で検査するためにアプリケーションの実行をログに記録するのに使用できる監査ログを作成します。

11.17. Enabling a FileLogger

FileLogger がファイルを追跡できるようにするには、以下のコードを使用します。
KnowledgeRuntimeLogger logger =
  KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "logdir/mylogfile");
...
logger.close();

11.18. JBoss ルールでの StatelessKnowledgeSession の使用

StatelessKnowledgeSession は、拡張せずに StatefulKnowledgeSession をラップします。その主なことは、デシジョンサービスタイプのシナリオに焦点を当てています。これにより、dispose() を呼び出す必要がなくなります。ステートレスセッションは、Java コードからの反復挿入とメソッド呼び出し fireAllRules() をサポートしません。execute() を呼び出す動作は、StatefulKnowledgeSession を内部にインスタンス化し、すべてのユーザーデータを追加してユーザーコマンドを実行し、fireAllRules() を呼び出してから、単一のショットメソッドです。dispose()このクラスを使用する主な方法は、BatchExecution ( Commandインターフェイスでサポートされるサブインターフェイス)を使用することですが、単純なオブジェクト挿入のみが必要な場合には、2 つの便利なメソッドが提供されます。CommandExecutorCommandExecutor および BatchExecution は、独自のセクションで詳しく話されています。

11.19. コレクションを使用した StatelessKnowledgeSession の実行

これは、コレクションで StatelessKnowledgeSession 実行を実行するためのコードです。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newFileSystemResource( fileName ), ResourceType.DRL );
if (kbuilder.hasErrors() ) {
    System.out.println( kbuilder.getErrors() );
} else {
    KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
    kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
    StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
    ksession.execute( collection );
}

11.20. InsertElements コマンドを使用した StatelessKnowledgeSession の実行

これは、InsertElements コマンドで StatelessKnowledgeSession 実行を実行するためのコードです。
ksession.execute( CommandFactory.newInsertElements( collection ) );
注記
コレクションとその個々の要素を挿入するには、CommandFactory.newInsert(collection) を使用します。

11.21. BatchExecutionHelper

CommandFactory のメソッドでは、サポートされるコマンドを作成します。これらはすべて XStream と BatchExecutionHelper を使用してマーシャリングできます。BatchExecutionHelper XML 形式の詳細と、JBoss Rules Pipeline を使用して BatchExecution および ExecutionResults のマーシャリングを自動化する方法を説明します。

11.22. CommandExecutor インターフェイス

CommandExecutor インターフェイスでは、ユーザーは out パラメーターを使用してデータをエクスポートできます。つまり、挿入されたファクト、グローバル、およびクエリーの結果はすべて、このインターフェイスを使用して返されることができます。

11.23. Out Identifiers

以下は、除外識別子の例です。
// Set up a list of commands
List cmds = new ArrayList();
cmds.add( CommandFactory.newSetGlobal( "list1", new ArrayList(), true ) );
cmds.add( CommandFactory.newInsert( new Person( "jon", 102 ), "person" ) );
cmds.add( CommandFactory.newQuery( "Get People" "getPeople" );

// Execute the list
ExecutionResults results =
  ksession.execute( CommandFactory.newBatchExecution( cmds ) );

// Retrieve the ArrayList
results.getValue( "list1" );
// Retrieve the inserted Person fact
results.getValue( "person" );
// Retrieve the query as a QueryResults instance.
results.getValue( "Get People" );

第12章 モードとメソッド

12.1. 順次モード

JBoss Rules で 順次モード を使用すると、よりシンプルな方法でエンジンを使用できます。これにより、ユーザーがステートレスセッションを使用していて、最初のデータセットの後にデータをアサートしたり変更したりできない場合に、ルールを再評価せずに使用できます。

12.2. 順次モードオプション

以下は、順次モードを使用する際に選択できるオプションの一部です。
  1. ルールセットの顕著性および位置(ルールの端末ノードに sequence 属性を設定して)ルールの順序を設定します。
  2. 配列を作成します(可能なルールのアクティベーションごとに 1 つの要素)。要素の位置は、実行順序を示します。
  3. right-input オブジェクトメモリーを除く、すべてのノードメモリーをオフにします。
  4. 左入力アダプターノードの伝播を切断し、オブジェクトとノードを Command オブジェクトで参照できるようにします。これは、後で実行するために作業メモリーのリストに追加されます。
  5. すべてのオブジェクトをアサートします。すべてのアサーションが終了し、right-input ノードメモリーが設定されると、Command リストを確認して、それぞれを順番に実行できます。
  6. 結果となるすべてのアクティベーションは、ルールで決定されたシーケンス番号に基づいてアレイに配置する必要があります。反復範囲を減らすために、最初と最後に生成された要素を記録します。
  7. Activations の配列を繰り返し、入力された要素を順番に実行します。
  8. 許可されるルール実行の最大数がある場合は、そのアレイ内のすべてのルールを実行する早い段階でネットワーク評価を終了します。

12.3. 順次モードのアクティブ化

手順12.1 タスク

  1. ステートレスセッションを開始します。
  2. 順次モードは、デフォルトではオフになります。これを有効にするには、RuleBaseConfiguration.setSequential(true) を呼び出します。または、ルールベース設定プロパティー drools.sequential を true に設定します。
  3. 順次モードが動的アジェンダにフォールバックできるようにするには、SequentialAgenda.DYNAMICsetSequentialAgenda を呼び出します。
  4. オプションで、JBossRules.sequential.agenda プロパティーを sequential または dynamic に設定します。

12.4. CommandFactory

CommandFactory オブジェクトを使用すると、ステートレスセッションでコマンドを実行できます。これで、ファクトリーはセッションを破棄する前に fireAllRules() を実行します。

12.5. サポートされる CommandFactory オプション

これらのオプションはすべて CommandFactory と互換性があります。
  • FireAllRules
  • GetGlobal
  • SetGlobal
  • InsertObject
  • InsertElements
  • Query
  • StartProcess
  • batchExecution

12.6. Insert コマンド

InsertObject オプションの out 識別子を持つ単一のオブジェクトを挿入します。InsertElements 各要素を挿入する Iterable を繰り返し処理します。これにより、ステートレスナレッジセッションはクエリーを任意の順序で処理または実行できます。

12.7. コマンドの挿入例

StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
ExecutionResults bresults =
  ksession.execute( CommandFactory.newInsert( new Car( "sedan" ), "sedan_id" ) );
Sedan sedan = bresults.getValue( "sedan_id" );

12.8. 実行方法

execute メソッドは、一度に 1 つずつコマンドを実行するために使用されます。これは常に ExecutionResults インスタンスを返します。これにより、stilton_id などの out 識別子を指定すると、任意のコマンド結果にアクセスできるようになります。

12.9. 実行方法の例

StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
Command cmd = CommandFactory.newInsertElements( Arrays.asList( Object[] { 
                  new Car( "sedan" ),
                  new Car( "hatchback" ),
                  new Car( "convertible" ),
              });
ExecutionResults bresults = ksession.execute( cmd );

12.10. BatchExecution コマンド

BatchExecution コマンドを使用すると、複数のコマンドを一度に実行できます。これは、コマンドの一覧から作成される複合コマンドを表します。を実行すると一覧を繰り返し処理し、各コマンドを順次実行します。つまり、一部のオブジェクトを挿入し、プロセスを開始し、fireAllRules を呼び出してクエリーを実行することができます(すべて 1 つの execute(...) 呼び出し)。

12.11. The FireAllRules Command

FireAllRules コマンドは、最後にルールの自動実行を無効にします。これは、手動のオーバーライド機能の一種です。

12.12. Out Identifiers

コマンドは、out identifier をサポートします。out 識別子が設定されているコマンドは、返される ExecutionResults インスタンスに結果を追加します。

12.13. アウト識別子の例

この例では、BatchExecution コマンドを使用して、識別子を外す仕組みを示しています。
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();

List cmds = new ArrayList();        
cmds.add( CommandFactory.newInsertObject( new Car( "red", 1), "red") );
cmds.add( CommandFactory.newStartProcess( "process cars" ) );
cmds.add( CommandFactory.newQuery( "cars" ) );
ExecutionResults bresults = ksession.execute( CommandFactory.newBatchExecution( cmds ) );
Car red = ( Car ) bresults.getValue( "red" );
QueryResults qresults = ( QueryResults ) bresults.getValue( "cars" );
上記の例では、複数のコマンドが実行され、そのうちの 2 つが ExecutionResults にデータを投入しています。query コマンドはデフォルトでクエリー名と同じ識別子を使用しますが、別の識別子にマップすることもできます。

12.14. 実行 XML の例

カスタム XStream マーシャリングは、JBoss Rules Pipeline で使用して XML スクリプトを実現できます。これはサービスに適しています。以下に 2 つの例を示します。
BatchExecution XML:
<batch-execution>
   <insert out-identifier='outRed'>
      <org.drools.Car>
         <type>red</type>
         <price>25000</price>
         <oldPrice>0</oldPrice>
      </org.drools.Car>
   </insert>
</batch-execution>
ExecutionResults XML:
<execution-results>
   <result identifier='outBlue'>
      <org.drools.Car>
         <type>Blue</type>
         <oldPrice>25</oldPrice>        
         <price>30000</price>
      </org.drools.Car>
   </result>
</execution-results>

12.15. 実行マーシャリングの例

これは、XML にマーシャリングされる BatchExecution の例です。
<batch-execution>
  <insert out-identifier="sedan">
    <org.drools.Car>
      <type>sedan</type>
      <price>1</price>
      <oldPrice>0</oldPrice>
    </org.drools.Car>
  </insert>
  <query out-identifier='cars2' name='carsWithParams'>
    <string>hatchback</string>
    <string>sedan</string>
  </query>
</batch-execution>
CommandExecutorExecutionResults を返します。これはパイプラインコードスニペットでも処理されます。上記の <batch-execution> XML サンプルと同様の出力は以下のようになります。
<execution-results>
  <result identifier="sedan">
    <org.drools.Car>
      <type>sedan</type>
      <price>2</price>
    </org.drools.Car>
  </result>        
  <result identifier='cars2'>
    <query-results>
      <identifiers>
        <identifier>car</identifier>
      </identifiers>
      <row>
        <org.drools.Car>
          <type>hatchback</type>
          <price>2</price>
          <oldPrice>0</oldPrice>
        </org.drools.Car>
      </row>
      <row>
        <org.drools.Car>
          <type>hatchback</type>
          <price>1</price>
          <oldPrice>0</oldPrice>
        </org.drools.Car>
      </row>
    </query-results>
  </result>
</execution-results>

12.16. batch-execution とコマンドの例

  1. 現在、スキーマ検証をサポートする XML スキーマはありません。これは基本的な形式です。ルート要素は <batch-execution> で、これには 0 個以上の commands 要素を含めることができます。
    <batch-execution>
    ...
    </batch-execution>
    
  2. insert 要素は "out-identifier" 属性を特長とし、挿入されたオブジェクトは結果ペイロードの一部として返されます。
    <batch-execution>
       <insert out-identifier='userVar'>
          ...
       </insert>
    </batch-execution>
    
  3. <insert-elements> 要素を使用してオブジェクトのコレクションを挿入することもできます。このコマンドは out-identifier をサポートしません。org.domain.UserClass は、XStream がシリアライズするユーザーオブジェクトです。
    <batch-execution>
       <insert-elements>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </insert-elements>
    </batch-execution>
    
  4. <set-global> 要素は、セッションのグローバルを設定します。
    <batch-execution>
       <set-global identifier='userVar'>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </set-global>
    </batch-execution>
    
  5. <set-global> は、outout-identifier の 2 つのオプション属性もサポートします。ブール値の true 値は、識別子 属性からの名前を使用して、グローバルを <batch-execution-results> ペイロードに追加します。out-identifierout のように機能しますが、さらに <batch-execution-results> ペイロードで使用される識別子を上書きできます。
    <batch-execution>
       <set-global identifier='userVar1' out='true'>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </set-global>
       <set-global identifier='userVar2' out-identifier='alternativeUserVar2'>
          <org.domain.UserClass>
             ...
          </org.domain.UserClass>
       </set-global>
    </batch-execution>
    
  6. コンテンツのない <get-global> 要素があります。out-identifier 属性のみがあります。値の取得は <get-global> 要素の唯一の目的であるため、out 属性は必要ありません。
    <batch-execution>
       <get-global identifier='userVar1' />
       <get-global identifier='userVar2' out-identifier='alternativeUserVar2'/>
    </batch-execution>
    
  7. query コマンドは、パラメータークエリーとパラメーターレスクエリーの両方をサポートします。name 属性は呼び出されるクエリーの名前で、out-identifier はクエリーに使用される識別子で、<execution-results> ペイロードになります。
    <batch-execution>
       <query out-identifier='cars' name='cars'/>
       <query out-identifier='cars2' name='carsWithParams'>
          <string>red</string>
          <string>blue</string>
       </query>
    </batch-execution>
    
  8. <start-process> コマンドは、任意のパラメーターを受け入れます。
    <batch-execution>
       <startProcess processId='org.drools.actions'>
          <parameter identifier='person'>
             <org.drools.TestVariable>
                <name>John Doe</name>
             </org.drools.TestVariable>
          </parameter>
       </startProcess>
    </batch-execution
    
  9. signal event コマンドを使用すると、プロセスを特定できます。
    <signal-event process-instance-id='1' event-type='MyEvent'>
       <string>MyValue</string>
    </signal-event>
    
  10. 完全なワークアイテムコマンドは、プロセスが完了したときにユーザーに通知します。
    <complete-work-item id='" + workItem.getId() + "' >
       <result identifier='Result'>
          <string>SomeOtherString</string>
       </result>
    </complete-work-item>
    
  11. abort work item コマンドを使用すると、実行中にプロセスをキャンセルできます。
    <abort-work-item id='21' />
    

12.17. The MarshallerFactory

MarshallerFactory は、ステートフルセッションをマーシャリングおよびアンマーシャリングするために使用されます。

12.18. マーシャラーの例

これは、marsheller の実際の例になります。
// ksession is the StatefulKnowledgeSession
// kbase is the KnowledgeBase
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = MarshallerFactory.newMarshaller( kbase );
marshaller.marshall( baos, ksession );
baos.close();

12.19. マーシャリングオプション

表12.1 ** テーブルタイトル **

オプション Description
ObjectMarshallingStrategy このインターフェイスはマーシャリングの実装を提供し、柔軟性を高めることができます。
SerializeMarshallingStrategy
これは、ユーザーインスタンスで Serializable または Externalizable メソッドを呼び出すデフォルトのストラテジーです。
IdentityMarshallingStrategy
このストラテジーは、各ユーザーオブジェクトの整数 ID を作成し、id がストリームに書き込まれる間に Map に保存します。
マーシャリングを解除すると、IdentityMarshallingStrategy マップにアクセスしてインスタンスを取得します。つまり、IdentityMarshallingStrategy を使用する場合、これは Marshaller インスタンスの有効期間間ステートフルであり、ID を作成し、マーシャリングしようとするすべてのオブジェクトへの参照を保持することを意味します。

12.20. IdentityMarshallingStrategy の例

これは、IdentityMarshallingStrategy を使用するコードです。
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMarshallingStrategy oms = MarshallerFactory.newIdentityMarshallingStrategy()
Marshaller marshaller =
  MarshallerFactory.newMarshaller( kbase, new ObjectMarshallingStrategy[]{ oms } );
marshaller.marshall( baos, ksession );
baos.close();

12.21. The ObjectMarshallingStrategyAcceptor

ObjectMarshallingStrategyAcceptor は、各オブジェクトマーシャリングストラテジーに含まれるインターフェイスです。Marshaller にはストラテジーのチェーンがあります。ユーザーオブジェクトの読み取りまたは書き込みを試行すると、ObjectMarshallingStrategyAcceptor を使用して、ユーザーオブジェクトのマーシャリングに使用するかどうかを決定します。

12.22. ClassFilterAcceptor 実装

ClassFilterAcceptor 実装を使用すると、文字列とワイルドカードを使用してクラス名を照合できます。デフォルトは *.*" です。

12.23. アクセプターを使用した IdentityMarshallingStrategy

これは、アクセプターで IdentityMarshallingStrategy を使用する例です。受け入れチェックの順序は、提供されたアレイの自然順序であることに注意してください。
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMarshallingStrategyAcceptor identityAcceptor =
  MarshallerFactory.newClassFilterAcceptor( new String[] { "org.domain.pkg1.*" } );
ObjectMarshallingStrategy identityStrategy =
  MarshallerFactory.newIdentityMarshallingStrategy( identityAcceptor );
ObjectMarshallingStrategy sms = MarshallerFactory.newSerializeMarshallingStrategy();
Marshaller marshaller =
  MarshallerFactory.newMarshaller( kbase,
                                   new ObjectMarshallingStrategy[]{ identityStrategy, sms } );
marshaller.marshall( baos, ksession );
baos.close();

12.24. JBoss ルールの永続性およびトランザクション

JBoss ルールでは、Java Persistence API (JPA)を使用した追加設定なしの永続化が可能です。Java Transaction API (JTA)の実装をインストールする必要があります。開発の目的で、Bitronix トランザクションマネージャーを使用できます。実稼働環境での使用には、JBoss Transactions が推奨されます。

12.25. トランザクションの例

トランザクションを実行する内容は以下のようになります。
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY,
         Persistence.createEntityManagerFactory( "emf-name" ) );
env.set( EnvironmentName.TRANSACTION_MANAGER,
         TransactionManagerServices.getTransactionManager() );
          
// KnowledgeSessionConfiguration may be null, and a default will be used
StatefulKnowledgeSession ksession =
  JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );
int sessionId = ksession.getId();
 
UserTransaction ut =
  (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" );
ut.begin();
ksession.insert( data1 );
ksession.insert( data2 );
ksession.startProcess( "process1" );
ut.commit();

12.26. JPA の使用

手順12.2 タスク

  1. 環境が EntityManagerFactoryTransactionManager の両方で設定されていることを確認してください。
  2. GUI またはコマンドラインから JPA を起動します。
  3. id を使用して、以前に永続化したステートフルナレッジセッションをロードします。ロールバックが発生すると ksession 状態もロールバックされ、ロールバック後も引き続き使用できます。

12.27. JPA を使用した StatefulKnowledgeSession の読み込み

これは、JPA を実装する StatefulKnowledgeSession を読み込むコードです。
StatefulKnowledgeSession ksession =
  JPAKnowledgeService.loadStatefulKnowledgeSession( sessionId, kbase, null, env );

12.28. JPA の設定

永続を有効にするには、以下の例のように複数のクラスを persistence.xml に追加する必要があります。
<persistence-unit name="org.drools.persistence.jpa" transaction-type="JTA">
   <provider>org.hibernate.ejb.HibernatePersistence</provider>
   <jta-data-source>jdbc/BitronixJTADataSource</jta-data-source>       
   <class>org.drools.persistence.session.SessionInfo</class>
   <class>org.drools.persistence.processinstance.ProcessInstanceInfo</class>
   <class>org.drools.persistence.processinstance.ProcessInstanceEventInfo</class>
   <class>org.drools.persistence.processinstance.WorkItemInfo</class>
   <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>            
         <property name="hibernate.max_fetch_depth" value="3"/>
         <property name="hibernate.hbm2ddl.auto" value="update" />
         <property name="hibernate.show_sql" value="true" />
         <property name="hibernate.transaction.manager_lookup_class"
                      value="org.hibernate.transaction.BTMTransactionManagerLookup" />
   </properties>
</persistence-unit>

12.29. JTA DataSource の設定

これは、JTA DataSource を設定するためのコードです。
PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName( "jdbc/BitronixJTADataSource" );
ds.setClassName( "org.h2.jdbcx.JdbcDataSource" );
ds.setMaxPoolSize( 3 );
ds.setAllowLocalTransactions( true );
ds.getDriverProperties().put( "user", "sa" );
ds.getDriverProperties().put( "password", "sasa" );
ds.getDriverProperties().put( "URL", "jdbc:h2:mem:mydb" );
ds.init();

12.30. JNDI プロパティー

また、Bitronix は、テストに適していますが、単純な埋め込み JNDI サービスも提供します。使用するには、jndi.properties ファイルを META-INF に追加し、以下の行を追加します。
java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory

12.31. ナレッジベースの名前空間

これは、ビルド目的で KnowledgeBase に割り当てることのできる namespace の一覧です。
  • deftemplate
  • defrule
  • deffunction
  • and/or/not/exists/test 条件要素
  • リテラル、変数、戻り値、および述語フィールドの制約

第13章 スプレッドシート形式のデシジョンテーブルの使用

13.1. ハードキーワード

ハードキーワード は、ルールテキストで使用されるドメインオブジェクト、プロパティー、メソッド、関数、およびその他の要素に名前を付けるときに使用できない単語です。これは、truefalse などの単語で、コマンドに対して間違いがある可能性があります。

13.2. ソフトキーワード

ソフトキーワード は、ドメインオブジェクト、プロパティー、メソッド、関数、およびその他の要素の命名に使用できます。ルールエンジンはコンテキストを認識し、それに応じて処理します。

13.3. ソフトキーワードの一覧

  • lock-on-active
  • date-effective
  • date-expires
  • no-loop
  • auto-focus
  • activation-group
  • agenda-group
  • ruleflow-group
  • entry-point
  • duration
  • package
  • import
  • dialect
  • salience
  • enabled
  • attributes
  • rule
  • extend
  • when
  • 次に、以下を実行します。
  • template
  • query
  • declare
  • function
  • global
  • eval
  • not
  • in
  • または
  • および
  • exists
  • forall
  • accumulate
  • collect
  • from
  • action
  • reverse
  • result
  • end
  • over
  • init

13.4. コメント

コメントは、ルールエンジンが無視するテキストのセクションです。これらはセマンティックコードブロック内(ルールの RHS など)を除き、それらが検出されると削除されます。

13.5. 単一行コメントの例

これは、単一行のコメントです。単一行のコメントを作成するには、'//' を使用します。パーサーは、コメント記号の後にある行を無視します。
rule "Testing Comments"
when
    // this is a single line comment
    eval( true ) // this is a comment in the same line of a pattern
then
    // this is a comment inside a semantic code block
end

13.6. 複数行のコメントの例

複数行のコメントは次のようになります。この設定は、テキストのブロック(インおよび外部のセマンティックコードブロックの両方)をコメントアウトします。
rule "Test Multi-line Comments"
when
    /* this is a multi-line comment
       in the left hand side of a rule */
    eval( true )
then
    /* and this is a multi-line comment
       in the right hand side of a rule */
end

13.7. エラーメッセージ

101: no viable alternative
パーサーが決定ポイントになり、代替を特定できなかったことを示します。
[ERR 101] Line 3:2 no viable alternative at input 'WHEN'
このメッセージは、ルール名がないため、パーサーが間違った場所にあるトークン WHEN (ハードキーワード)が発生したことを意味します。
[ERR 101] Line 0:-1 no viable alternative at input '<eof>' in rule simple_rule in pattern [name]
オープン引用符、アポストロフィ、または括弧を示します。
102: mismatched input
パーサーが特定のシンボルを検索していた場合、これが現在の入力位置で終了していないことを示します。
[ERR 102] Line 0:-1 mismatched input '<eof>' expecting ')' in rule simple_rule in pattern [name]
このエラーは、ルールステートメントが不完全の結果です。通常、0:-1 の位置を取得すると、パーサーがソースの最後に到達したことを意味します。
103: failed predicate
セマンティック述語の検証が false と評価されました。通常、これらのセマンティック述語はソフトキーワードを識別するために使用されます。
104: trailing semi-colon not allowed
このエラーは、式がセミコロンで終了できない eval 句に関連付けられています。
105: early Exit
アバイザーは文法のサブルールになり、少なくとも 1 回は代替の選択肢に一致する必要がありますが、サブルールはいずれにも一致しませんでした。

13.8. パッケージ

パッケージ は、インポートやグローバルなど、ルールおよびその他の関連するコンストラクトのコレクションです。パッケージメンバーは通常、HR ルールなど、相互に関連します。パッケージは namespace を表します。これは、特定のルールグループに対して一意に保たれます。パッケージ名自体は namespace であり、いずれの方法でもファイルやフォルダーとは関係ありません。
複数のルールソースからルールをアセンブルし、すべてのルールが(ルールがアセンブルされると)すべてのルールが保持されるトップレベルのパッケージ設定を 1 つ設定できます。異なる名前で宣言された同じパッケージリソースにマージすることはできません。ただし、1 つの Rulebase には、それ上に構築される複数のパッケージが含まれる場合があります。一般的な構造は、パッケージ宣言と同じファイル内にパッケージの全ルールを持つことです(これにより、完全に自己完結型になります)。

13.9. インポートステートメント

インポートステートメント は Java で import ステートメントのように機能します。ルールで使用するオブジェクトの完全修飾パスおよびタイプ名を指定する必要があります。JBoss Rules は、同じ名前の Java パッケージからクラスを自動的にインポートし、パッケージ java.lang からもインポートします。

13.10. グローバルの使用

グローバルを使用するには、以下を行う必要があります。
  1. ルールファイルでグローバル変数を宣言し、ルールで使用します。以下に例を示します。
    global java.util.List myGlobalList;
    
    rule "Using a global"
    when
        eval( true )
    then
        myGlobalList.add( "Hello World" );
    end
    
  2. ワーキングメモリーにグローバル値を設定します。ファクトをワーキングメモリーにアサートする前に、グローバル値をすべて設定することが推奨されます。以下に例を示します。
    List list = new ArrayList();
    WorkingMemory wm = rulebase.newStatefulSession();
    wm.setGlobal( "myGlobalList", list );
    

13.11. From 要素

from 要素を使用すると、Hibernate セッションをグローバルとして渡すことができます。また、名前付きの Hibernate クエリーからデータをプルすることもできます。

13.12. 電子メールサービスでのグローバルの使用

手順13.1 タスク

  1. ルールエンジンを呼び出す統合コードを開きます。
  2. emailService オブジェクトを取得してから、作業メモリーに設定します。
  3. DRL では、emailService タイプのグローバルがあり、名前が email であることを宣言します。
  4. ルールの結果では、email.sendSMS (number, message)などのものを使用できます。
    警告
    グローバルはルール間でデータを共有するように設計されていないため、その目的に使用しないでください。ルールは常に、ワーキングメモリーの状態に関して推論し、これに対応するため、ルールからルールにデータを渡す場合は、データをファクトとしてワーキングメモリーにアサートします。
    重要
    ルール内からグローバル値を設定または変更しないでください。ワーキングメモリーインターフェイスを使用して、アプリケーションから値を常に設定することが推奨されます。

第14章 関数

14.1. 関数

関数 は、通常の Java クラスではなく、ルールのソースファイルにセマンティックコードを配置する方法です。ルールで関数を使用する主な利点は、ロジックをすべて 1 つの場所に保持できることです。必要に応じて関数を変更できます。
関数は、特に特定のアクションが繰り返し使用される場合に、ルールの結果(then)部分に対してアクションを呼び出すのに最も役立ちます。

14.2. 関数宣言の例

一般的な関数宣言は以下のようになります。
function String hello(String name) {
    return "Hello "+name+"!";
}
注記
function キーワードは、技術的に Java の一部ではない場合でも、使用されることに注意してください。関数へのパラメーターは、メソッドの として定義されます。パラメーターが必要ない場合は指定する必要はありません。戻り値の型は、通常のメソッドと同様に定義されます。

14.3. 静的メソッドによる関数宣言の例

この関数宣言の例は、ヘルパークラス(Foo.hello() )の static メソッドを示しています。JBoss Rules は関数インポートの使用をサポートするため、以下のコードのみを入力する必要があります。
import function my.package.Foo.hello

14.4. 関数宣言の例の呼び出し

関数が定義またはインポートされる方法に関係なく、結果またはセマンティックコードブロック内で呼び出して関数を使用します。以下に例を示します。
rule "using a static function"
when 
    eval( true )
then
    System.out.println( hello( "Bob" ) );
end

14.5. 型宣言

タイプ宣言 には、新しいタイプの宣言を許可し、タイプのメタデータの宣言を許可するという 2 つの主要なゴールがあります。

14.6. タイプ宣言ロール

表14.1 ** テーブルタイトル **

Role Description
新しいタイプの宣言
プレーンな Java オブジェクトをファクトとして使用し、すぐに使用できる JBoss ルール。ただし、ユーザーがルールエンジンに直接モデルを定義する場合は、新しいタイプを宣言することで実行できます。これは、すでにビルドされているドメインモデルがある場合にも使用できますが、ユーザーはこのモデルを、理由処理中に主に使用する追加のエンティティーで補完します。
メタデータの宣言
ファクトにはメタ情報が関連付けられている可能性があります。メタデータ情報の例には、ファクト属性で表現されず、そのファクトタイプのすべてのインスタンス間で一貫性のあるあらゆる種類のデータが含まれます。このメタ情報はエンジンによってランタイム時にクエリーされ、推論(reasoning)プロセスで使用できます。

14.7. 新規タイプの宣言

新しいタイプを宣言するには、キーワード declare が使用され、フィールドの一覧とキーワード end が使用されます。新しいファクトにはフィールドの一覧が必要です。そうしないと、エンジンはクラスパスで既存のファクトクラスを検索し、見つからない場合はエラーを発生させます。

14.8. 新しいファクトタイプの例の宣言

この例では、Address という新しいファクトタイプが使用されます。このファクトタイプには、numberstreetName、および city の 3 つの属性があります。各属性には、ユーザーが作成した他のクラスや以前に宣言した他のクラスなど、有効な Java タイプであることができるタイプがあります。
declare Address
   number : int
   streetName : String
   city : String
end

14.9. 新しいファクトタイプの追加例を宣言する

このファクトタイプの宣言は、Person の例を使用します。dateOfBirth java.util.Date 型(Java API からの)で、address はファクトタイプ Address です。
declare Person
    name : String
    dateOfBirth : java.util.Date
    address : Address
end

14.10. インポート例の使用

以下の例は、import 機能を使用する方法を示しています。これにより、完全修飾クラス名を使用する必要がなくなります。
import java.util.Date

declare Person
    name : String
    dateOfBirth : Date
    address : Address
end

14.11. 生成された Java クラス

新しいファクトタイプを宣言すると、JBoss Rules はファクトタイプを表す Java クラスを実装するバイトコードを生成します。生成された Java クラスは、型定義の 1 対 1 の Java Bean マッピングになります。

14.12. 生成された Java クラスの例

以下は、Person ファクトタイプを使用して生成された Java クラスの例です。
public class Person implements Serializable {
    private String name;
    private java.util.Date dateOfBirth;
    private Address address;

    // empty constructor
    public Person() {...}

    // constructor with all fields 
    public Person( String name, Date dateOfBirth, Address address ) {...}

    // if keys are defined, constructor with keys
    public Person( ...keys... ) {...}

    // getters and setters
    // equals/hashCode
    // toString
}

14.13. ルールでの宣言タイプの使用例

生成されたクラスは単純な Java クラスであるため、他のファクトと同様にルールで透過的に使用できます。
rule "Using a declared Type"
when 
    $p : Person( name == "Bob" )
then
    // Insert Mark, who is Bob's manager.
    Person mark = new Person();
    mark.setName("Mark");
    insert( mark );
end

14.14. メタデータの宣言

JBoss ルールでは、ファクトタイプ、ファクト属性、およびルールなど、複数の異なる構造にメタデータを割り当てることができます。JBoss ルールは at 記号('@')を使用してメタデータを導入し、常に以下の形式を使用します。
@metadata_key( metadata_value )
括弧化された metadata_value は任意です。

14.15. メタデータ属性の操作

JBoss ルールは任意のメタデータ属性の宣言を許可しますが、エンジンに特別な意味を持つものもあれば、実行時にクエリーに使用できるものもあります。JBoss ルールでは、ファクトタイプとファクト属性の両方のメタデータの宣言が可能です。ファクトタイプの属性の前に宣言されたメタデータはファクトタイプに割り当てられ、属性の後に宣言されたメタデータはその特定の属性に割り当てられます。

14.16. ファクトタイプを使用したメタデータ属性の宣言例

これは、ファクトタイプおよび属性のメタデータ属性を宣言する例です。ファクトタイプ(@author および @dateOfCreation)に宣言された 2 つのメタデータ項目があり、name 属性(@key および @maxLength)用にさらに 2 つの定義があります。@key メタデータには必須の値がないため、括弧と値は省略されています。
import java.util.Date

declare Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )

    name : String @key @maxLength( 30 )
    dateOfBirth : Date 
    address : Address
end

14.17. @position 属性

@position 属性を使用すると、フィールドの位置を宣言し、デフォルトの宣言した順序を上書きすることができます。これは、パターンの位置制約に使用されます。

14.18. @position の例

@position 属性が使用される内容になります。
declare Cheese
    name : String @position(1)
    shop : String @position(2)
    price : int @position(0)
end

14.19. 事前定義されたクラスレベルのアノテーション

表14.2 事前定義されたクラスレベルのアノテーション

Annotation Description
@role( <fact | event> )
この属性は、ロールをファクトおよびイベントに割り当てるために使用できます。
@typesafe( <boolean> )
デフォルトでは、すべてのタイプ宣言は型安全性が有効な状態でコンパイルされます。@typesafe( false ) フォールバックを許可し、すべての制約が MVEL 制約として生成され、動的に実行される安全でない評価のタイプを許可することにより、この動作を上書きする手段を提供します。これは、一般的なコレクションではない場合や、タイプが混同されているコレクションを処理する場合に便利です。
@timestamp( <attribute name> )
タイムスタンプを作成します。
@duration( <attribute name> )
属性の実装の期間を設定します。
@expires( <time interval> )
属性の有効期限が切れるタイミングを定義できます。
@propertyChangeSupport
Javabean 仕様に定義されているプロパティー変更のサポートを実装するファクトにアノテーションを付け、エンジンがファクトプロパティーの変更をリッスンするように登録できるようになりました。
@propertyReactive type プロパティーをリアクティブにします。

14.20. @Key 属性関数

属性をキー属性として宣言すると、生成されたタイプに 2 つの大きな影響があります。
  1. 属性はタイプのキー識別子として使用されるため、このタイプのインスタンスを比較するときに、生成されたクラスは 属性を考慮に入れて equals ()メソッドおよび hashCode ()メソッドを実装します。
  2. JBoss Rules は、すべてのキー属性をパラメーターとして使用してコンストラクターを生成します。

14.21. @Key 宣言の例

これは、タイプの @key 宣言の例です。JBoss ルールは equals ()メソッドと hashCode ()メソッドを生成し、firstName 属性と lastName 属性をチェックして、Person の 2 つのインスタンスが同等であるかどうかを判断します。age 属性はチェックしません。また、firstName と lastName をパラメーターとして取るコンストラクターも生成します。
declare Person
    firstName : String @key
    lastName : String @key
    age : int
end

14.22. キーアーキテクトの例を使用したインスタンスの作成

これは、キーコンストラクターを使用してインスタンスを作成する内容になります。
Person person = new Person( "John", "Doe" );

14.23. 位置引数

パターンは、タイプ宣言での位置引数をサポートし、@position 属性で定義されます。
位置引数は、位置が既知の名前付きフィールドにマッピングされるため、フィールド名を指定する必要はありません。(つまり、Person (name == "mark")は Person ("mark";)として書き換えることができます。 セミコロン ; は重要です。これにより、エンジンは位置引数である前のすべてを認識します。パターンで位置引数と名前付き引数を混在させるには、セミコロン ';' を使用して区切ります。バインドされていない位置で使用される変数は、その位置にマップするフィールドにバインドされます。

14.24. 位置引数の例

以下の例を確認してください。
declare Cheese
    name : String
    shop : String
    price : int
end
デフォルトの順序は宣言された順序ですが、@position を使用して上書きできます。
declare Cheese
    name : String @position(1)
    shop : String @position(2)
    price : int @position(0)
end

14.25. @Position アノテーション

@Position アノテーションを使用して、クラスパスの元の pojos にアノテーションを付けることができます。現在、アノテーションを付けることができるのはクラスのフィールドのみです。クラスの継承はサポートされますが、メソッドのインターフェイスはサポートされません。

14.26. パターンの例

これらのパターンの例には、2 つの制約とバインディングがあります。セミコロン ; は、位置セクションと名前付き引数セクションを区別するために使用されます。位置引数では、変数、リテラル、およびリテラルのみを使用する式がサポートされますが、変数はサポートされません。
Cheese( "stilton", "Cheese Shop", p; )
Cheese( "stilton", "Cheese Shop"; p : price )
Cheese( "stilton"; shop == "Cheese Shop", p : price )
Cheese( name == "stilton"; shop == "Cheese Shop", p : price )

第15章 追加の宣言

15.1. 既存タイプのメタデータの宣言

JBoss ルールを使用すると、新しいファクトタイプのメタデータ属性を宣言する場合と同じ方法で、既存のタイプのメタデータ属性の宣言が可能になります。唯一の違いは、その宣言にフィールドがない点です。

15.2. 既存タイプのメタデータの宣言例

以下の例は、既存タイプのメタデータを宣言する方法を示しています。
import org.drools.examples.Person

declare Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )
end

15.3. 完全修飾クラス名を使用したメタデータの宣言例

この例は、インポートアノテーションを使用する代わりに完全修飾クラス名を使用してメタデータを宣言する方法を示しています。
declare org.drools.examples.Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )
end

15.4. 宣言タイプの例用のパラメーター化されたコンストラクター

以下のような宣言タイプの場合:
declare Person
    firstName : String @key
    lastName : String @key
    age : int
end
コンパイラーは暗黙的に 3 つのコンストラクターを生成します。1 つはパラメーターなし、もう 1 つは @key フィールドのあるコンストラクター、もう 1 つはすべてのフィールドを持つコンストラクターです。
Person() // parameterless constructor
Person( String firstName, String lastName )
Person( String firstName, String lastName, int age )

15.5. 非タイプセーフクラス

タイプ宣言に @typesafe (<boolean>)アノテーションが追加されました。デフォルトでは、すべてのタイプ宣言は型安全性が有効な状態でコンパイルされます。@typesafe (false)は、フォールバックを許可し、すべての制約が MVEL 制約として生成され、動的に実行される安全でない評価を入力できるようにすることでこの動作を上書きする手段を提供します。これは、一般的なコレクションではない場合や、タイプが混同されているコレクションを処理する場合に便利です。

15.6. アプリケーションコードから宣言タイプへのアクセス

アプリケーションは、宣言されたタイプのファクトにアクセスして処理する必要がある場合があります。このような場合、JBoss ルールは、アプリケーションが必要とする最も一般的なファクトを処理するためのシンプルな API を提供します。宣言されたファクトは、宣言されたパッケージに属します。

15.7. タイプの宣言

これは、タイプの宣言のプロセスを示しています。
package org.drools.examples

import java.util.Date

declare Person
    name : String
    dateOfBirth : Date
    address : Address
end

15.8. API の例を使用した宣言的ファクトタイプの処理

以下の例は、API を使用した宣言されたファクトタイプの処理を示しています。
// get a reference to a knowledge base with a declared type:
KnowledgeBase kbase = ...

// get the declared FactType
FactType personType = kbase.getFactType( "org.drools.examples",
                                         "Person" );

// handle the type as necessary:
// create instances:
Object bob = personType.newInstance();

// set attributes values
personType.set( bob,
                "name",
                "Bob" );
personType.set( bob,
                "age",
                42 );

// insert fact into a session
StatefulKnowledgeSession ksession = ...
ksession.insert( bob );
ksession.fireAllRules();

// read attributes
String name = personType.get( bob, "name" );
int age = personType.get( bob, "age" );

15.9. タイプ宣言の拡張

タイプ宣言は、継承の extended キーワードをサポートします。DRL で宣言されたサブタイプで Java で宣言されたタイプを拡張するには、フィールドなしで declare ステートメントで supertype を繰り返します。

15.10. タイプ宣言の拡張例

これは、extends アノテーションの使用を示しています。
import org.people.Person

declare Person
end

declare Student extends Person
    school : String
end

declare LongTermStudent extends Student
    years : int
    course : String
end

15.11. 特性

トレイトを 使用すると、クラス階層に自然に適合しない複数の動的型をモデル化できます。トレイトは、実行時に個々のオブジェクトに適用できるインターフェイスです。インターフェイスから特性を作成するには、@format(trait) アノテーションが DRL の宣言に追加されます。

15.12. トレイトの例

declare GoldenCustomer
    @format(trait)
    // fields will map to getters/setters
    code     : String
    balance  : long
    discount : int
    maxExpense : long
end
特性をオブジェクトに適用するには、新しい don キーワードが追加されます。
when
    $c : Customer()
then
    GoldenCustomer gc = don( $c, Customer.class );
end

15.13. コアオブジェクトおよびトレイト

コアオブジェクトが特性を使用しない場合、プロキシークラスはその場に作成されます(コア/トレイクラスの組み合わせごとにこのようなクラスが遅延して生成されます)。コアオブジェクトをラップしてトレイトインターフェイスを実装するプロキシーインスタンスは、自動的に挿入され、他のルールをアクティブにする可能性があります。インターフェイスを宣言して使用し、エンジンから自由に実装プロキシーを取得するという利点は、ルールの作成時に複数の継承階層を悪用できることです。ただし、コアクラスはこれらのインターフェイスを静的に実装する必要はありません。また、レガシークラスをコアとして使用することもできます。どのオブジェクトでも特性を付けることができます。ただし、効率の理由から、@Traitable アノテーションを宣言された Bean クラスに追加して、コンパイラーが生成する必要のある glue コードの量を減らすことができます。これはオプションであり、エンジンの動作は変更されません。

15.14. @Traitable Example

これは、@traitable アノテーションの使用を示しています。
declare Customer
    @Traitable
    code    : String
    balance : long
end

15.15. トレイトを使用したルールの作成

コアクラスとトレイトインターフェイス間の接続はプロキシーレベルのみです。(つまり、トレイトはコアクラスにとくに関連付けられていません。) つまり、同じ特性をまったく異なるオブジェクトに適用できます。このため、トレイトはそのコアオブジェクトのフィールドを透過的に公開しません。トレイトインターフェイスを使用してルールを作成する場合は、通常通り、インターフェイスのフィールドのみが利用できます。ただし、コアオブジェクトフィールドに対応するインターフェイスのフィールドは、プロキシークラスによってマッピングされます。

15.16. トレイトを使用したルールの例

以下の例では、トレイトインターフェイスが フィールドにマッピングされています。
when
    $o: OrderItem( $p : price, $code : custCode )
    $c: GoldenCustomer( code == $code, $a : balance, $d: discount )
then
    $c.setBalance( $a - $p*$d );
end

15.17. 非表示のフィールド

非表示フィールドは、インターフェイスによって公開されないコアクラスのフィールドです。

15.18. 2 つのパートナープロキシー

2 つのパートプロキシー は、直感的に処理されないソフトフィールドと非表示フィールドを処理するために開発されました。内部的には、プロキシーは適切なプロキシーとラッパーによって形成されます。前者はインターフェイスを実装し、後者はコアオブジェクトフィールドを管理し、ソフトフィールドをサポートする名前/値マップを実装します。プロキシーは、必要に応じてコアオブジェクトとマップラッパーの両方を使用してインターフェイスを実装します。

15.19. ラッパー

ラッパー は、ルールの作成時に入力の外れ形式を提供します。ただし、他の用途もあります。ラッパーは、オブジェクトに接続されている特性の数に関係なく、ラップするオブジェクトに固有のものです。同じオブジェクトのすべてのプロキシーが同じラッパーを共有します。さらに、ラッパーにはラップされたオブジェクトにアタッチされたすべてのプロキシーへのバック参照が含まれ、トレイトが相互を認識できるようにします。

15.20. ラッパーの例

以下は、ラッパーを使用する例です。
when
    $sc : GoldenCustomer( $c : code, // hard getter
                          $maxExpense : maxExpense > 1000 // soft getter
    )
then
    $sc.setDiscount( ... ); // soft setter
end

15.21. isA アノテーションを持つラッパーの例

これは、isA アノテーションと共に使用されるラッパーを示しています。
$sc : GoldenCustomer( $maxExpense : maxExpense > 1000,
                      this isA "SeniorCustomer"
)

15.22. トレイトの削除

ビジネスロジックでは、ラップされたオブジェクトから特性を削除する必要がある場合があります。これには 2 つの方法があります。
論理ド
トレイト操作により、プロキシーの論理挿入が行われます。
then
    don( $x, // core object
         Customer.class, // trait class
         true // optional flag for logical insertion
    )
shed キーワード
shed キーワードにより、指定の引数タイプに対応するプロキシーが取り消されます。
then
    Thing t = shed( $x, GoldenCustomer.class )
この操作は、org.drools.factmodel.traits.Thing インターフェイスを実装する別のプロキシーを返します。ここで、getFields ()メソッドおよび getCore ()メソッドが定義されます。内部的には、宣言された特性はすべて生成されます(指定された他の特性に加えて)。これにより、ラッパーをソフトフィールドで保持でき、それ以外は失われます。

15.23. ルール構文の例

以下は、ルールの作成時に使用する必要のある構文の例です。
rule "<name>"
    <attribute>*
when
    <conditional element>*
then
    <action>*
end

15.24. タイマー属性の例

これは、timer 属性の例です。
timer ( int: <initial delay> <repeat interval>? )
timer ( int: 30s )
timer ( int: 30s 5m )

timer ( cron: <cron expression> )
timer ( cron:* 0/15 * * * ? )

15.25. タイマー

JBoss ルールでは、以下のタイマーを使用できます。
Interval
タイマーの間隔(int: で示されます)は、java.util.Timer オブジェクトのセマンティクスに従い、初期遅延とオプションの繰り返し間隔を使用します。
Cron
cron (cron: で示されます)タイマーは、標準の Unix cron 式に従います。

15.26. Cron タイマーの例

Cron タイマーは以下のようになります。
rule "Send SMS every 15 minutes"
    timer (cron:* 0/15 * * * ?)
when
    $a : Alarm( on == true )
then
    channels[ "sms" ].insert( new Sms( $a.mobileNumber, "The alarm is still on" );
end

15.27. calendars

カレンダーは、ルールが実行されるタイミングを制御するために使用されます。JBoss ルールは Quartz カレンダーを使用します。

15.28. Quartz カレンダーの例

以下は Quartz カレンダーの例です。
Calendar weekDayCal = QuartzHelper.quartzCalendarAdapter(org.quartz.Calendar quartzCal)

15.29. カレンダーの登録

手順15.1 タスク

  1. StatefulKnowledgeSession を起動します。
  2. 次のコードを使用してカレンダーを登録します。
    ksession.getCalendars().set( "weekday", weekDayCal );
  3. カレンダーとタイマーを同時に使用する場合は、以下のコードを使用します。
    rule "weekdays are high priority"
       calendars "weekday"
       timer (int:0 1h)
    when 
        Alarm()
    then
        send( "priority high - we have an alarm” );
    end 
    
    rule "weekend are low priority"
       calendars "weekend"
       timer (int:0 4h)
    when 
        Alarm()
    then
        send( "priority low - we have an alarm” );
    end

15.30. 左辺(Left Hand Side)

Left Hand Side (LHS)は、ルールの条件部分の共通名です。これは、ゼロ以上の Conditional 要素で設定されます。LHS が空の場合は、常に true である条件要素と見なされ、新しい WorkingMemory セッションが作成されると 1 回アクティベートされます。

15.31. 条件要素

条件要素は、1 つ以上の パターン で機能します。最も一般的な条件要素は および です。いずれの方法でも接続されていないルールの LHS に複数のパターンがある場合は暗黙的になります。

15.32. Conditional 要素のないルールの例

これは、条件付き要素のないルールの例です。
rule "no CEs"
when
    // empty
then
    ... // actions (executed once)
end

// The above rule is internally rewritten as:

rule "eval(true)"
when
    eval( true )
then
    ... // actions (executed once)
end

第16章 パターン

16.1. パターン

pattern 要素は、最も重要な Conditional 要素です。ワーキングメモリーに挿入される各ファクトに一致する可能性があります。パターンには制約が含まれ、任意のパターンバインディングがあります。

16.2. パターンの例

パターンは以下のようになります。
rule "2 unconnected patterns"
when
    Pattern1()
    Pattern2()
then
    ... // actions
end

// The above rule is internally rewritten as:

rule "2 and connected patterns"
when
    Pattern1()
    and Pattern2()
then
    ... // actions
end
注記
および には、 先頭の宣言バインディングを含めることはできません。これは、宣言が一度に 1 つのファクトのみを参照し、と が満たされると両方のファクトに一致するためです。

16.3. パターンマッチング

パターンは、指定されたタイプのファクトと一致します。タイプは、ファクトオブジェクトの実際のクラスである必要はありません。パターンは、スーパークラスやインターフェイスを参照する可能性があるため、複数の異なるクラスのファクトと一致する可能性があります。制約は括弧内に定義されます。

16.4. パターンバインディング

パターンは一致するオブジェクトにバインドできます。これは、$p などのパターンバインディング変数を使用して実行できます。

16.5. 変数の例を使用したパターンバインディング

これは、変数を使用したパターンバインディングの例です。
rule ...
when
    $p : Person()
then
    System.out.println( "Person " + $p );
end
注記
プリフィックスドル記号($)は必須ではありません。

16.6. 制約

制約は、true または false を返す式です。たとえば、5 の状態が 6 未満である制約を設定できます。

第17章 要素および変数

17.1. Java Bean (POJO)のプロパティーアクセス

Bean プロパティーは直接使用できます。bean プロパティーは標準の Java Bean getter を使用して公開されます。メソッド getMyProperty ()(また はプリミティブブール値の場合は isMyProperty ())で、引数なしで何かを返します。
JBoss ルールは標準の JDK Introspector クラスを使用してこのマッピングを行うため、標準の Java Bean 仕様に従います。
警告
プロパティーアクセサーは、ルールに影響を与える可能性のある方法でオブジェクトの状態を変更しないでください。ルールエンジンは、呼び出し間で一致の結果を効果的にキャッシュして、より高速にします。

17.2. POJO の例

Bean プロパティーは以下のようになります。
Person( age == 50 )

// this is the same as:
Person( getAge() == 50 )
age プロパティー
age プロパティーは、ゲッター getAge ()ではなく DRL で age として記述されます。
プロパティーアクセサー
フィールドインデックス作成によるパフォーマンスの向上により、ゲッター(getAge())の代わりにプロパティーアクセス(age)を使用できます。

17.3. POJO の使用

手順17.1 タスク

  1. 以下の例を確認してください。
    public int getAge() {
        Date now = DateUtil.now(); // Do NOT do this
        return DateUtil.differenceInYears(now, birthday);
    }
  2. この問題を解決するには、現在の日付を作業メモリーにラップするファクトを挿入し、必要に応じてそのファクトを fireAllRules の間で更新します。

17.4. POJO フォールバック

POJO を使用する場合は、フォールバックメソッドが適用されます。プロパティーの getter が見つからない場合、コンパイラーはプロパティー名をメソッド名として使用し、引数なしで使用します。ネストされたプロパティーもインデックス化されます。

17.5. フォールバックの例

これは、フォールバックが実装されたときに発生するものです。
Person( age == 50 )

// If Person.getAge() does not exists, this falls back to:
Person( age() == 50 )
これは、ネストされたプロパティーのようになります。
Person( address.houseNumber == 50 )

// this is the same as:
Person( getAddress().getHouseNumber() == 50 )
警告
ステートフルセッションでは、ネストされたアクセサーを使用する場合は注意が必要です。作業メモリーはネストされた値を認識しないため、それらが変更するタイミングは分からないためです。親参照のいずれかが作業メモリーに挿入されている間は、不変に考慮してください。ネストされた値を変更する場合は、すべての外部ファクトを更新済みとしてマークする必要があります。上記の例では、houseNumber が変更されると、その Address が指定された Person は更新済みとしてマークされる必要があります。

17.6. Java 式

表17.1 Java 式

機能
パターンの括弧内の制約として ブール値 を返す任意の Java 式を使用できます。Java 式は、プロパティーアクセスなどの他の式の拡張機能と組み合わせることができます。
Person( age == 50 )
評価の優先度は、ロジックや数学式のように括弧を使用して変更できます。
Person( age > 100 && ( age % 10 == 0 ) )
Java メソッドは再利用できます。
Person( Math.round( weight / ( height * height ) ) < 25.0 )
フィールドと値が異なるタイプの場合、タイプ強制は常に試行されます。不良強制を試行すると例外が出力されます。
Person( age == "10" ) // "10" is coerced to 10
警告
メソッドは、ルールに影響を与える可能性のある方法でオブジェクトの状態を変更しないでください。LHS のファクトで実行されるメソッドは、読み取り専用 のメソッドである必要があります。
警告
ファクトの状態は、ルールの呼び出し間で変更しないでください(変更するたびにファクトがワーキングメモリーに更新済みとしてマークされない限り)。
Person( System.currentTimeMillis() % 1000 == 0 ) // Do NOT do this
重要
すべての演算子には、== および != 以外の通常の Java セマンティクスがあります。
== 演算子には null 安全な equals ()セマンティクスが あります。
// Similar to: java.util.Objects.equals(person.getFirstName(), "John")
// so (because "John" is not null) similar to:
// "John".equals(person.getFirstName())
Person( firstName == "John" )
!= 演算子には null 安全な !equals ()セマンティクスが あります。
// Similar to: !java.util.Objects.equals(person.getFirstName(), "John")
Person( firstName != "John" )

17.7. コンマ区切りの Operator

制約グループを分離するには、コンマ文字('、,')が使用されます。暗黙的な接続セマンティクスがあります。
コンマ演算子は、読み取りが容易になり、エンジンがそれらを最適化できるため、最上位レベルの制約で使用されます。

17.8. コンマ区切りの Operator の例

以下は、暗黙的なセマンティクスと接続セマンティクスのコンマ区切りのシナリオを示しています。
// Person is at least 50 and weighs at least 80 kg
Person( age > 50, weight > 80 )
// Person is at least 50, weighs at least 80 kg and is taller than 2 meter.
Person( age > 50, weight > 80, height > 2 )
注記
コンマ()演算子は括弧などの複合制約式に埋め込むことはできません。

17.9. バインド変数

プロパティーを JBoss ルールの変数にバインドできます。これにより、実行とパフォーマンスが速くなります。

17.10. バインド変数の例

変数にバインドされるプロパティーの例を以下に示します。
// 2 persons of the same age
Person( $firstAge : age ) // binding
Person( age == $firstAge ) // constraint expression
注記
後方互換性の理由から、以下のように制約バインディングと制約式を混在させることができます(ただし推奨されません)。
// Not recommended
Person( $age : age * 2 < 100 )
// Recommended (separates bindings and constraint expressions)
Person( age * 2 < 100, $age : age )

17.11. ユニフィケーション

複数のプロパティー間で引数を 統一 できます。位置引数は、常にユニフィケーションで常に処理されますが、名前付き引数の場合はユニフィケーション記号 ':=' が存在します。

17.12. ユニフィケーションの例

これは、以下の 2 つの引数を統合するものです。
Person( $age := age ) 
Person( $age := age)

17.13. JBoss ルールのオプションおよび演算子

表17.2 JBoss ルールのオプションおよび演算子

オプション 説明
日付リテラル
日付形式 dd-mmm-yyyy はデフォルトでサポートされます。これをカスタマイズするには、drools.dateformat という名前のシステムプロパティーとして別の日付形式マスクを指定します。より詳細な制御が必要な場合は、制限を使用します。
Cheese( bestBefore < "27-Oct-2009" )
List および Map アクセス
インデックスで List 値に直接アクセスできます。
// Same as childList(0).getAge() == 18
Person( childList[0].age == 18 )
値キー
キーで Map 値に直接アクセスできます。
// Same as credentialMap.get("jsmith").isValid()
Person( credentialMap["jsmith"].valid )
省略された組み合わせ比較条件
これにより、制限接続 &&amp ; または || を使用してフィールドに複数の制限を設定することができます。括弧によるグループ化が許可され、再帰的な構文パターンが発生します。
// Simple abbreviated combined relation condition using a single &&
Person( age > 30 && < 40 )
// Complex abbreviated combined relation using groupings
Person( age ( (> 30 && < 40) ||
              (> 20 && < 25) ) )
// Mixing abbreviated combined relation with constraint connectives
Person( age > 30 && < 40 || location == "london" )
Operator
Operator は自然順序付けのあるプロパティーで使用できます。たとえば、Date フィールドの < は、String フィールドの の を意味し、アルファベット順で小さいことを意味します。
Person( firstName < $otherFirstName )
Person( birthDate < $otherBirthDate )
Operator が一致する
有効な Java regular expression に対してフィールドを一致させます。通常、regexp は文字列リテラルですが、有効な正規表現に解決される変数も使用できます。String プロパティーにのみ適用されます。null 値に対して matches を使用すると、常に false と評価されます。
Cheese( type matches "(Buffalo)?\\S*Mozarella" )
Operator が一致しない
文字列が正規表現と一致しない場合、演算子は true を返します。matches 演算子と同じルールが適用されます。String プロパティーにのみ適用されます。
Cheese( type not matches "(Buffulo)?\\S*Mozarella" )
オペレーターに が含まれる
演算子に 含ま れる は、であるフィールドを確認するために使用されます。 コレクションまたは配列には指定された値が含まれます。これは Collection プロパティーにのみ適用されます。
CheeseCounter( cheeses contains "stilton" ) // contains with a String literal
CheeseCounter( cheeses contains $var ) // contains with a variable
Operator が含まれていない
含んで いない演算子は、Collection または配列であるフィールドに指定の値が含ま れて いない かどうかを確認するために使用されます。これは Collection プロパティーにのみ適用されます。
CheeseCounter( cheeses not contains "cheddar" ) // not contains with a String literal
CheeseCounter( cheeses not contains $var ) // not contains with a variable
演算子 memberOf
演算子 memberOf は、フィールドがコレクションまたは配列のメンバーであるかどうかを確認するために使用されます。そのコレクションは変数である必要があります。
CheeseCounter( cheese memberOf $matureCheeses )
演算子が memberOf ではない
memberOf 以外の演算子は、フィールドがコレクションまたは配列のメンバーではないかどうかを確認するために使用されます。そのコレクションは変数である必要があります。
CheeseCounter( cheese not memberOf $matureCheeses )
オペレーターのサウンドのような
この演算子は と 一致 するのと似ていますが、単語が指定の値とほぼ同じサウンド(英語の通知を使用)を持っているかどうかを確認します。
// match cheese "fubar" or "foobar"
Cheese( name soundslike 'foobar' )
演算子 str
演算子 str は、String であるフィールドが特定の値で開始するか、または終了するかを確認するために使用されます。文字列の長さを確認するのにも使用できます。
Message( routingValue str[startsWith] "R1" )
Message( routingValue str[endsWith] "R2" )
Message( routingValue str[length] 17 )
複合値の制限
複合値の制限では、一致する値が複数ある場合に使用されます。現在、evaluators は現在、これ をサポートしていません。この演算子の 2 番目のオペランドは、括弧で囲まれた値のコンマ区切りリストである必要があります。値は変数、リテラル、戻り値、または修飾識別子として指定できます。エバリュエーターはいずれも実際には 構文上の sugar で、演算子 != および == を使用して複数の制限のリストとして内部的に書き換えられます。
Person( $cheese : favouriteCheese )
Cheese( type in ( "stilton", "cheddar", $cheese ) )

17.14. Operator の優先順位

表17.3 Operator の優先順位

演算子のタイプ 演算子 注記
(ネストされた)プロパティーアクセス をクリックします。 通常の Java セマンティクスではない
List/Map アクセス [ ] 通常の Java セマンティクスではない
制約バインディング : 通常の Java セマンティクスではない
Multiplicative * /%
加法 + -
shift << >>>>>
Relational < ><= >=instanceof
等価 == != 通常の Java (ではなく)セマンティクスを使用しません。代わりに(ではなく等号 セマンティクスを使用します。
非短時間の回転 AND &
非短時間排他的 OR ^
non-short circuiting inclusive OR |
論理 AND &&
論理 OR ||
ternary ? :
コンマ区切り AND , 通常の Java セマンティクスではない

17.15. きめ細かなプロパティー変更リスナー

この機能により、パターンマッチングは、特定のパターン内に実際に制約またはバインドされているプロパティーの変更にのみ反応できます。これにより、パフォーマンスと再帰が役立ち、人為的なオブジェクト分割を回避することができます。
注記
デフォルトでは、ルールエンジンの動作を以前のリリースと後方互換性を持たせるために、この機能はオフになっています。特定の Bean でアクティベートする場合は、@propertyReactive アノテーションを付ける必要があります。

17.16. きめ細かなプロパティー変更リスナーの例

DRL の例
declare Person
          @propertyReactive
          firstName : String
          lastName : String
          end
Java クラスの例
@PropertyReactive
          public static class Person {
          private String firstName;
          private String lastName;
          }

17.17. 粒度の細かいプロパティー変更リスナーの使用

これらのリスナーを使用すると、無限再帰を回避するために no-loop 属性を実装する必要がなくなります。エンジンは、ルールの RHS が他のプロパティーを変更し、プロパティーでパターンマッチングが実行されることを認識します。Java クラスでは、呼び出しが実際に他のプロパティーを変更することを示すために、メソッドにアノテーションを付けることもできます。

17.18. @watch でのパターンの使用

@watch でパターンにアノテーションを付けると、パターンが反応する推論されたプロパティーセットを変更できます。@watch アノテーションで指定されたプロパティーは、自動的に推測されるプロパティーに追加されます。1 つ以上の名前を ! で開始し、ワイルドカード *!* を使用してパターンで使用されるタイプのプロパティーをすべてリッスンするか、または何もリッスンするようにパターンによって明示的に除外できます。

17.19. @watch の例

これは、ルールの LHS 内の @watch アノテーションです。
// listens for changes on both firstName (inferred) and lastName
          Person( firstName == $expectedFirstName ) @watch( lastName )

          // listens for all the properties of the Person bean
          Person( firstName == $expectedFirstName ) @watch( * )

          // listens for changes on lastName and explicitly exclude firstName
          Person( firstName == $expectedFirstName ) @watch( lastName, !firstName )

          // listens for changes on all the properties except the age one
          Person( firstName == $expectedFirstName ) @watch( *, !age )
注記
@PropertyReactive アノテーションのないタイプを使用するパターンでこのアノテーションを使用することは意味がないため、ルールコンパイラーを使用するとコンパイルエラーが発生します。また、@watch で同じプロパティーを複製すると、@watch (firstName, ! firstName)のようにコンパイルエラーが発生します。

17.20. @PropertySpecificOption の使用

KnowledgeBuilderConfiguration の on オプションを使用して、デフォルトで @watch を有効にするか、完全に拒否できます。この新しい PropertySpecificOption には、以下の 3 つの値のいずれかを指定できます。
- DISABLED => the feature is turned off and all the other related annotations are just ignored
          - ALLOWED => this is the default behavior: types are not property reactive unless they are not annotated with @PropertySpecific
          - ALWAYS => all types are property reactive by default

17.21. 基本的な条件要素

表17.4 基本的な条件要素

名前 説明 追加オプション
および
Conditional 要素 および は、他の Conditional 要素を論理積にグループ化するために使用されます。JBoss ルールは 接頭辞 と インフィックス と の両方をサポート ます。括弧を使用した明示的なグループ化をサポートします。従来の infix および prefix および を使用することもできます。
//infixAnd
Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType )
//infixAnd with grouping
( Cheese( cheeseType : type ) and
  ( Person( favouriteCheese == cheeseType ) or 
    Person( favouriteCheese == cheeseType ) )
接頭辞 および もサポートされます。
(and Cheese( cheeseType : type )
     Person( favouriteCheese == cheeseType ) )
LHS のルート要素は暗黙的な接頭辞であるため 指定する必要はありません。
when
    Cheese( cheeseType : type )
    Person( favouriteCheese == cheeseType )
then
    ...
または
これは、2 つ以上の同様のルールを生成するためのショートカットです。JBoss ルールは、接頭辞 または infix また の両方をサポートします。従来の infix、接頭辞、および明示的なグループ化の括弧を使用できます。
//infixOr
Cheese( cheeseType : type ) or Person( favouriteCheese == cheeseType )
//infixOr with grouping
( Cheese( cheeseType : type ) or
  ( Person( favouriteCheese == cheeseType ) and
    Person( favouriteCheese == cheeseType ) )
(or Person( sex == "f", age > 60 )
    Person( sex == "m", age > 65 )
オプションのパターンバインディングを許可します。各パターンは、ディープ変数を使用して個別にバインドする必要があります。
pensioner : ( Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 ) )
(or pensioner : Person( sex == "f", age > 60 ) 
    pensioner : Person( sex == "m", age > 65 ) )
not
これにより、存在しないと指定されたオブジェクトが作業メモリーに含まれていないことを確認します。これは、適用する条件要素の括弧の後に続きます。(単一のパターンでは、括弧を省略できます。)
// Brackets are optional:
not Bus(color == "red")
// Brackets are optional:
not ( Bus(color == "red", number == 42) )
// "not" with nested infix and - two patterns,
// brackets are requires:
not ( Bus(color == "red") and
      Bus(color == "blue") )
exists
これにより、ワーキングメモリーがチェックされ、指定したアイテムが存在するかどうかを確認します。キーワードが 存在 するには、適用する CE の周りの括弧が続く必要があります。(単一のパターンでは、括弧を省略できます。)
exists Bus(color == "red")
// brackets are optional:
exists ( Bus(color == "red", number == 42) )
// "exists" with nested infix and,
// brackets are required:
exists ( Bus(color == "red") and
         Bus(color == "blue") )
注記
Conditional Element また は の動作は、フィールド制約の制約と制約のために接続 || とは異なります。エンジンは Conditional 要素 また は を解釈できません。代わりに、また は が指定されたルールは、複数のサブルールとして書き換えられます。このプロセスにより、最終的には、ルートノードおよび各 CE に 1 つのサブルールを持つルールが作成されます。各サブルールは通常のルールと同様にアクティベートおよび実行できます。これらのサブルールの間には、特別な動作や対話はありません。

17.22. 条件要素 Forall

最初のパターンに一致するすべてのファクトが残りのすべてのパターンと一致する場合、この要素は true と評価されます。これは スコープ区切り文字 です。したがって、以前にバインドされたすべての変数を使用できますが、その中にバインドされた変数は外部では使用できません。
Forall 他の CE 内でネストできます。たとえば、forallnot CE 内で使用できます。オプションの括弧は 1 つのパターンのみであるため、ネストされた forall 括弧を使用する必要があります。

17.23. forall の例

true への評価
rule "All English buses are red"
when
    forall( $bus : Bus( type == 'english') 
                   Bus( this == $bus, color = 'red' ) )
then
    // all English buses are red
end
単一のパターン forall
rule "All Buses are Red"
when
    forall( Bus( color == 'red' ) )
then
    // all Bus facts are red
end
マルチパターン forall
rule "all employees have health and dental care programs"
when
    forall( $emp : Employee()
            HealthCare( employee == $emp )
            DentalCare( employee == $emp )
          )
then
    // all employees have health and dental care
end
ネストされた forall
rule "not all employees have health and dental care"
when 
    not ( forall( $emp : Employee()
                  HealthCare( employee == $emp )
                  DentalCare( employee == $emp ) ) 
        )
then
    // not all employees have health and dental care
end

17.24. 条件要素(Conditioning 要素)

の Conditional Element を使用すると、ユーザーは LHS パターンと一致するデータの任意のソースを指定できます。これにより、エンジンは作業メモリーにないデータよりも推論できます。データソースは、バインドされた変数のサブフィールド、またはメソッド呼び出しの結果になります。すぐに使用できる他のアプリケーションコンポーネントやフレームワークとの統合を可能にする強力な構築です。一般的な例としては、ハイバネート名前付きクエリーを使用してデータベースからオンデマンドで取得されるデータとの統合があります。
オブジェクトソースの定義に使用される式として、通常の MVEL 構文に準拠する任意の式を使用できます。したがって、オブジェクトプロパティーナビゲーションを簡単に使用でき、メソッド呼び出しを実行し、マップとコレクション要素にアクセスできます。
重要
fromlock-on-active ルール属性で使用すると、ルールが実行されない可能性があります。
この問題に対処する方法はいくつかあります。
  • すべてのファクトを作業メモリーにアサートしたり、制約式でネストされたオブジェクト参照を使用したりできる場合は from は使用しないでください。
  • 条件(LHS)の最後の文として modify ブロックで使用される変数を配置します。
  • 同じルールフローグループ内のルールがアクティベーションをどのように配置するかを明示的に管理できる場合は、lock-on-active を使用しないでください。

17.25. 例

パターンの理由およびバインディング
rule "validate zipcode"
when
    Person( $personAddress : address ) 
    Address( zipcode == "23920W") from $personAddress 
then
    // zip code is ok
end
グラフ表記の使用
rule "validate zipcode"
when
    $p : Person( ) 
    $a : Address( zipcode == "23920W") from $p.address 
then
    // zip code is ok
end
すべてのオブジェクトの反復
rule "apply 10% discount to all items over US$ 100,00 in an order"
when
    $order : Order()
    $item  : OrderItem( value > 100 ) from $order.items
then
    // apply discount to $item
end
lock-on-active で使用します。
rule "Assign people in North Carolina (NC) to sales region 1"
ruleflow-group "test"
lock-on-active true
when
    $p : Person(address.state == "NC" )  
then
    modify ($p) {} // Assign person to sales region 1 in a modify block
end

rule "Apply a discount to people in the city of Raleigh"
ruleflow-group "test"
lock-on-active true
when
    $p : Person(address.city == "Raleigh" )  
then
    modify ($p) {} //Apply discount to person in a modify block
end

17.26. 条件要素の収集

Conditional Element collect を使用すると、指定のソースまたはワーキングメモリーから取得したオブジェクトのコレクションを推論できます。First Oder Logic では、これはカーディナリティー数量です。
collect の結果パターンは、java.util.Collection インターフェイスを実装し、デフォルトの no-arg パブリックコンストラクターを提供する任意の具象クラスにすることができます。java.util.Collection インターフェイスを実装し、デフォルトの no-arg パブリックコンストラクターを提供する限り、ArrayList、LinkedList、HashSet、または独自のクラスなどの Java コレクションを使用できます。
収集 CE の前にバインドされた変数はソースと結果の両方のパターンの範囲にあるため、これを使用してソースパターンと結果パターンの両方を制限することができます。collect 内で作成されたバインディングは、その外部では使用できません。

17.27. 条件要素アクセラレーション

Conditional Element accumulate は、collect のより柔軟で強力な形式で、収集 の機能を行うために使用でき、CE が 収集 する結果も達成できるという意味です。これにより、ルールはオブジェクトのコレクションを反復処理し、各要素に対してカスタムアクションを実行できます。最後に、結果オブジェクトを返します。
accumulate は、事前に定義された accumulate 関数の使用またはインラインカスタムコードの使用の両方をサポートします。ただし、インラインのカスタムコードは、ルール作成者が維持することが難しく、コードの重複が頻繁に発生するため、回避する必要があります。accumulate 関数は、テストと再利用が容易になります。
Accumulate CE は複数の異なる構文もサポートします。推奨構文は、bellow のようにトップレベルの accumulate ですが、他のすべての構文は後方互換性を維持するためにサポートされます。

17.28. 条件要素の構文アクセラレーション

最上位の累積構文
accumulate( <source pattern>; <functions> [;<constraints>] )
構文の例
rule "Raise alarm"
when
    $s : Sensor()
    accumulate( Reading( sensor == $s, $temp : temperature );
                $min : min( $temp ),
                $max : max( $temp ),
                $avg : average( $temp );
                $min < 20, $avg > 70 )
then
    // raise the alarm
end
上記の例では、min、max、および average は Accumulate Functions で、各センサーのすべての測定値に対して、最低気温、最高気温、平均気温の値を計算します。

17.29. 条件要素の関数がアクセラレートする

  • average
  • min
  • max
  • count
  • sum
  • collectList
  • collectSet
これらの一般的な関数は、任意の式を入力として受け入れます。たとえば、あるユーザーが注文のすべての項目の平均的な投資を計算する場合は、average 関数を使用してルールを作成することができます。
rule "Average profit"
when
    $order : Order()
    accumulate( OrderItem( order == $order, $cost : cost, $price : price );
                $avgProfit : average( 1 - $cost / $price ) )
then
    // average profit for $order is $avgProfit
end

17.30. Conditional 要素の累積およびプラグイン性

accumulate 関数はすべてプラグ可能なものです。つまり、必要に応じて、ドメイン固有の機能をエンジンに簡単に追加でき、ルールは制限なしに使用を開始できます。新しい Accumulate 関数を実装するには、org.drools.runtime.rule.TypedAccumulateFunction インターフェイスを実装し、設定ファイルに行を追加するか、システムプロパティーを設定して新しい関数について認識させる必要があります。

17.31. Conditional 要素の累積およびプラグイン性の例

Accumulate Function 実装の例として、以下は average 関数の実装になります。
/**
 * An implementation of an accumulator capable of calculating average values
 */
public class AverageAccumulateFunction implements org.drools.runtime.rule.TypedAccumulateFunction {

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }

    public void writeExternal(ObjectOutput out) throws IOException {

    }

    public static class AverageData implements Externalizable {
        public int    count = 0;
        public double total = 0;
        
        public AverageData() {}

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            count   = in.readInt();
            total   = in.readDouble();
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeInt(count);
            out.writeDouble(total);
        }

    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#createContext()
     */
    public Serializable createContext() {
        return new AverageData();
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object)
     */
    public void init(Serializable context) throws Exception {
        AverageData data = (AverageData) context;
        data.count = 0;
        data.total = 0;
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#accumulate(java.lang.Object, java.lang.Object)
     */
    public void accumulate(Serializable context,
                           Object value) {
        AverageData data = (AverageData) context;
        data.count++;
        data.total += ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#reverse(java.lang.Object, java.lang.Object)
     */
    public void reverse(Serializable context,
                        Object value) throws Exception {
        AverageData data = (AverageData) context;
        data.count--;
        data.total -= ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#getResult(java.lang.Object)
     */
    public Object getResult(Serializable context) throws Exception {
        AverageData data = (AverageData) context;
        return new Double( data.count == 0 ? 0 : data.total / data.count );
    }

    /* (non-Javadoc)
     * @see org.drools.base.accumulators.AccumulateFunction#supportsReverse()
     */
    public boolean supportsReverse() {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public Class< ? > getResultType() {
        return Number.class;
    }

}

17.32. Conditional 要素の関数のコード

関数内のプラグイン用のコード(設定ファイルに入力)
jbossrules.accumulate.function.average =
   org.jbossrules.base.accumulators.AverageAccumulateFunction
代替構文:戻り値の型を持つ単一の関数
rule "Apply 10% discount to orders over US$ 100,00"
when
    $order : Order()
    $total : Number( doubleValue > 100 ) 
             from accumulate( OrderItem( order == $order, $value : value ),
                              sum( $value ) )
then
    # apply discount to $order
end
** 項目名 **
**アイテムの説明 **

17.33. インラインのカスタムコードで累積

警告
accumulate をインラインのカスタムコードで使用することは、いくつかの理由で適切ではありません。これには、ルールを使用したルールの保守やテストの難易度や、そのコードの再利用ができないことが含まれます。独自の accumulate 関数を実装すると、簡単なテストが可能になります。この形式の accumulate は、後方互換性としてのみサポートされます。
インラインカスタムコードを含む accumulate CE の一般的な構文は次のとおりです。
<result pattern> from accumulate( <source pattern>,
                                  init( <init code> ),
                                  action( <action code> ),
                                  reverse( <reverse code> ),
                                  result( <result expression> ) )
各要素の意味は次のとおりです。
  • <source pattern >: ソースパターンは、エンジンが各ソースオブジェクトと照合しようとする通常のパターンです。
  • <init code > : これは、選択した方言のコードのセマンティックブロックで、ソースオブジェクトを反復する前にタプルごとに 1 回実行されます。
  • <action code >: これは、各ソースオブジェクトに対して実行される、選択したダイアレクトのコードのセマンティックブロックです。
  • <reverse code > : これは、選択したダイアレクトのコードのオプションのセマンティックブロックです。存在する場合は、ソースパターンに一致しなくなった各ソースオブジェクトに対して実行されます。このコードブロックの目的は、< action code > ブロックで実行された計算を元に戻すことです。これにより、ソースオブジェクトが変更または取り消されたときにエンジンが計算をデクリメントできるため、これらの操作のパフォーマンスが大幅に向上します。
  • & lt;result expression& gt; : これは、すべてのソースオブジェクトが反復された後に実行される、選択した方言のセマンティック式です。
  • <result pattern > : これは、エンジンが < result expression> から返されたオブジェクトと照合しようとする正規表現です。一致する場合、accumulate 条件要素は true と評価され、エンジンはルール内の次の CE の評価を続行します。一致しない場合、累積 CE は false に評価され、エンジンはそのルールの CE の評価を停止します。

17.34. インラインのカスタムコード例で累積

インラインのカスタムコード
rule "Apply 10% discount to orders over US$ 100,00"
when
    $order : Order()
    $total : Number( doubleValue > 100 ) 
             from accumulate( OrderItem( order == $order, $value : value ),
                              init( double total = 0; ),
                              action( total += $value; ),
                              reverse( total -= $value; ),
                              result( total ) )
then
    # apply discount to $order
end
上記の例では、Working Memory の各 Order に対して、エンジンは total 変数をゼロに初期化する init コード を実行します。次に、この順番に対して OrderItem のすべてのオブジェクトを繰り返し処理し、それぞれに アクション を実行します(この例では、すべての項目の値を total 変数に合計します)。OrderItem オブジェクトをすべて反復すると、結果式 (上記の例では変数 total)に対応する値が返されます。最後に、エンジンは Number パターンで結果を照合しようとし、double 値が 100 を超える場合はルールが実行されます。
カスタムオブジェクトのインスタンス化および入力
rule "Accumulate using custom objects"
when
    $person   : Person( $likes : likes )
    $cheesery : Cheesery( totalAmount > 100 )
                from accumulate( $cheese : Cheese( type == $likes ),
                                 init( Cheesery cheesery = new Cheesery(); ),
                                 action( cheesery.addCheese( $cheese ); ),
                                 reverse( cheesery.removeCheese( $cheese ); ),
                                 result( cheesery ) );
then
    // do something
end

17.35. 条件要素評価

条件要素 eval は基本的に catch-all で、セマンティックコード(プリミティブブール値を返す)を実行できます。このコードは、ルールの LHS にバインドされていた変数と、ルールパッケージの関数を参照できます。eval を過剰に使用するとルールの宣言性が低くなり、エンジンの実行が悪くなる可能性があります。eval はパターンのどこでも使用できますが、ルールの LHS の最後の条件要素として追加することがベストプラクティスです。
Eval はインデックス化できないため、フィールドの制約として効率的ではありません。ただし、これにより、関数が時間の経過とともに変化する値を返す場合に(フィールド制約内で許可されない)場合に使用することが理想的です。

17.36. 条件要素評価の例

以下は、使用中の 評価 の例です。
p1 : Parameter()
p2 : Parameter()
eval( p1.getList().containsKey( p2.getItem() ) )
p1 : Parameter()
p2 : Parameter()
// call function isValid in the LHS
eval( isValid( p1, p2 ) )

17.37. 右辺(Right Hand Side)

RHS (Right Hand Side)は、ルールの結果またはアクション部分の共通名です。RHS の主な目的は、作業メモリーデータを挿入、取り消すことです。実行するアクションの一覧が含まれている必要があります。ルールの RHS 部分も小さくし、宣言し、読み取り可能に保つ必要があります。
注記
RHS で必須または条件付きコードが必要な場合は、ルールを複数のルールに分割します。

17.38. RHS 規則メソッド

表17.5 RHS 規則メソッド

名前 Description
update (object, handle);
オブジェクトが変更されている(LHS 上のものにバインドされているオブジェクト)と、再編成する必要があるルールがエンジンに指示します。
update(object);
update ( )を使用すると、ナレッジヘルパーは、渡されたオブジェクトのアイデンティティーチェックを介してファクトハンドルを検索します。(プロパティー Change Listeners をエンジンに挿入する Java Bean に提供すると、オブジェクトの変更時に update() を呼び出す必要がなくなります)。ファクトのフィールド値を変更したら、別のファクトを変更する前に update を呼び出す必要があります。そうしないと、ルールエンジン内のインデックス作成に問題が発生します。modify キーワードは、この問題を回避します。
insert(newobject());
作成用の新規オブジェクトを作業メモリーに配置します。
insertLogical(newobject());
insert と同様ですが、現在実行中のルールの真理をサポートするファクトがない場合には、オブジェクトは自動的に取り消されます。
retract(handle);
作業メモリーからオブジェクトを削除します。

17.39. Drools 変数を使用した便利なメソッド

  • drools.halt() 呼び出しは、ルールの実行をすぐに終了します。これは、現行セッションが fireUntilHalt() と機能したときにコントロールをポイントに戻すために必要です。
  • メソッド insert(Object o)update(Object o)、および retract(Object o)drools で呼び出すことができますが、頻繁に使用されるため、オブジェクト参照なしで呼び出すことができます。
  • drools.getWorkingMemory() WorkingMemory オブジェクトを返します。
  • drools.setFocus( String s) 指定したアジェンダグループにフォーカスを設定します。
  • drools.getRule().getName()ルールの RHS から呼び出される は、ルールの名前を返します。
  • drools.getTuple() 現在実行中のルールに一致する Tuple を返し、drools.getActivation() は対応する Activation を提供します。(呼び出しは、ロギングやデバッグに役立ちます。)

17.40. Kcontext 変数を使用した便利なメソッド

  • kcontext.getKnowledgeRuntime().halt() 呼び出しは、ルールの実行をすぐに終了します。
  • アクセサー getAgenda() はセッションの Agenda への参照を返します。次に、さまざまなルールグループ(アクティベーショングループ、アジェンダグループ、およびルールフローグループ)へのアクセスが提供されます。非常に一般的なパラダイムとは、一部のアジェンダグループのアクティベーションであり、長い呼び出しで行うことができます。
    // give focus to the agenda group CleanUp
    kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "CleanUp" ).setFocus();
    ( drools.setFocus( "CleanUp" ) を使用しても同じことができます。)
  • クエリーを実行するには、getQueryResults(String query) を呼び出します。ここで、結果を処理できます。
  • イベント管理を処理するメソッドのセットを使用すると、作業メモリーと Agenda のイベントリスナーを追加および削除できます。
  • メソッドgetKnowledgeBase() は、KnowledgeBase オブジェクト、システム内のすべてのナレッジのバックボーン、および現行セッションの発信元を返します。
  • グローバルは、setGlobal(...)getGlobal(...)、および getGlobals() で管理できます。
  • メソッド getEnvironment() はランタイムの Environment を返します。

17.41. Modify ステートメント

表17.6 Modify ステートメント

名前 Description 構文
modify
これにより、ファクトの更新に対する構造化されたアプローチが提供されます。これは、更新操作と、オブジェクトのフィールドを変更するセッター呼び出しを多数組み合わせます。
modify ( <fact-expression> ) {
    <expression> [ , <expression> ]*
}
括弧化された & lt;fact-expression> は、ファクトオブジェクト参照を生成する必要があります。ブロックの式リストは、通常のオブジェクト参照なしで記述される、指定のオブジェクトのセッター呼び出しで設定され、コンパイラーによって自動的に付加される必要があります。
rule "modify stilton"
when
    $stilton : Cheese(type == "stilton")
then
    modify( $stilton ){
        setPrice( 20 ),
        setAge( "overripe" )
    }
end

17.42. クエリーの例

注記
結果を返すには、ksession.getQueryResults("name") を使用します。name はクエリーの名前です。これにより、クエリー結果のリストが返されます。これにより、クエリーに一致するオブジェクトを取得できます。
30 歳未満のユーザーに対するクエリー
query "people over the age of 30" 
    person : Person( age > 30 )
end
X 歳を超えたユーザー、および Y に存在するユーザーに対するクエリー
query "people over the age of x"  (int x, String y)
    person : Person( age > x, location == y )
end

17.43. QueryResults の例

標準の for ループを使用して、返される QueryResults を繰り返し処理します。各要素は QueryResultsRow で、これを使用してタプルの各列にアクセスできます。これらの列には、バインドされた宣言名またはインデックスの位置からアクセスできます。
QueryResults results = ksession.getQueryResults( "people over the age of 30" );
System.out.println( "we have " + results.size() + " people over the age  of 30" );

System.out.println( "These people are are over 30:" );

for ( QueryResultsRow row : results ) {
    Person person = ( Person ) row.get( "person" );
    System.out.println( person.getName() + "\n" );
}

17.44. 他のクエリーを呼び出すクエリー

クエリーは他のクエリーを呼び出すことができます。これは、オプションのクエリー引数と組み合わせて派生クエリースタイルの後向き連鎖を提供します。引数では、位置構文と名前付き構文がサポートされます。位置と名前付きの両方を組み合わせることもできますが、位置は最初にセミコロンで区切る必要があります。リテラル式はクエリー引数として渡すことができますが、式と変数の組み合わせはできません。
注記
このプロセスで ? 記号を使用すると、クエリーはプルされ、結果が返されると、基礎となるデータが変更されるため、追加の結果を受け取ることはありません。

17.45. 他のクエリーの例を呼び出すクエリー

別のクエリーを呼び出すクエリー
declare Location
    thing : String 
    location : String 
end

query isContainedIn( String x, String y ) 
    Location(x, y;)
    or 
    ( Location(z, y;) and ?isContainedIn(x, z;) )
end
クエリー結果から時間の経過とともに変更をリアクティブに受信するためのライブクエリーの使用
query isContainedIn( String x, String y ) 
    Location(x, y;)
    or 
    ( Location(z, y;) and isContainedIn(x, z;) )
end

rule look when 
    Person( $l : likes ) 
    isContainedIn( $l, 'office'; )
then
   insertLogical( $l 'is in the office' );
end

17.46. 派生クエリーのユニフィケーション

JBoss ルールは、派生クエリーのユニフィケーションをサポートします。これは、引数が任意であることを意味します。静的フィールド org.drools.runtime.rule.Variable.v を使用して、指定されていない Java からクエリーを呼び出すことができます。(変数の代替インスタンスではなく、v を使用する必要があります。) これらは out 引数と呼ばれます。
注記
クエリー自体は、引数が in か out かに関係なく、コンパイル時に宣言されません。これは、使用ごとにランタイム時に定義することができます。

第18章 ドメイン固有言語(DSL)

18.1. ドメイン固有の言語

ドメイン固有言語 (または DSL)は、問題があるドメイン専用のルール言語を作成する方法です。DSL 定義のセットは、DSL センテンスから DRL コンストラクトへの変換から設定され、基礎となるすべてのルール言語とエンジン機能を使用できるようにします。DRL ファイルに変換される DSL ルール(または DSLR)ファイルにルールを作成できます。
DSL および DSLR ファイルはプレーンテキストファイルで、任意のテキストエディターを使用して作成および変更できます。IDE と Web ベースの BRMS の両方で使用できる DSL および DSLR エディターもありますが、完全な DSL 機能が提供されない場合があります。

18.2. DSL の使用

DSL は、ルールオーサリング(およびルール作成者)と、ドメインオブジェクトのモデル化とルールエンジンのネイティブ言語およびメソッドによる技術的な侵入の層として機能します。DSL は実装の詳細を非表示にし、ルールロジックに適切なことにフォーカスします。DSL の文章は、ルールで繰り返し使用される条件付き要素および結果アクションのテンプレートとしても機能します。場合によっては若干の違いがあります。これらの繰り返しフレーズにマッピングされる DSL 文を、これらのバリエーションを含める手段を提供するパラメーターを使用して定義することができます。

18.3. DSL の例

表18.1 DSL の例

Description
[when]Something is {colour}=Something(colour=="{colour}")
[when] 式の範囲を示します(つまり、これが LHS またはルールの RHS に対して有効であるか)。
bracketed キーワードの後の部分は、ルールで使用する式です。
等号(=")の右側にある部分は、式のルール言語へのマッピングです。この文字列の形式は、宛先、RHS、または LHS によって異なります。LHS の場合、通常の LHS 構文に従って用語である必要があります。RHS の場合は、Java ステートメントである可能性があります。

18.4. DSL パーサーの仕組み

DSL パーサーが DSL 定義の式を使用して DSL で書かれたルールファイルの行と一致するたびに、文字列操作の 3 つの手順を実行します。
  • DSL は、式に変数名が括弧に含まれる文字列値を抽出します。
  • これらのキャプチャーから取得した値は、マッピングの右側の名前が発生した場合に補間されます。
  • 補間文字列は、DSL ルールファイルの行にある式全体によって一致したものを置き換えます。
注記
(たとえば)? を使用して、前の文字がオプションであることを示します。これを使用する適切な理由の 1 つは、DSL の自然言語フレーズのバリエーションを解決することです。ただし、これらの式は正規表現パターンである場合、Java のパターン構文のすべてのワイルドカード文字は、前のバックスラッシュ('\')でエスケープする必要があります。

18.5. DSL コンパイラー

DSL コンパイラーは、DSL ルールファイルの行を行ごとに変換します。これを実行したくない場合は、キャプチャーが文字形式テキスト(単語または単一文字)で囲まれていることを確認してください。その結果、パーサーが行う一致する操作は、行内のどこかから部分文字列を分割します。以下の例では、引用符が区別文字として使用されます。(キャプチャーを囲む文字は補間中に含まれず、それらの間のコンテンツのみが含まれます。)

18.6. DSL 構文の例

表18.2 DSL 構文の例

名前 説明
引用符 ルールエディターが入力する可能性のあるテキストデータには引用符を使用します。キャプチャーを単語で囲むことで、テキストが正しく一致することを確認することもできます。
[when]something is "{color}"=Something(color=="{color}")
[when]another {state} thing=OtherThing(state=="{state}"
中かっこ DSL マッピングでは、変数定義または参照を囲むためにのみ中括弧 "{" と "}" を使用する必要があります。これによりキャプチャーが実行されます。式または右側の代替テキスト内で文字通り発生する場合は、前のバックスラッシュ("\")でエスケープする必要があります。
[then]do something= if (foo) \{ doSomething(); \}
正しい構文を使用したマッピングの例 該当なし
# This is a comment to be ignored.
[when]There is a person with name of "{name}"=Person(name=="{name}")
[when]Person is at least {age} years old and lives in "{location}"=
      Person(age >= {age}, location=="{location}")
[then]Log "{message}"=System.out.println("{message}");
[when]And = and
拡張された DSL の例 該当なし
There is a person with name of "Kitty"
   ==> Person(name="Kitty")
Person is at least 42 years old and lives in "Atlanta"
   ==> Person(age >= 42, location="Atlanta")
Log "boo"
   ==> System.out.println("boo");
There is a person with name of "Bob" and Person is at least 30 years old and lives in "Utah"
   ==> Person(name="Bob") and Person(age >= 30, location="Utah")
注記
DSL ルール行からプレーンテキストをキャプチャーし、拡張で文字列リテラルとして使用する場合は、マッピングの右側にある引用符を指定する必要があります。

18.7. DSL 式の連鎖

DSL 式は 1 行でチェーンでき、一度に使用することができます。1 つの終端が開始し、パラメーターを表すテキストが終了する場所は明確である必要があります。(それ以外の場合は、行がパラメーター値として行が終了するまですべてのテキストを取得するリスクがあります。) DSL 式は、DSL 定義ファイルの順序に従って試行されます。一致すると、残りの DSL 式もすべて調査されます。

18.8. ファクトへの制約の追加

表18.3 ファクトへの制約の追加

名前 説明
LHS 条件の表現
DSL ファシリティーを使用すると、単純な規則によりパターンに制約を追加できます。DSL 式がハイフン(マイナス文字、-)で始まる場合は、フィールド制約であると仮定されるため、が前の最後のパターン行に追加されます。
この例では、Cheese クラスには type、price、age、および country のフィールドがあります。通常の DRL では、一部の LHS 条件を表現できます。
Cheese(age < 5, price == 20, type=="stilton", country=="ch")
DSL 定義
この例の DSL 定義では、3 つの DSL フレーズが作成され、これらのフィールドに関連する制約の組み合わせを作成できます。
[when]There is a Cheese with=Cheese()
[when]- age is less than {age}=age<{age}
[when]- type is '{type}'=type=='{type}'
[when]- country equal to '{country}'=country=='{country}'
"-"
パーサーは-で始まる行を取得し、これを前述のパターンに制約として追加し、必要に応じてコンマを挿入します。
There is a Cheese with
        - age is less than 42
        - type is 'stilton'
Cheese(age<42, type=='stilton')
DSL フレーズの定義
さまざまな演算子と、任意のフィールド制約を処理する汎用式の DSL フレーズを定義すると、DSL エントリーの量が減少します。
[when][]is less than or equal to=<=
[when][]is less than=<
[when][]is greater than or equal to=>=
[when][]is greater than=>
[when][]is equal to===
[when][]equals===
[when][]There is a Cheese with=Cheese()
[when][]- {field:\w*} {operator} {value:\d*}={field} {operator} {value}
DSL 定義ルール 該当なし
There is a Cheese with
   - age is less than 42
   - rating is greater than 50
   - type equals 'stilton'
この場合、is less than などのフレーズは < に置き換えられ、行が最後の DSL エントリーと一致します。これによりハイフンが削除されますが、最終的な結果は引き続き前述のパターンに制約として追加されます。すべての行を処理した後、生成される DRL テキストは以下のようになります。
Cheese(age<42, rating > 50, type=='stilton')
注記
DSL 内のエントリーの順序は、個別の DSL 式が同じ行と一致することが意図されている場合(もう一方の後)に重要です。

18.9. DSL の開発に関するヒント

  • アプリケーションが必要とするルールの代表的なサンプルを記述し、開発時にテストします。
  • DRL および DSLR の両方で、ルールに定義された推論プロセスが適用される必要があるアプリケーションデータを表すデータモデルに応じてエンティティーを参照します。
  • ほとんどのデータモデルのタイプがファクトである場合、ルールの作成が容易になります。
  • 変数の部分をパラメーターとしてマーク付けします。これにより、有用な DSL エントリーの信頼できるリードが提供されます。
  • この最初の設計フェーズでは、特定の条件要素とアクションについて実装の意思決定を延期するには、行をより大きな記号(">)で接頭辞を付けることで、特定の条件要素とアクションを DRL フォームに残すことができます。(これはデバッグステートメントの挿入にも便利です。)
  • 既存の DSL 定義を再利用するか、既存の条件または結果エントリーにパラメーターを追加して、新しいルールを作成することができます。
  • DSL エントリーの数を小さくします。パラメーターを使用すると、同様のルールパターンまたは制約に同じ DSL 文を適用できます。

18.10. DSL および DSLR リファレンス

DSL ファイルは、行指向形式のテキストファイルです。このエントリーは、DRL 構文に従って DSLR ファイルをファイルに変換するために使用されます。
  • "#" または "//" で始まる行(空白の前後または空白なし)は、コメントとして扱われます。デバッグオプションを要求する単語については、#/で始まるコメント行がスキャンされます。以下を参照してください。
  • ブラクト("[")で始まる行は、DSL エントリー定義の最初の行であると想定されます。
  • その他の行は、前の DSL エントリー定義に追加され、行末はスペースに置き換えられます。

18.11. DSL エントリーの作成

DSL エントリーは、以下の 4 つの部分で設定されます。
  • スコープの定義。when、condition、then、または "consequence"、"*" および keyword" のキーワード([" と "]")で囲まれています。これは、DSL エントリーがルールの条件または結果に対して有効であるか、またはその両方であるかを示します。キーワードのスコープは、エントリーにグローバル重要性があることを意味します。つまり、DSLR ファイルのどこにでも認識されます。
  • Java クラス名として記述された型定義。括弧で囲まれたものです。次の部分がオープンブラケットで始まる場合を除き、この部分はオプションになります。括弧の空のペアも有効です。
  • DSL 式は(Java)正規表現で設定されており、任意の数の組み込み 変数定義があり、 等号("=")で終了します。変数の定義は中括弧({" と "}")で囲まれています。これは、コロン(:)で区切られた、変数名と 2 つのオプションの添付ファイルで設定されます。アタッチメントが 1 つある場合は、変数に割り当てられるテキストに一致する正規表現になります。添付ファイルが 2 つある場合には、最初に GUI エディターのヒント、2 つ目は正規表現です。
    正規表現でマジックな文字はすべて、式内で文字通り発生する必要がある場合は、前のバックスラッシュ("\)でエスケープする必要があります。
  • 等号記号の後の行の残りの部分は、正規表現に一致する DSLR テキストの代替テキストです。変数参照、つまり中括弧で囲まれた変数名が含まれる場合があります。必要に応じて、変数名の後に感嘆符("!")と変換関数が続きます。以下を参照してください。
    置き換え文字列内で文字通り発生する必要がある場合は、括弧("{" and "}")を前のバックスラッシュ("\")でエスケープする必要があります。

18.12. DSL 拡張のデバッグオプション

表18.4 DSL 拡張のデバッグオプション

単語 Description
result 作成される DRL テキストを行番号で出力します。
steps 条件および結果行の各拡張ステップを出力します。
キーワード スコープキーワードを使用して、すべての DSL エントリーの内部表現をダンプします。
when スコープ when または "*" ですべての DSL エントリーの内部表現をダンプします。
次に、以下を実行します。 スコープ "then" または "*" ですべての DSL エントリーの内部表現をダンプします。
使用方法 すべての DSL エントリーの使用状況の統計を表示します。

18.13. DSL 定義の例

DSL 定義は以下のようになります。
# Comment: DSL examples

#/ debug: display result and usage

# keyword definition: replaces "regula" by "rule"
[keyword][]regula=rule

# conditional element: "T" or "t", "a" or "an", convert matched word
[when][][Tt]here is an? {entity:\w+}=
        ${entity!lc}: {entity!ucfirst} ()

# consequence statement: convert matched word, literal braces
[then][]update {entity:\w+}=modify( ${entity!lc} )\{ \}

18.14. DSLR ファイルの変換

DSLR ファイルの変換は、以下のようになります。
  1. テキストがメモリーに読み込まれます。
  2. 各キーワードエントリーはテキスト全体に適用されます。キーワード定義の正規表現は、空白シーケンスを、任意の数の空白文字に一致するパターンに置き換え、変数定義を定義で提供される正規表現から行ったキャプチャーに置き換えるか、またはデフォルト(.*?")に置き換えることで変更されます。次に、DSLR テキストが、変更された正規表現に一致する文字列の発生を完全に検索します。変数キャプチャーに対応する一致する文字列のサブ文字列が抽出され、対応する置換テキストの変数参照を置き換えます。このテキストは DSLR テキストの一致する文字列を置き換えます。
  3. when と then と end の間の DSLR テキストのセクションはそれぞれ、以下で説明されているように、行ごとに統一された方法で配置および処理されます。
    行については、DSL ファイルに表示される順序で、行の セクションに関連する各 DSL エントリーが順番に実行されます。この正規表現の部分が変更されました。空白は、任意の数の空白文字に一致するパターンに置き換えられます。正規表現を使用した変数定義は、この正規表現でキャプチャーされ、デフォルトは ".*?" になります。作成される正規表現が行のすべてまたは一部と一致する場合、一致した部分は適切に変更された置換テキストに置き換えられます。
    代替テキストの変更は、変数参照を正規表現キャプチャーに対応するテキストに置き換えて行います。このテキストは、変数参照で指定した文字列変換関数に従って変更できます。詳細は、以下を参照してください。
    同じエントリーに定義されていない変数の名前付けの変数参照がある場合、展開担当者は現在のルールの前の行のいずれかで定義された場合に、その名前の変数にバインドされている値を置き換えます。
  4. 状態の DSLR 行が先頭のハイフンで記述されている場合は、拡張結果が最後の行に挿入されます。この行には、パターン CE、つまり型名の後に括弧のペアが含まれている必要があります。このペアが空の場合は、拡張された行(有効な制約を含む)が単純に挿入されます。それ以外の場合は、コンマ(",")が事前に挿入されます。
    結果内の DSLR 行が先頭のハイフンで書き込まれた場合、拡張された結果は最後の行に挿入されます。この行には modify ステートメントが含まれ、括弧({" と "}")のペアで終わります。このペアが空の場合は、拡張行(有効なメソッド呼び出しを含む)が単純に挿入されます。挿入しないと、コンマ(",")が事前に挿入されます。
注記
現在、先頭のハイフンが付いた行を使用してテキストを他の条件要素フォームに挿入することはでき ません (例:accumulate)。または、最初の挿入(eval など)でのみ機能することもできます。

18.15. string Transformation Functions

表18.5 string Transformation Functions

名前 Description
uc すべての文字を大文字に変換します。
lc すべての文字を小文字に変換します。
ucfirst 最初の文字を大文字に変換し、他のすべての文字を小文字に変換します。
num 文字列からすべての数字と-を抽出します。元の文字列の最後の 2 桁の数字の前に "." または "," が付けられている場合、対応する位置に 10 進数のピリオドが挿入されます。
a?b/c 文字列を文字列で比較 、等しい場合は b に置き換えます。それ以外の場合は c に置き換えます。ただし、c は、構造全体が翻訳テーブルのように、bc などのトリプレットにすることができます。

18.16. DSL 変換機能の文字列設定

表18.6 DSL 変換機能の文字列設定

名前 説明
.dsl
DSL 定義が含まれるファイルには、通常 エクステンション .dsl が付与されます。ResourceType.DSL で Knowledge Builder に渡されます。DSL 定義を使用するファイルの場合、拡張子 .dslr を使用する必要があります。Knowledge Builder には ResourceType.DSLR が必要です。ただし、IDE はファイル拡張子に依存して、ルールファイルを正しく認識して操作します。
# definitions for conditions
[when][]There is an? {entity}=${entity!lc}: {entity!ucfirst}()
[when][]- with an? {attr} greater than {amount}={attr} <= {amount!num}
[when][]- with a {what} {attr}={attr} {what!positive?>0/negative?%lt;0/zero?==0/ERROR}
DSL の合格
DSL は、DSL を使用してルールファイルの前に Knowledge Builder に渡す必要があります。
DSLR ファイルを解析および拡張する場合、DSL 設定は読み取り、パーサーに提供されます。したがって、パーサーは DSL 式を認識し、それらをネイティブルール言語式に変換できます。
KnowledgeBuilder kBuilder = new KnowledgeBuilder();
Resource dsl = ResourceFactory.newClassPathResource( dslPath, getClass() );
kBuilder.add( dsl, ResourceType.DSL );
Resource dslr = ResourceFactory.newClassPathResource( dslrPath, getClass() );
kBuilder.add( dslr, ResourceType.DSLR );

第19章 XML

19.1. XML 形式

警告
XML ルール言語は、Drools 5.x で導入された機能をサポートするように更新されておらず、非推奨の機能を検討してください。
オプションとして、JBoss ルールはネイティブをサポートします DRL の代わりにルール言語。これにより、XML データとしてルールを取得して管理できます。XML 以外の DRL 形式と同様に、XML 形式は可能な限り高速に内部 AST 表現に解析されます(SAX パーサーを使用)。外部変換ステップは必要ありません。

19.2. XML ルールの例

これは、XML のルールの例です。
<?xml version="1.0" encoding="UTF-8"?>

<package name="com.sample"
         xmlns="http://drools.org/drools-5.0"
         xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
         xs:schemaLocation="http://drools.org/drools-5.0 drools-5.0.xsd">

<import name="java.util.HashMap" />
<import name="org.drools.*" />

<global identifier="x" type="com.sample.X" />
<global identifier="yada" type="com.sample.Yada" />

<function return-type="void" name="myFunc">
    <parameter identifier="foo" type="Bar" />
    <parameter identifier="bada" type="Bing" />

    <body>
     System.out.println("hello world");
    </body>
</function>

<rule name="simple_rule">
<rule-attribute name="salience" value="10" />
<rule-attribute name="no-loop" value="true" />
<rule-attribute name="agenda-group" value="agenda-group" />
<rule-attribute name="activation-group" value="activation-group" />

<lhs>
    <pattern identifier="foo2" object-type="Bar" >
            <or-constraint-connective>
                <and-constraint-connective>
                    <field-constraint field-name="a">
                        <or-restriction-connective>
                            <and-restriction-connective>
                                <literal-restriction evaluator=">" value="60" />
                                <literal-restriction evaluator="<" value="70" />
                            </and-restriction-connective>
                            <and-restriction-connective>
                                <literal-restriction evaluator="<" value="50" />
                                <literal-restriction evaluator=">" value="55" />
                            </and-restriction-connective>
                        </or-restriction-connective>
                    </field-constraint>

                    <field-constraint field-name="a3">
                        <literal-restriction evaluator="==" value="black" />
                    </field-constraint>
                </and-constraint-connective>

                <and-constraint-connective>
                    <field-constraint field-name="a">
                        <literal-restriction evaluator="==" value="40" />
                    </field-constraint>

                    <field-constraint field-name="a3">
                        <literal-restriction evaluator="==" value="pink" />
                    </field-constraint>
                </and-constraint-connective>

                <and-constraint-connective>
                    <field-constraint field-name="a">
                        <literal-restriction evaluator="==" value="12"/>
                    </field-constraint>

                    <field-constraint field-name="a3">
                        <or-restriction-connective>
                            <literal-restriction evaluator="==" value="yellow"/>
                            <literal-restriction evaluator="==" value="blue" />
                        </or-restriction-connective>
                    </field-constraint>
                </and-constraint-connective>
            </or-constraint-connective>
        </pattern>

        <not>
            <pattern object-type="Person">
                <field-constraint field-name="likes">
                    <variable-restriction evaluator="==" identifier="type"/>
                </field-constraint>
            </pattern>

            <exists>
                <pattern object-type="Person">
                    <field-constraint field-name="likes">
                        <variable-restriction evaluator="==" identifier="type"/>
                    </field-constraint>
                </pattern>                
            </exists>
        </not>

        <or-conditional-element>
            <pattern identifier="foo3" object-type="Bar" >
                <field-constraint field-name="a">
                    <or-restriction-connective>
                        <literal-restriction evaluator="==" value="3" />
                        <literal-restriction evaluator="==" value="4" />
                    </or-restriction-connective>
                </field-constraint>
                <field-constraint field-name="a3">
                    <literal-restriction evaluator="==" value="hello" />
                </field-constraint>
                <field-constraint field-name="a4">
                    <literal-restriction evaluator="==" value="null" />
                </field-constraint>
            </pattern>

            <pattern identifier="foo4" object-type="Bar" >
                <field-binding field-name="a" identifier="a4" />
                <field-constraint field-name="a">
                    <literal-restriction evaluator="!=" value="4" />
                    <literal-restriction evaluator="!=" value="5" />
                </field-constraint>
            </pattern>
        </or-conditional-element>

        <pattern identifier="foo5" object-type="Bar" >
            <field-constraint field-name="b">
                <or-restriction-connective>
                    <return-value-restriction evaluator="==" >a4 + 1</return-value-restriction>
                    <variable-restriction evaluator=">" identifier="a4" />
                    <qualified-identifier-restriction evaluator="==">
                        org.drools.Bar.BAR_ENUM_VALUE
                    </qualified-identifier-restriction>
                </or-restriction-connective>
            </field-constraint>            
        </pattern>

        <pattern identifier="foo6" object-type="Bar" >
            <field-binding field-name="a" identifier="a4" />
            <field-constraint field-name="b">
                <literal-restriction evaluator="==" value="6" />
            </field-constraint>
        </pattern>
  </lhs>
 <rhs>
    if ( a == b ) {
      assert( foo3 );
    } else {
      retract( foo4 );
    }
    System.out.println( a4 );
   </rhs>
</rule>

</package>

19.3. XML 要素

表19.1 XML 要素

名前 Description
global
ルールで参照できるグローバルオブジェクトを定義します。
function
ルールで使用される関数の関数宣言が含まれます。本文で戻り値のタイプ、一意の名前、およびパラメーターを指定する必要があります。
import
ルールで使用するタイプをインポートします。

19.4. ルール要素の詳細

このルールの例には、LHS セクションおよび RHS (conditions および result)セクションがあります。RHS は、ルールがアクティブになったときに実行されるセマンティックコードのブロックです。LHS には、条件付き要素、制約、制限のネストされた要素が含まれているため、若干複雑です。
<rule name="simple_rule">
<rule-attribute name="salience" value="10" />
<rule-attribute name="no-loop" value="true" />
<rule-attribute name="agenda-group" value="agenda-group" />
<rule-attribute name="activation-group" value="activation-group" />

<lhs>
    <pattern identifier="cheese" object-type="Cheese">
        <from>
            <accumulate>
                <pattern object-type="Person"></pattern>
                <init>
                    int total = 0;
                </init>
                <action>
                    total += $cheese.getPrice();
                </action>
                <result>
                    new Integer( total ) );
                </result>
            </accumulate>
        </from>
    </pattern>

    <pattern identifier="max" object-type="Number">
        <from>
            <accumulate>
                <pattern identifier="cheese" object-type="Cheese"></pattern>
                <external-function evaluator="max" expression="$price"/>
            </accumulate>
        </from>
    </pattern>
</lhs>
<rhs>
    list1.add( $cheese );
</rhs>
</rule>

19.5. XML ルール要素

表19.2 XML ルール要素

要素 Description
Pattern
これにより、型(クラス)を指定し、変数をそのクラスのインスタンスにバインドできます。pattern オブジェクトで入れ子になっているのは、満たさなければならない制約と制約です。Predicate と Return Value の制約により、Java 式を埋め込むことができます。
条件要素(not、exists、および or)
これらの作業は DRL の対応するように機能します。の下にネスト化され、and 要素は論理的に結合されます。または と同じように(さらにネスト化することができます)。パターンの構文および "Not" は回避し、パターンの制約を満たすファクトの存在や、何もないかを確認します。
eval
ブール値を評価する限り、有効な Java コードの有効なスニペットを実行できます(フラグメントであるため、セミコロンで終了しないでください)。これには、関数の呼び出しが含まれます。ルールエンジンは毎回評価する必要があるため、Eval は列よりも効率が低くなりますが、Column 制約で行う必要がある内容を表現できる場合はキャッチ all 機能です。

19.6. XML と DRL 間での自動変換

JBoss ルールには、形式間で変換するユーティリティークラスがいくつか含まれています。これは、ソース形式から AST にルールを解析し、適切なターゲット形式にダンプすることで機能します。これにより、DRL のルールを書き込み、XML にエクスポートできます。

19.7. XML と DRL 間で自動変換を行うためのクラス

これらは、XML ファイルと DRL ファイルの間の変換時に使用するクラスです。これらの組み合わせを使用して、任意の形式(ラウンドトリップを含む)間で変換できます。
  • DrlDumper - DRL をエクスポートする場合
  • DrlParser - DRL を読み取る場合
  • XmlPackageReader - for reading XML
注記
DSL は(DSL を使用している DRL から)保持されませんが、変換が可能です。

第20章 Java Rule Engine アプリケーションプログラミングインターフェイス

20.1. JSR94

JSR94 は JBoss Rules で使用される API です。1 つの API から複数のルールエンジンをサポートできます。JSR94 はルール言語自体を処理しません。
JSR94 標準は、ルールエンジン間の機能における "least common denominator" を表します。つまり、JSR94 API には標準の Knowledge API よりも機能が少なくなります。JSR94 を使用すると、JBoss Rules のすべての機能を使用することはできません。JSR94 と組み合わせてプロパティーマップを使用して、DRL、DSL、および XML のグローバルサポートを使用する必要があります。これにより、移植できない機能が導入されました。
注記
JSR94 はルール言語を提供しないため、ルールエンジンの複雑度がごくわずかに解決されるのはごくわずかです。Red Hat は、ナレッジ(JBoss Rules and jBPM) API に対してプログラムを行うことを推奨します。

20.2. Javax.rules Interfaces

  • handle
    Handle は、StatefulRuleSession で追加された WorkingMemory からオブジェクトを取得するために使用されます。Handle を使用すると、WorkingMemory から オブジェクト を変更または削除できます。StatefulRuleSession からオブジェクト呼び出し updateObject() を変更するには、以下を実行します。これを削除するには、Handle を Parameter として指定して removeObject() を呼び出します。Java Rule Engine API の実装内で、カプセル化されたナレッジ(Drools および jBPM) API の modifyObject() メソッドおよび retractObject() メソッドと呼ばれます。
  • ObjectFilter
    このインターフェイスは、RuleSession のオブジェクトをフィルターするために使用されます。
  • RuleExecutionSetMetadata
    RuleExecutionSetMetadata は、RuleExecutionSet の名前、説明、および URI を保存するために使用されます。
  • RuleRuntime
    RuleRuntime は RuleSession へのキーです。RuleServiceProvider から取得した RuleRuntime。
    RuleRuntime 呼び出し createRuleSession ()を取得して RuleSession を開きます。
    RuleRuntime を使用して、RuleAdministrator によって登録されたすべての RuleExecutionSets の URI の一覧を取得できます。ルールエンジンに対して RuleSession を開くには、URI を文字列として指定する必要があります。ルールエンジンは、RuleSession 内の RuleExecutionSet のルールを使用します。
    マップはグローバルに使用されます。グローバルは、以前は(Drools 2.x で) ApplicationData と呼ばれていました。キーは Global の識別子と、Global として使用するオブジェクトの Value である必要があります。
  • RuleSession
    RuleSession は、ルールエンジンに問い合わせる場合に作業しているオブジェクトです。
    RuleRuntime から RuleSession を取得する場合、StatefulRuleSession または StatelessRuleSession になります。
    すべてのリソースが解放されるように、release ()-method を呼び出します。
  • StatefulRuleSession
    ルールエンジンを複数回実行する必要がある場合は、StatefulRuleSession を実行します。オブジェクトをアサートしたり、ルールを実行したりできます。
    Rule Session にアサートしているすべてのオブジェクトに対して Handle を取得します。それを失わないでください。作業メモリー内のオブジェクトを取り消したり、変更したりする必要があります。実装内で使用する Drools の作業メモリーに直接アクセスしていないので、RuleSession が取得されました。
  • StatelessRuleSession
    StatelessRuleSession は、ルールエンジンへの接続が 1 つだけであることを意味します。ルールエンジンにオブジェクトの一覧を提供し、ルールエンジンがそれらをすべてアサートし、実行をすぐに開始します。結果はオブジェクトの一覧です。結果一覧の内容はルールによって異なります。ルールが作業メモリーからオブジェクトを変更または取り消さない場合は、再度追加したすべてのオブジェクトを取得する必要があります。
    オブジェクトを取得する前に、結果のオブジェクトリストをフィルターリングする ObjectFilter を使用できます。

20.3. Javax.rules Classes

  • RuleServiceProvider
    RuleServiceProvider は、新しい Rule Session を開く必要がある RuleAdministrator または RuleRuntime へのアクセスを提供します。RuleServiceProvider を取得するには、RuleServiceProviderManager.getRuleServiceProvider ()を呼び出します。
    J2EE 環境では、RuleServiceProvider を JNDI にバインドし、ルックアップを作成してすべてのアプリケーションに配置できます。
  • RuleServiceProviderManager
    RuleServiceProvider は、JDBC で使用する DriverManager と比較されることが多くあります。DataBase の Driver の設定と同じように機能します。

20.4. javax.rules 例外

  • ConfigurationException
    この例外は、ユーザー設定エラーが発生すると出力されます。
  • InvalidHandleException
    クライアントが無効なハンドルをルールエンジンに渡すと、この例外が発生します。
  • InvalidRuleSessionException
    RuleSession でメソッドが呼び出され、RuleSession の内部状態が無効になると、InvalidRuleSessionException を出力する必要があります。これは、StatefulRuleSession がシリアライズされ、外部リソースにアクセスできないために発生する可能性があります。この例外は、(リリースメソッドが呼び出された後に使用しようとした場合など)RuleSession が無効な状態にあることを示すためにも使用されます(JCP API ドキュメント から取得)。
  • RuleException
    javax.rules パッケージ内のすべての Exception クラスのベースクラス。
  • RuleExecutionException
    この例外は、Drools 3 JSR 94 実装では出力されません。
  • RuleExecutionSetNotFoundException
    この例外は、クライアントが RuleRuntime から RuleExecutionSet を要求し、URI または RuleExecutionSet が見つからない場合に出力されます(JCP API ドキュメント から必要)。
  • RuleSessionCreateException
    この例外は、クライアントが RuleRuntime から RuleSession を要求し、RuleSession が返されないエラーが発生すると出力されます(JCP API ドキュメント から取得)。
  • RuleSessionTypeUnsupportedException
    この例外は、クライアントが RuleSession を要求し、ベンダーが(RuleRuntime で定義)指定されたタイプをサポートしていない場合、または RuleExecutionSet 自体が要求されたモードをサポートしていない場合(JCP API ドキュメント から取られる)に出力されます。

20.5. ルールサービスプロバイダーの使用

手順20.1 タスク

  1. 以下のコードを使用して JBoss Rules ルールサービスプロバイダーをロードします。
    Class ruleServiceProviderClass = Class.forName("org.drools.jsr94.rules.RuleServiceProviderImpl");
  2. 以下のコードを使用して登録します。
    RuleServiceProviderManager.registerRuleServiceProvider( "http://jboss.com/products/rules", ruleServiceProviderClass);
  3. 以下のコードを使用して RuleServiceProvider を呼び出します。
    RuleServiceProviderManager.getRuleServiceProvider("http://jboss.com/products/rules");
  4. ルールサービスを停止するには、以下のコードで登録を解除します。
    RuleServiceProviderManager.deregisterRuleServiceProvider( "http://jboss.com/products/rules");

20.6. Javax.rules.admin Interfaces

  • LocalRuleExecutionSetProvider
  • **ルール**
  • RuleAdministrator
  • RuleExecutionSet
  • RuleExecutionSetProvider

20.7. Javax.rules.admin Exceptions

  • RuleAdministrationException
    javax.rules.admin パッケージの全管理 RuleException クラスのベースクラス(JCP API ドキュメント から取る)。
  • RuleExecutionSetCreateException
    ルールセットの作成時にエラーが発生した場合に発生します。
  • RuleExecutionSetDeregistrationException
    URI からルールセットの登録を解除しようとすると、エラーが発生します。
  • RuleExecutionSetRegisterException
    URI にルールセットを登録しようとすると、エラーが発生します。

20.8. The RuleServiceProvider

RuleServiceProvider は、RuleRuntime および RuleAdministrator API へのアクセスを提供します。RuleAdministrator は RuleExecutionSet オブジェクトの管理の管理 API を提供し、RuleRuntime で取得できる RuleExecutionSet を登録できるようにします。

20.9. The RuleServiceProviderManager

RuleServiceProviderManager は、RuleServiceProviders の登録と取得を管理します。JBossRules RuleServiceProvider 実装は、JDBC ドライバーとほぼ同じ方法で Class.forName を使用してクラスがロードされたときに、静的ブロックを介して自動的に登録されます。

20.10. RuleServiceProvider 自動登録の例

以下は、RuleServiceProvider の自動登録の例です。
// RuleServiceProviderImpl is registered to "http://drools.org/"
// via a static initialization block
Class.forName("org.drools.jsr94.rules.RuleServiceProviderImpl");

// Get the rule service provider from the provider manager.
RuleServiceProvider ruleServiceProvider =
  RuleServiceProviderManager.getRuleServiceProvider("http://drools.org/");

20.11. RuleAdministrator API を使用した LocalRuleExecutionSet の登録

手順20.2 タスク

  1. RuleExecutionSet を作成します。これは、空の LocalRuleExecutionSetProvider または RuleExecutionSetProvider を返すファクトリーメソッドを提供する RuleAdministrator を使用して実行できます。
  2. RuleExecutionSet の名前を指定します。
  3. RuleExecutionSet を以下のように登録します。
    // Register the RuleExecutionSet with the RuleAdministrator
    String uri = ruleExecutionSet.getName();
    ruleAdministrator.registerRuleExecutionSet(uri, ruleExecutionSet, null);
  4. LocalRuleExecutionSetProvider を使用して、Streams など、シリアライズできないローカルソースから RuleExecutionSets をロードします。
  5. RuleExecutionSetProvider を使用して、DOM 要素やパッケージなどのシリアル化可能なソースから RuleExecutionSets をロードします。"ruleAdministrator.getLocalRuleExecutionSetProvider (null);" と "ruleAdministrator.getRuleExecutionSetProvider (null);" の両方(null をパラメーターとして使用)。
  6. 以下の例は、LocalRuleExecutionSet の登録方法を説明します。
    // Get the RuleAdministration
    RuleAdministrator ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
    LocalRuleExecutionSetProvider ruleExecutionSetProvider =
      ruleAdministrator.getLocalRuleExecutionSetProvider( null );
    
    // Create a Reader for the drl
    URL drlUrl = new URL("http://mydomain.org/sources/myrules.drl");
    Reader drlReader = new InputStreamReader(  drlUrl.openStream()  );
    
    // Create the RuleExecutionSet for the drl
    RuleExecutionSet ruleExecutionSet =
      ruleExecutionSetProvider.createRuleExecutionSet( drlReader, null );
    
  7. "ruleExecutionSetProvider.createRuleExecutionSet (reader, null) " プロパティーを使用して、受信ソースの設定を提供できます。null が渡されると、デフォルトを使用して入力を drl としてロードします。マップに許可されるキーは source および dsl です。キー source は、"drl" または "xml" を値として取ります。
  8. "source" を "drl" に設定して DRL を読み込むか、xml に設定して XML ソースを読み込みます。xml は、dsl キー/値の設定を無視します。dsl キーは、Reader または String (dse の内容)を値として指定できます。以下の dsl の例を参照してください。
    // Get the RuleAdministration
    RuleAdministration ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
    LocalRuleExecutionSetProvider ruleExecutionSetProvider =
      ruleAdministrator.getLocalRuleExecutionSetProvider( null );
    
    // Create a Reader for the drl
    URL drlUrl = new URL("http://mydomain.org/sources/myrules.drl");
    Reader drlReader = new InputStreamReader(  drlUrl.openStream()  );
    
    // Create a Reader for the dsl and a put in the properties map
    URL dslUrl = new URL("http://mydomain.org/sources/myrules.dsl");
    Reader dslReader = new InputStreamReader( dslUrl.openStream()  );
    Map properties = new HashMap();
    properties.put( "source", "drl" );
    properties.put( "dsl", dslReader );
    
    // Create the RuleExecutionSet for the drl and dsl
    RuleExecutionSet ruleExecutionSet =
      ruleExecutionSetProvider.createRuleExecutionSet( reader, properties );
    

20.12. ステートフルおよびステートレスの RuleSession の使用

手順20.3 タスク

  1. 以下に示すように RuleServiceProvider にアクセスしてランタイムを取得します。
    RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();
  2. ルールセッションを作成するには、2 つの RuleRuntime パブリック定数のいずれかを使用します。これらは "RuleRuntime.STATEFUL_SESSION_TYPE" および "RuleRuntime.STATELESS_SESSION_TYPE" で、RuleSession をインスタンス化する RuleExecutionSet に URI が付随します。
  3. 必要に応じて、プロパティーにアクセスしてグローバルを指定します。
  4. createRuleSession (...)メソッドは RuleSession インスタンスを返します。StatefulRuleSession または StatelessRuleSession にキャストする必要があります。
    (StatefulRuleSession) session =
      ruleRuntime.createRuleSession( uri,
                                     null,
                                     RuleRuntime.STATEFUL_SESSION_TYPE );
    session.addObject( new PurchaseOrder( "cheese" ) );
    session.executeRules();
  5. StatelessRuleSession を使用すると、オブジェクトのリストと任意のフィルターを渡す executeRules (List list)のみを呼び出すと、作成されるオブジェクトが返されます。
    (StatelessRuleSession) session =
      ruleRuntime.createRuleSession( uri,
                                     null,
                                     RuleRuntime.STATELESS_SESSION_TYPE );
    List list = new ArrayList();
    list.add( new PurchaseOrder( "even more cheese" ) );
    
    List results = new ArrayList();
    results = session.executeRules( list );

20.13. JSR94 でのグローバルの使用

JSR94 は、RuleSession ファクトリーメソッドに渡されるプロパティーマップを使用して、グローバル(ポータブルでない方法で)をサポートします。最初に DRL または XML ファイルでグローバルを定義する必要があります。定義しないと、例外が発生します。キーは DRL または XML で宣言された識別子を表し、値は実行で使用するインスタンスです。

20.14. JSR94 の例でのグローバルの使用

以下は、JSR94 でグローバルを実装する例です。
java.util.List globalList = new java.util.ArrayList( );
java.util.Map map = new java.util.HashMap( );
map.put( "list", globalList ); 
//Open a stateless Session
StatelessRuleSession srs =
  (StatelessRuleSession) runtime.createRuleSession( "SistersRules",
                                                    map,
                                                    RuleRuntime.STATELESS_SESSION_TYPE );
...
// Persons added to List
// call executeRules( ) giving a List of Objects as parameter
// There are rules which will put Objects in the List
// fetch the list from the map
List list = (java.util.List) map.get("list");
DRL でグローバル list を宣言しないでください。
package SistersRules; 
import org.drools.jsr94.rules.Person; 
global java.util.List list
rule FindSisters 
when 
    $person1 : Person ( $name1:name ) 
    $person2 : Person ( $name2:name ) 
    eval( $person1.hasSister($person2) ) 
then 
    list.add($person1.getName() + " and " + $person2.getName() +" are sisters"); 
    assert( $person1.getName() + " and " + $person2.getName() +" are sisters"); 
end

20.15. JSR94 について

JSR94 の詳細情報が必要な場合は、以下のリンクを参照してください。
  1. Java Rule Engine API の公式 JCP 仕様(JSR 94)
  2. Java Rule Engine API ドキュメント
  3. The Logic From The Bottom Line: An Introduction to The Drools Project.2004 年に TheServiceSide.com で公開されている N. Warehouse Rupp
  4. Getting Started with the Java Rule Engine API (JSR 94): ルールベースのアプリケーションへDr.Qusay H. Mahmoud (2005 年に Sun Developer Network で公開)
  5. Jess および javax.rules API。2003 年に TheServerSide.com で公開されている Ernest Friedman-Hill

第21章 JBoss Developer Studio

21.1. ルール統合開発環境(IDE)

統合開発環境(IDE)は、さまざまな形式でルールを編集してテストし、アプリケーションと統合するための環境を提供します。
JBoss Rules IDE は Eclipse プラグインとして提供され、Eclipse 内からルールを作成して管理したり、アプリケーションとルールを統合できます。これは任意のツールです。JBoss Rules IDE は Red Hat Developer Studio (旧称 JBoss IDE)の一部でもあります。

21.2. ルール IDE 機能

ルール IDE には以下の機能があります。
  1. テキストエディター/グラフィカルルールエディター
    1. DRL 構文を認識し、コンテンツサポートを提供します(概要ビューを含む)。
    2. DSL (ドメイン固有の言語)拡張機能を認識し、コンテンツ支援を提供するエディター。
  2. ルールフローグラフィカルエディター
    プロセスを表す視覚グラフ(ルールフロー)を編集できます。次に、RuleFlow をルールパッケージに適用して、命令的な制御を行うことができます。
  3. 高速な作成ウィザード
    1. ルールプロジェクト
    2. ルールリソース(DRL ファイル)
    3. ドメイン固有の言語
    4. デシジョンテーブル
    5. ルールフロー
  4. ドメイン固有の言語エディター
    1. ユーザーの言語からルール言語へのマッピングの作成および管理
  5. ルールの検証
    1. ルールが入力されると、ルールはバックグラウンドでビルドされ、可能な場合は問題ビューでエラーが報告されます。

21.3. JBoss Rules Runtimes

JBoss Rules ランタイムは、JBoss Rules プロジェクト jar の特定のリリースを表す jar ファイルのコレクションです。ランタイムを作成するには、IDE が選択したリリースを示す必要があります。プラグイン自体に含まれる最新の JBoss Rules プロジェクト jars に基づいて新しいランタイムを作成することもできます。Eclipse ワークスペースにはデフォルトの JBoss Rules ランタイムを指定する必要がありますが、各プロジェクトはデフォルトを上書きし、とくにそのプロジェクトに適切なランタイムを選択できます。

21.4. JBoss Rules Runtime の定義

手順21.1 タスク

  1. JBoss Rules で新しいセッションを作成します。
  2. Window Preferences を選択します。設定ダイアログが表示されます。
  3. このダイアログの左側で、JBoss Rules カテゴリーで Installed JBoss Rules runtimes を選択します。右側のパネルには、現在定義されている JBoss Rules ランタイムが表示されます。
  4. 追加 ボタンをクリックします。ランタイムの名前とファイルシステムの場所を求めるダイアログがポップアップ表示されます。
  5. JBoss Rules Eclipse プラグインからデフォルトの jar ファイルを使用するには、Create a new Drools 5 runtime ... ボタンをクリックして、新しい JBoss Rules ランタイムを自動的に作成します。ファイルブラウザーが表示され、このランタイムを作成するファイルシステムのフォルダーを選択するように求められます。次に、プラグインは必要な依存関係をすべて指定されたフォルダーに自動的にコピーします。
  6. JBoss Rules プロジェクトの特定のリリースを使用するには、必要な JBoss Rules ライブラリーおよび依存関係がすべて含まれるディレクトリーに、ファイルシステム上に作成します。ランタイムに名前を付け、必要なすべての jar を含むこのフォルダーの場所を選択します。OK をクリックします。
  7. ランタイムはインストールされた JBoss Rules ランタイムのテーブルに表示されます。新しく作成されたランタイムの前にあるチェックボックスをクリックして、デフォルトの JBoss Rules ランタイムにします。デフォルトの JBoss Rules ランタイムは、プロジェクト固有のランタイムを選択していないすべての JBoss Rules プロジェクトのランタイムとして使用されます。
  8. デフォルトのランタイムを変更し、デフォルトのランタイムを使用しているすべてのプロジェクトがクラスパスを適宜更新する場合は、Eclipse を再起動します。

21.5. JBoss Rules プロジェクトのランタイムの選択

手順21.2 タスク

  1. New JBoss Rules Project ウィザードを使用して新しい JBoss Rules プロジェクトを作成します。
  2. または、Java オブジェクトを右クリックし、Convert to JBoss Rules Project ダイアログをクリックし、アクションを使用して既存の Java プロジェクトを JBoss Rules プロジェクトに変換します。プラグインは、必要なすべての jar をプロジェクトのクラスパスに自動的に追加します。
  3. オプションで、New JBoss Rules Project ウィザードの最後のページで、プロジェクト固有のランタイムを選択できます。Use default JBoss Rules runtime チェックボックスの選択を解除し、ドロップダウンボックスで適切なランタイムを選択します。
  4. 設定にアクセスし、ランタイムを追加するには、ワークスペース設定に移動し、Configure workspace settings ... をクリックします。
  5. プロジェクトプロパティーを開き、JBoss Rules カテゴリーを選択すると、いつでも JBoss Rules プロジェクトのランタイムを変更できます。Enable project specific settings チェックボックスを選択し、ドロップダウンボックスから適切なランタイムを選択します。
  6. Configure workspace settings ... リンクをクリックします。これにより、現在インストールされているランタイムを示すワークスペース設定が開きます。メニューを使用して、このスペースに新しいランタイムを追加します。Enable project specific settings チェックボックスの選択を解除すると、グローバル設定で定義されているデフォルトのランタイムが使用されます。

21.6. ルールファイルの例

新規作成されたプロジェクトには、src/rules ディレクトリーにルールファイルのサンプル(Sample.drl)と、JBoss Rules エンジンでルールを実行するのに使用できる Java ファイル(DroolsTest.java)のサンプルが含まれます。これは、com.sample パッケージの src/java フォルダーにあります。実行中に必要な他の jar はすべて、JBoss Rules Library と呼ばれるカスタムクラスパスコンテナーのクラスパスに追加されます。

21.7. JBoss Rules Builder

JBoss Rules プラグインは、Eclipse インスタンスに JBoss Rules Builder 機能を追加します。つまり、リソースの変更時にルールをビルドし、検証するすべてのプロジェクトでビルダーを有効にできます。これは Rule Project ウィザードで自動的に行われますが、任意のプロジェクトで手動で有効にすることもできます。ルールを完全検証するには、当然ユニットテストで実行する必要があります。
注記
1 ファイルあたり 500 を超えるルールを持つルールファイルがある場合は、パフォーマンスが低下する可能性があります。これに対応するには、ビルダーをオフにするか、ルールエディターを使用できる .rule ファイルに大きなルールを追加しますが、バックグラウンドで構築されません。

21.8. 新しいルールの作成

手順21.3 タスク

  1. 空のテキストの .drl ファイルを作成します。
  2. ルールをコピーして貼り付けます。
  3. 保存して終了します。
  4. または、Rules Wizard を使用してルールを作成しますが、Ctrl+N を押すか、ツールバーから選択します。
  5. ウィザードは、ルールリソースを生成するための基本的なオプションを要求します。ルールファイルを保存するには、通常、src/rules ディレクトリーを作成し、適切な名前が付けられたサブディレクトリーを作成します。パッケージ名は必須であり、Java のパッケージ名に似ています。(つまり、関連するルールをグループ化するための namespace を確立します。)
  6. 最適なオプションを選択し、Finish をクリックします。

結果

これで、拡張できるルールのスケルトンができました。

21.9. ルールエディター

ルールエディター は、ルールマネージャーと開発者が変更する場所です。ルールエディターは、Eclipse の通常のテキストエディターのパターンに従います。ポップアップコンテンツのサポートを提供します。Ctrl+Space を押すと、ポップアップコンテンツのサポートを呼び出すことができます。
ルールエディターは、.drl (または .rule)拡張子を持つファイルで機能します。通常、これらには関連するルールが含まれますが、同じパッケージ namespace にグループ化される個別のファイルにルールを含めることもできます。これらの DRL ファイルはプレーンテキストファイルです。

21.10. JBoss ルールビュー

ルールを変更する際には、以下のビューの代替手段があります。
ワーキングメモリービュー
JBoss Rules ワーキングメモリーのすべての要素を表示します。
アジェンダビュー
アジェンダのすべての要素を表示します。アジェンダのルールごとに、ルール名とバインド変数が表示されます。
グローバルデータビュー
JBoss Rules のワーキングメモリーで現在定義されているグローバルデータをすべて表示します。
監査ビュー
ルールエンジンの実行中にログに記録されたイベントを含む監査ログをツリー形式で表示するために使用できます。
Rete ビュー
これにより、DRL ファイルの現在の Rete Network が表示されます。DRL エディターウィンドウの下部にある Rete Tree タブをクリックして表示します。Rete Network ビジュアライゼーションを開いた状態で、個別のノードでドラッグアンドドロップを使用して、最適なネットワークの概要を配置できます。また、長方形をドラッグして複数のノードを選択して、グループ全体を移動することもできます。
注記
Rete ビューは、JBoss Rules Builder がプロジェクトのプロパティーに設定されているプロジェクトでのみ機能します。他のプロジェクトでは、回避策を使用できます。現在のプロジェクトの横にある JBoss Rule Project を設定し、Rete ビューで検査するライブラリーと DRL を転送します。DRL エディターの右下のタブをクリックし、Generate Rete View をクリックします。

21.11. JBoss ルールビューの使用

手順21.4 タスク

  1. JBoss Rules ビューを使用できるようにするには、ワーキングメモリーを呼び出して、コードでブレークポイントを作成します。たとえば、workingMemory.fireAllRules() を呼び出す行は、休止を配置するための理想的な場所です。
  2. デバッガーが結合ポイントで停止する場合は、デバッグ変数ビューで作業メモリー変数を選択します。その後、利用可能なビューを使用して、選択した作業メモリーの詳細を表示できます。

21.12. 論理構造の表示

論理構造の表示は、JBoss Rules ビューで使用されます。ワーキングメモリーまたはアジェンダアイテム内の要素の論理構造を切り替えることができます。論理構造を使用すると、より明確な方法で要素のセットを可視化できます。

21.13. 監査ログの作成

手順21.5 タスク

  1. 監査ログを作成するには、ルールエンジンを実行します。新しい監査ログを作成するオプションが表示されます。
  2. 次のコードを入力します。
    StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
    // Create a new Knowledge Runtime Logger, that logs to file.
    // An event.log file is created in the subdirectory log dir (which must exist) of the working directory
    KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger( ksession, "log/event");
    
    ksession.insert(...);
    ksession.fireAllRules();
    
    // stop logging
    logger.close();
  3. Open Log アクション、Audit View の最初のアイコンをクリックしてログを開き、ファイルを選択します。Audit View には、ルールの実行中にログに記録されたすべてのイベントが表示されるようになりました。

21.14. 監査ビューのイベントアイコン

表21.1 監査ビューのイベントアイコン

アイコン Description
緑の角度 オブジェクトが挿入されている。
黄色の角角 オブジェクトが更新されました。
赤色 オブジェクトが削除されました。
右矢印 アクティベーションが作成されている。
左矢印 アクティベーションがキャンセルされました。
青色の diamond アクティベーションが実行されました。
プロセスアイコン ルールフローが開始または終了しました。
アクティビティーアイコン ruleflow-group のアクティブ化または非アクティブ化。
JBoss Rules アイコン ルールパッケージを追加または削除しました。

21.15. 原因を取得するメソッド

アクティベーションの実行時にイベントが発生すると、アクティベーションの実行イベントの子として表示されます。以下のイベントの原因を取得できます。
  1. オブジェクトの変更または取り消しのイベントの原因は、そのオブジェクトの最後のオブジェクトイベントです。これは、オブジェクトをアサートされたイベントまたはそのオブジェクトの最後のオブジェクト変更イベントです。
  2. アクティベーションがキャンセルまたは実行されたイベントの原因は、対応するアクティベーション作成されたイベントです。
注記
イベントを選択すると、監査ビューで原因が緑色で表示されます。アクションを右クリックし、Show Cause メニュー項目を選択できます。これにより、選択したイベントの原因がスクロールされます。

21.16. DSL エディター

DSL エディターは、言語から Rule Expressions へのマッピングの表形式ビューを提供します。Language Expression は、ルールエディターのコンテンツ支援をフィードし、DSL 設定から言語式を提案できるようにします。(ルールエディターは、ルールリソースを編集するために読み込まれると DSL 設定を読み込みます。

21.17. ルール言語マッピング

ルール言語マッピングは、ルールエンジンコンパイラーで言語式をコンパイルする方法を定義します。このルール言語式の形式は、ルールの条件またはアクション部分を対象としているかどうかによって異なります。(たとえば、RHS では Java のスニペットである場合があります。) スコープ アイテムは式が属する場所を示し、when 項目は LHS、続いて RHS を示し、* 項目はどこにも移動できることを意味します。キーワードのエイリアスを作成することもできます。

21.18. ルール言語マッピングの使用

手順21.6 タスク

  1. DSL エディターを開き、マッピングタブを選択します。
  2. マッピング項目(表の行)を選択して、テーブルのテキストフィールドで式とマッピングを確認します。
  3. ダブルクリックするか、編集ボタンを押して編集ダイアログを開きます。
  4. 他のボタンでは、マッピングを削除および追加できます。マッピングがまだ使用中の間は削除しないでください。

21.19. DSL 翻訳コンポーネント

表21.2 DSL 翻訳コンポーネント

名前 duty
parser パーサーは、DSL 行のルールテキストを行で読み取り、言語式の一部に一致しようとします。一致後、中括弧({age} など)間のプレースホルダーに対応する値はルールソースから抽出されます。
プレースホルダー 対応するルール式のプレースホルダーは、対応する値に置き換えられます。たとえば、自然言語式は、フィールドの年齢と場所に基づいて Person タイプのファクトの 2 つの制約にマップされます。{age} および {location} の値は元のルールテキストから抽出されます。
注記
drl 内の特定のルールに言語マッピングを使用しない場合は、式の前に > を付け、コンパイラーは言語定義に従って変換しません。また、ドメイン固有言語はオプションです。ルールがコンパイルされたら、.dsl ファイルも利用できる必要があります。

21.20. 大きな DRL ファイルでの操作に関するヒント

  1. 使用する JDK に応じて、永続的な生成の最大サイズを増やすことができます。これを行うには、で Eclipse を起動します。 -XX:MaxPermSize=###m
  2. 4000 以上のルールセットでは、永続生成を少なくとも 128Mb に設定する必要があります。
  3. .rule 拡張子を持つファイルにルールを配置することができます。バックグラウンドビルダーは、IDE の実行時間を短縮する変更があるたびにコンパイルを試みません。

21.21. ブレークポイントの作成

手順21.7 タスク

  1. ルールのデバッグを容易にするブレークポイントを作成するには、DRL エディターを開き、使用する DRL ファイルを読み込みます。
  2. ブレークポイントを追加する行で、DRL エディターのルールャーをダブルクリックします。ルールブレークポイントは、ルールの結果でのみ作成できることに注意してください。ブレークポイントが許可されていない行をダブルクリックしても何も起こりません。ブレークポイントは、さらにルールャーをダブルクリックすることで削除できます。
  3. ルーラーを右クリックします。Toggle breakpoint アクションが含まれるポップアップメニューが表示されます。ルールブレークポイントは、ルールの結果でのみ作成できることに注意してください。その行でルールブレークポイントが許可されていない場合は、アクションは自動的に無効になります。
  4. アクションをクリックして選択した行にブレークポイントを追加するか、すでにある場合は削除します。
    注記
    Debug パースペクティブには、定義したすべてのブレークポイントの表示、プロパティーの取得、有効化/無効化、削除などに使用できる Breakpoints ビューが含まれます。

21.22. JBoss ルールアプリケーションとしてのデバッグ

手順21.8 タスク

  1. DRL エディターを開きます。
  2. アプリケーションのメインクラスを選択します。
  3. これを右クリックし 、Debug As > サブメニューを 選択し、JBoss Rules Application を選択します。
    または、Debug ... メニュー項目を選択して、デバッグ設定を作成、管理、および実行するための新しいダイアログを開くこともできます。
  4. 左側のツリーで Drools Application 項目を選択し、New launch configuration ボタン(ツリーの上にあるツールバーの左端のアイコン)をクリックします。これにより、最初に選択したメインクラスに基づいて、すでに入力されているプロパティーの一部を持つ新しい設定が作成されます。
  5. デバッグ設定の名前を意味のあるものに変更します。その他のすべてのプロパティーにはデフォルト値を使用できます。
  6. 下部の Debug ボタンをクリックして、アプリケーションのデバッグを開始します。デバッグ設定は一度だけ定義する必要があります。次回 JBoss Rules アプリケーションを実行すると、ツリーで JBoss Rules ツリーノードのサブ要素として以前に定義した設定を選択し、JBoss Rules ボタンをクリックします。Eclipse ツールバーには、前の設定のいずれかを迅速に再実行するためのショートカットボタンも含まれています。

結果

Debug ボタンをクリックすると、アプリケーションは実行を開始し、ブレークポイントが発生すると停止します。JBoss Rules ブレークポイントが発生すると、対応する DRL ファイルが開き、アクティブな行が強調表示されます。Variables ビューには、すべてのルールパラメーターとその値も含まれます。その後、デフォルトの Java デバッグアクションを使用して、再開、終了、ステップオーバーなどの次のステップを決定できます。デバッグビューを使用して、作業メモリーの内容と、その時点で Agenda を検証することもできます。現在実行している作業メモリーが自動的に表示されるため、作業メモリーを選択する必要はありません。

21.23. ルール IDE 設定

ルール IDE には、以下のオプションの設定を可能にするカスタマイズ可能な設定のセットが同梱されます。
Java リソースが変更された場合は、すべてのルールを自動的に再解析します。
Java クラスが変更されたときに、すべてのルールの再構築をトリガーします。
DRL ファイルでの相互参照の許可
DRL ファイルに、別のファイルで定義されている別のリソースを参照させるリソースを指定できるようにします。たとえば、別のファイルで宣言されたタイプを使用して、ファイルにルールを設定できます。このオプションを有効にすると、2 つの異なる DRL ファイルで同じリソース(つまり、同じパッケージに同じ名前の 2 つのルール)を宣言できなくなりました。
内部 Drools クラスの使用
パブリック API で公開されない JBoss Rules クラスの使用を許可または拒否(警告の生成)します。

第22章 Hello World の例

22.1. helloworld 例:ナレッジベースおよびセッションの作成

final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

// this will parse and compile in one step
kbuilder.add(ResourceFactory.newClassPathResource("HelloWorld.drl",
        HelloWorldExample.class), ResourceType.DRL);

// Check the builder for errors
if (kbuilder.hasErrors()) {
    System.out.println(kbuilder.getErrors().toString());
    throw new RuntimeException("Unable to compile \"HelloWorld.drl\".");
}

// get the compiled packages (which are serializable)
final Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();

// add the packages to a KnowledgeBase (deploy the knowledge packages).
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(pkgs);

final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
  • KnowledgeBuilder を使用して、DRL ソースファイルを Package オブジェクトに変換します。このオブジェクトは、ナレッジベースが消費できます。
  • add メソッドは、Resource インターフェイスと Resource Type をパラメーターとして取ります。Resource を使用して、さまざまな場所から DRL ソースファイルを取得できます。この場合、DRL ファイルは ResourceFactory を使用してクラスパスから取得されますが、ディスクファイルまたは URL から取得できます。
  • 異なる名前空間の複数のパッケージを同じナレッジベースに追加できます。
  • ナレッジベースはパッケージを検証しますが、エラー情報には String としてのみアクセスできます。そのため、エラー情報をデバッグしたい場合は、KnowledgeBuilder インスタンスで実行する必要があります。
  • ビルダーにエラーが発生したら、Package コレクションを取得し、KnowledgeBaseFactory から KnowledgeBase をインスタンス化し、パッケージコレクションを追加します。

22.2. helloworld 例:イベントロギングおよび監査

// setup the debug listeners
ksession.addEventListener( new DebugAgendaEventListener() );
ksession.addEventListener( new DebugWorkingMemoryEventListener() );
        
// setup the audit logging
KnowledgeRuntimeLogger logger =
  KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/helloworld");
  • デフォルトのデバッグリスナーは、DebugAgendaEventListenerDebugWorkingMemoryEventListener の 2 つです。これらは、コンソールウィンドウに表示される System.err ストリームにデバッグイベント情報を出力します。
  • KnowledgeRuntimeLogger は、グラフィカルビューアーで表示できる実行監査を提供します。このロガーは、Agenda および Working Memory リスナーに構築された特別な実装です。
  • エンジンの実行が終了したら、logger.close() を呼び出す必要があります。

22.3. helloworld 例:メッセージクラス

public static class Message {
    public static final int HELLO   = 0;
    public static final int GOODBYE = 1;

    private String          message;
    private int             status; 
    ...
}
  • この例で使用される単一のクラスには、メッセージ(String)と、2 つの整数 HELLO または GOODBYE のいずれかであるステータスの 2 つのフィールドがあります。

22.4. helloworld 例:実行

final Message message = new Message();
message.setMessage("Hello World");
message.setStatus(Message.HELLO);
ksession.insert(message);

ksession.fireAllRules();

logger.close();

ksession.dispose();
  • メッセージテキスト Hello World とステータス HELLO で単一の Message オブジェクトが作成され、その時点で fireAllRules() が実行されるエンジンに挿入されます。
  • すべてのネットワーク評価は、挿入時に行われます。プログラムの実行が fireAllRules() メソッドを呼び出すと、エンジンはすでにどのルールに完全に一致し、実行できるかを認識します。
注記
サンプルを Java アプリケーションとして実行するには、以下を実行します。
  1. Eclipse IDE で org.drools.examples.helloworld.HelloWorldExample クラスを開きます。
  2. クラスを右クリックし、Run as... を選択してから Java applicationを選択します。

22.5. helloworld 例:コンソールウィンドウの System.out

Hello 
Goodbye
==>[ActivationCreated(0): rule=Hello World; 
                   tuple=[fid:1:1:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
[ObjectInserted: handle=[fid:1:1:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96];
                 object=org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]
[BeforeActivationFired: rule=Hello World; 
                   tuple=[fid:1:1:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
==>[ActivationCreated(4): rule=Good Bye; 
                   tuple=[fid:1:2:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
[ObjectUpdated: handle=[fid:1:2:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96];
                old_object=org.drools.examples.helloworld.HelloWorldExample$Message@17cec96;
                new_object=org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]
[AfterActivationFired(0): rule=Hello World]
[BeforeActivationFired: rule=Good Bye; 
                   tuple=[fid:1:2:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
[AfterActivationFired(4): rule=Good Bye]
  • fireAllRules() メソッドにブレークポイントを配置し、ksession 変数を選択すると、"Hello World" ルールがすでにアクティベートされ、Agenda で、挿入中にすべてのパターンマッチングの作業がすでに行われていることを確認できます。
  • デバッグリスナーが System.err を出力すると、アプリケーションは System.out に移動します。

22.6. helloworld 例:ルール Hello World

rule "Hello World"
      dialect "mvel"
  when
      m : Message( status == Message.HELLO, message : message )
  then
      System.out.println( message ); 
      modify ( m ) { message = "Goodbye cruel world",
                     status = Message.GOODBYE };
end
  • ルールの LHS (の後)セクションでは、ステータスが Message.HELLO のワーキングメモリーに挿入される各 Message オブジェクトに対してアクティベートされることを示しています。
  • 変数バインディングが 2 つ作成されます。変数 messagemessage 属性にバインドされ、変数 m は一致する Message オブジェクト自体にバインドされます。
  • ルールの RHS (その )または結果部分は、ルールの属性 dialect で宣言されているように MVEL 式言語を使用して記述されます。
  • バインドされた変数 message のコンテンツを System.out に出力すると、ルールは message および xhtml 属性の値を PROMPT にバインドした値に変更します。status Message m
  • MVEL の modify ステートメントを使用すると、1 つのステートメントに割り当てブロックを適用できます。エンジンはブロックの最後に変更を自動的に通知します。

22.7. helloworld 例:Debug as.. の使用 オプション

手順22.1 タスク

  1. このデバッグオプションにアクセスするには、Eclipse IDE で org.drools.examples.HelloWorld クラスを開きます。
  2. クラスを右クリックし、Debug as... を選択してから Drools application を選択します。ルールは、その場所に関する情報とともに表示されます。

22.8. helloworld 例:ルール "Good Bye"

rule "Good Bye"
      dialect "java"
  when
      Message( status == Message.GOODBYE, message : message )
  then
      System.out.println( message ); 
end
  • "java" 方言を指定する "Good Bye" ルールは、ステータスが Message のオブジェクトと一致する以外は、"Hello World" ルールと似ています。 Message.GOODBYE

第23章 顕著性の状態の例

23.1. 顕著性の状態の例:状態クラスの例

public class State {
    public static final int NOTRUN   = 0;
    public static final int FINISHED = 1;

    private final PropertyChangeSupport changes =
        new PropertyChangeSupport( this );

    private String name;
    private int    state;

    ... setters and getters go here...
}
  • State クラスには、名前と現在の状態のフィールドがあります(クラス org.drools.examples.state.Stateを参照してください)。各オブジェクトで使用可能な 2 つの状態は NOTRUNFINISHED です。

23.2. 顕著性の状態例:実行

State a = new State( "A" );
State b = new State( "B" );
State c = new State( "C" );
final State d = new State( "D" );

// By setting dynamic to TRUE, Drools will use JavaBean
// PropertyChangeListeners so you don't have to call modify or update().
boolean dynamic = true;

session.insert( a, dynamic );
session.insert( b, dynamic );
session.insert( c, dynamic );
session.insert( d, dynamic );

session.fireAllRules();
session.dispose(); // Stateful rule session must always be disposed when finished
  • 各インスタンスは Session へとアサートされ、fireAllRules() が呼び出されます。

23.3. 顕著性の状態の例:アプリケーションの実行

手順23.1 タスク

  1. Eclipse IDE で org.drools.examples.state.StateExampleUsingSalience クラスを開きます。
  2. クラスを右クリックし、Run as... を選択してから Java アプリケーション を選択します。以下の出力が表示されます。
    A finished
    B finished
    C finished
    D finished
    

23.4. 顕著性の状態の例:操作による監査ロギングの使用

手順23.2 タスク

  1. 操作によって生成された監査ログを表示するには、IDE を開き、Window をクリックして Show ViewOther. を選択します ( Drools および Audit View )。
  2. 監査ビューで Open Log ボタンをクリックし、< drools-examples-dir>/log/state.log ファイルを選択します。

23.5. 顕著性の状態の例:ルール "Bootstrap"

rule Bootstrap
    when
        a : State(name == "A", state == State.NOTRUN )
    then
        System.out.println(a.getName() + " finished" );
        a.setState( State.FINISHED );
end
結果:
rule "A to B"
    when
        State(name == "A", state == State.FINISHED )
        b : State(name == "B", state == State.NOTRUN )
    then
        System.out.println(b.getName() + " finished" );
        b.setState( State.FINISHED );
end
  • すべてのアクションと対応する変更が作業メモリーに表示されます。
  • NOTRUN 状態の State オブジェクト A のアサーションは Bootstrap ルールをアクティブにしますが、他の State オブジェクトのアサーションはすぐに効果がありません。
  • ルール Bootstrap を実行すると、A の状態が FINISHED に変更されます。これにより、ルール "A to B" が有効になります。

23.6. 顕著性の状態の例:"B to C" ルール

rule "B to C"
        salience 10
    when
        State(name == "B", state == State.FINISHED )
        c : State(name == "C", state == State.NOTRUN )
    then
        System.out.println(c.getName() + " finished" );
        c.setState( State.FINISHED );
end
  • 競合解決ストラテジーにより、エンジンの Agenda が実行するルールを決定できます。
  • "B to C" の顕著性の値が 高いので、(デフォルトの顕著性の値 0 と比較して)最初に実行され、オブジェクト C の状態が FINISHED に変更されます。
  • また、Agenda ビューを使用して Agenda の状態を調査し、デバッグポイントがルール自体に配置され、Agenda ビューが開かれます。

23.7. 顕著性の状態の例:"B to D" ルール

rule "B to D"
    when
        State(name == "B", state == State.FINISHED )
        d : State(name == "D", state == State.NOTRUN )
    then
        System.out.println(d.getName() + " finished" );
        d.setState( State.FINISHED );
end
  • 最後にルール "B to D" が実行され、オブジェクト D の状態が FINISHED に変更されます。
  • および を実行するルールは他にないため、エンジンは停止します。

23.8. 顕著性の状態の例:動的ファクトの挿入

// By setting dynamic to TRUE, JBoss Rules will use JavaBean
// PropertyChangeListeners so you don't have to call modify or update().
final boolean dynamic = true;

session.insert( fact, dynamic );
  • エンジンがファクトプロパティーの変更を確認し、これに対応するには、アプリケーションは変更が発生したことをエンジンに指示する必要があります。これは、modify ステートメントを使用してルールで明示的に実行することも、JavaBeans 仕様 で定義されているようにファクトが PropertyChangeSupport を実装していることをエンジンに認識させることもできます。
  • 上記の例は、PropertyChangeSupport を使用して、ルールで明示的な 変更 ステートメントが不要になる方法を示しています。
  • ファクトが PropertyChangeSupport を実装していることを確認してください。このクラスは org.drools.example.State と同じです。

23.9. 顕著性の状態例:PropertyChangeSupport を使用したセット

public void setState(final int newState) {
    int oldState = this.state;
    this.state = newState;
    this.changes.firePropertyChange( "state",
                                     oldState,
                                     newState );
}
  • org.drools.examples クラスの state のセッター。
  • PropertyChangeListener オブジェクトを使用する場合、各セッターは通知用にいくつかの追加コードを実装する必要があります。

23.10. 顕著性の状態の例:アジェンダグループルール "B to C"

rule "B to C"
      agenda-group "B to C"
      auto-focus true       
  when
      State(name == "B", state == State.FINISHED )      
      c : State(name == "C", state == State.NOTRUN )
  then
      System.out.println(c.getName() + " finished" );
      c.setState( State.FINISHED );
      kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "B to D" ).setFocus();
end
結果:
rule "B to D"
      agenda-group "B to D"
  when
      State(name == "B", state == State.FINISHED )      
      d : State(name == "D", state == State.NOTRUN )
  then
      System.out.println(d.getName() + " finished" );
      d.setState( State.FINISHED );
end
  • デフォルトでは、すべてのルールはアジェンダグループ "MAIN" にあります。
  • "agenda-group" 属性を使用すると、ルールに異なるアジェンダグループを指定できます。最初は、作業メモリーは Agenda グループ "MAIN" にフォーカスします。
  • グループのルールは、グループがフォーカスを受けた場合にのみ実行されます。これは、setFocus() メソッドまたはルール属性 auto-focus を使用して実現できます。
  • auto-focus は、ルールが一致してアクティベートされると、ルールがアジェンダグループに自動的にフォーカスを設定することを意味します。これは、B to D の前に "B to C" ルールを実行できるようにする auto-focus です。
  • "B to C" のルールは、アジェンダグループ "B to D" で setFocus() を呼び出し、アクティブなルールを実行できるようにします。これにより、"B to D" ルールを実行できます。

23.11. 顕著性の状態の例:アジェンダグループルール "B to D"

rule "B to D"
      agenda-group "B to D"
  when
      State(name == "B", state == State.FINISHED )      
      d : State(name == "D", state == State.NOTRUN )
  then
      System.out.println(d.getName() + " finished" );
      d.setState( State.FINISHED );
end

23.12. 顕著性の状態の例:アジェンダグループルール "D to E"

rule "D to E"
  when
      State(name == "D", state == State.FINISHED )      
      e : State(name == "E", state == State.NOTRUN )
  then
      System.out.println(e.getName() + " finished" );
      e.setState( State.FINISHED );
end
これにより、以下のような出力が生成されます。
A finished
B finished
C finished
D finished
E finished
  • StateExampleWithDynamicRules ルールベース に fireAllRules() の後に別のルールを追加します。

第24章 フィボナッチの例

24.1. フィボナッチの例:クラス

public static class Fibonacci {
    private int  sequence;
    private long value;

    public Fibonacci( final int sequence ) {
        this.sequence = sequence;
        this.value = -1;
    }

    ... setters and getters go here...
}
  • sequence フィールドは、フィボナッチ数シーケンスでのオブジェクトの位置を示すために使用されます。
  • value フィールドは、そのシーケンスの位置のフィボナッチオブジェクトの値を示します。-1 を使用して、計算する必要がある値を指定します。

24.2. フィボナッチの例:実行

手順24.1 タスク

  1. Eclipse IED を起動します。
  2. org.drools.examples.fibonacci.FibonacciExample クラスを開きます。
  3. クラスを右クリックし、Run as... を選択してから Java アプリケーション を選択します。

結果

Eclipse はコンソールウィンドウに次の出力を表示します(..snip... を使用)。これは、スペースを節約するために削除された行を示します。

recurse for 50
recurse for 49
recurse for 48
recurse for 47
...snip...
recurse for 5
recurse for 4
recurse for 3
recurse for 2
1 == 1
2 == 1
3 == 2
4 == 3
5 == 5
6 == 8
...snip...
47 == 2971215073
48 == 4807526976
49 == 7778742049
50 == 12586269025

24.3. フィボナッチの例:実行の詳細

ksession.insert( new Fibonacci( 50 ) );
ksession.fireAllRules();
  • Java でこれを使用するには、sequence フィールドが 50 の Fibonacci オブジェクトが 1 つ挿入されます。
  • 他の 49 Fibonacci オブジェクトを挿入するために再帰ルールが使用されます。
  • この例では MVEL 方言を使用しています。つまり、modify キーワードを使用できます。これにより、ブロックセッターアクションが可能になり、エンジンに変更を通知できます。

24.4. フィボナッチの例:再帰ルール

rule Recurse
    salience 10
    when
        f : Fibonacci ( value == -1 )
        not ( Fibonacci ( sequence == 1 ) )
    then
        insert( new Fibonacci( f.sequence - 1 ) );
        System.out.println( "recurse for " + f.sequence );
end
  • Recurse ルールは、値が -1 のアサートされた各 Fibonacci オブジェクトを照合し、現在一致したオブジェクトよりもシーケンスが 1 つ小さい新しい Fibonacci オブジェクトを作成し、アサートします。
  • 1 と同等の sequence フィールドが存在しない場合に Fibonacci オブジェクトが追加されるたびに、ルールは再度一致して実行されます。
  • メモリーにフィボナッチオブジェクト 50 個すべてが存在する場合には、not 条件要素を使用して、ルールの一致を停止します。
  • Recurse ルールには salience の値があるため、Bootstrap ルールの実行前に、50 Fibonacci オブジェクトがすべてアサートされます。
  • Audit ビューに切り替えて、Fibonacci オブジェクトの元のアサーションを表示するには、sequence フィールドが 50 で、Java コードで実行されます。そこから、Audit ビューには、ルールの継続的な再帰が表示されます。ここで、それぞれのアサートされた Fibonacci オブジェクトにより、Recurse ルールが有効になり、再度実行されます。

24.5. フィボナッチの例:ブートストラップルール

rule Bootstrap
    when
        f : Fibonacci( sequence == 1 || == 2, value == -1 ) // multi-restriction
    then 
        modify ( f ){ value = 1 };
        System.out.println( f.sequence + " == " + f.value );
end
  • sequence フィールドが 2 の Fibonacci オブジェクトがアサートされると、ブートストラップルールがマッチし、Recurse ルールとともにアクティベートされます。
  • フィールド sequence のマルチ制限に注意してください。1 または 2 と等価性をテストします。
  • sequence が 1 の Fibonacci オブジェクトがアサートされると、Bootstrap ルールが再びマッチし、このルールで 2 つのアクティベーションが発生します。sequence が 1 の Fibonacci オブジェクトが存在するとすぐに、not 条件要素がルールの一致を停止するため、Recurse ルールは一致およびアクティブ化されません。

24.6. フィボナッチの例:ルールの計算

rule Calculate
    when
        // Bind f1 and s1
        f1 : Fibonacci( s1 : sequence, value != -1 )
        // Bind f2 and v2; refer to bound variable s1
        f2 : Fibonacci( sequence == (s1 + 1), v2 : value != -1 )
        // Bind f3 and s3; alternative reference of f2.sequence
        f3 : Fibonacci( s3 : sequence == (f2.sequence + 1 ), value == -1 )      
    then
        // Note the various referencing techniques.
        modify ( f3 ) { value = f1.value + v2 };
        System.out.println( s3 + " == " + f3.value );
end
  • 値が -1 以外の Fibonacci オブジェクトが 2 つある場合には、Calculate ルールがそれらを一致させることができます。
  • 作業メモリーには 50 個の Fibonacci オブジェクトがあります。それぞれの値を順番に計算するには、適切なトリプルを選択する必要があります。
  • フィールド制約のないルールでフィボナッチパターン 3 つを使用して、複数の商品を絞り込むと、多くの誤ったルールが実行されることになります。Calculate ルールは、フィールドの制約を使用して、正しい順序でフィボナッチパターンを正しく制約します。この手法は、に 一致するクロス製品 と呼ばれます。
  • 最初のパターンでは、値が != -1 の Fibonacci を検索して、パターンとフィールドの両方をバインドします。2 番目の Fibonacci もこれを実行しますが、追加のフィールド制約を追加して、シーケンスが f1 にバインドされている Fibonacci よりも 1 つ大きいようにします。このルールが初めて実行されると、f1 がシーケンス 1 および f2 がシーケンス 2 を参照するようにします。最後のパターンでは、値が -1 と同等で、シーケンスが f2 よりも大きい Fibonacci を検索します。
  • 利用可能なクロス製品から 3 つのオブジェクトが正しく選択されています。Fibonaccif3 にバインドされる 3 番目の Fibonacci オブジェクトの値を計算できます。
  • modify ステートメントは、f3 にバインドされる Fibonacci オブジェクトの値を更新します。これは、値が -1 以外の Fibonacci オブジェクトが新たに存在することを意味します。これにより、Calculate ルールを再一致させ、次のフィボナッチ番号を算出することができます。
  • Audit ビューに切り替えると、最後のブートストラップが Fibonacci オブジェクトを変更する方法が示され、"Calculate" ルールが一致できるようになります。次に、別の Fibonacci オブジェクトが変更され、Calculate ルールが再び一致できるようになります。これは、すべての Fibonacci オブジェクトに値が設定されるまで継続されます。

第25章 銀行の例

25.1. 銀行の例:RuleRunner

public class RuleRunner {

    public RuleRunner() {
    }

    public void runRules(String[] rules,
                         Object[] facts) throws Exception {

        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

        for ( int i = 0; i < rules.length; i++ ) {
            String ruleFile = rules[i];
            System.out.println( "Loading file: " + ruleFile );
            kbuilder.add( ResourceFactory.newClassPathResource( ruleFile,
                                                                RuleRunner.class ),
                          ResourceType.DRL );
        }

        Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();
        kbase.addKnowledgePackages( pkgs );
        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

        for ( int i = 0; i < facts.length; i++ ) {
            Object fact = facts[i];
            System.out.println( "Inserting fact: " + fact );
            ksession.insert( fact );
        }

        ksession.fireAllRules();
    }
  • クラス RuleRunner は、データセットに対して 1 つ以上の DRL ファイルを実行するために使用されます。パッケージをコンパイルし、実行ごとにナレッジベースを作成します。これにより、各シナリオを簡単に実行し、出力を検査できます。

25.2. 銀行の例:Example1.drl のルール

rule "Rule 01"   
    when
        eval( 1==1 )
    then
        System.out.println( "Rule 01 Works" );
end
出力:
Loading file: Example1.drl
Rule 01 Works
  • このルールには、常に true になる eval 条件が 1 つあるため、このルールは開始後に一致して実行されます。
  • 出力には、ルールが一致し、単一の print ステートメントを実行します。

25.3. 銀行の例:Java の例 2

public class Example2 {
    public static void main(String[] args) throws Exception {
        Number[] numbers = new Number[] {wrap(3), wrap(1), wrap(4), wrap(1), wrap(5)};
        new RuleRunner().runRules( new String[] { "Example2.drl" },
                                   numbers );
    }
    
    private static Integer wrap( int i ) {
        return new Integer(i);
    }
}
  • この例では、基本ファクトをアサートして出力します。

25.4. 銀行の例:Example2.drl のルール

rule "Rule 02"
    when
        Number( $intValue : intValue )
    then
        System.out.println( "Number found with value: " + $intValue ); 
end
出力:
Loading file: Example2.drl
Inserting fact: 3
Inserting fact: 1
Inserting fact: 4
Inserting fact: 1
Inserting fact: 5
Number found with value: 5
Number found with value: 1
Number found with value: 4
Number found with value: 1
Number found with value: 3
  • これは、指定した数字を出力するための基本的なルールです。Number オブジェクトであるファクトを特定し、値を出力します。抽象クラス Number を使用していることに注意してください。
  • パターンマッチングエンジンは、アサートされたオブジェクトのインターフェイスとスーパークラスを照合できます。
  • 出力には、DRL が読み込まれ、ファクトが挿入され、一致したルールと実行されたルールが表示されます。挿入された各番号が一致し、実行されると、出力されることが分かります。

25.5. 銀行の例:Example3.java

public class Example3 {
    public static void main(String[] args) throws Exception {
        Number[] numbers = new Number[] {wrap(3), wrap(1), wrap(4), wrap(1), wrap(5)};
        new RuleRunner().runRules( new String[] { "Example3.drl" },
                                   numbers );
    }
    
    private static Integer wrap(int i) {
        return new Integer(i);
    }
}
  • これは、基本的なルールベースのソート手法です。

25.6. 銀行の例:Example3.drl のルール

rule "Rule 03"
    when
        $number : Number( )
        not Number( intValue < $number.intValue )
    then
        System.out.println("Number found with value: " + $number.intValue() ); 
        retract( $number );
end
出力:
Loading file: Example3.drl
Inserting fact: 3
Inserting fact: 1
Inserting fact: 4
Inserting fact: 1
Inserting fact: 5
Number found with value: 1
Number found with value: 1
Number found with value: 3
Number found with value: 4
Number found with value: 5
  • ルールの最初の行は Number を識別し、値を抽出します。
  • 2 行目は、最初のパターンで見つかった数値よりも小さい番号が存在しないことを保証します。出力後の数字の取り消しは、最小数が削除され、次の最小数を示していることを意味します。

25.7. 銀行の例:Class Cashflow

public class Cashflow {
    private Date   date;
    private double amount;

    public Cashflow() {
    }

    public Cashflow(Date date, double amount) {
        this.date = date;
        this.amount = amount;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public String toString() {
        return "Cashflow[date=" + date + ",amount=" + amount + "]";
    }
}
  • クラス Cashflow には、日付と数量の 2 つの単純な属性があります。(浮動小数点数はほとんどの数値を正確に表すことができないため、通貨単位にタイプ double を使用することは一般的に適切ではありません。)
  • 値を設定するオーバーロードされたコンストラクターがあり、cashflow を出力するメソッド toString があります。

25.8. 銀行の例:Example4.java

public class Example4 {
    public static void main(String[] args) throws Exception {
        Object[] cashflows = {
            new Cashflow(new SimpleDate("01/01/2007"), 300.00),
            new Cashflow(new SimpleDate("05/01/2007"), 100.00),
            new Cashflow(new SimpleDate("11/01/2007"), 500.00),
            new Cashflow(new SimpleDate("07/01/2007"), 800.00),
            new Cashflow(new SimpleDate("02/01/2007"), 400.00),
        };
        
        new RuleRunner().runRules( new String[] { "Example4.drl" },
                                   cashflows );
    }
}
  • この例の Java コードは、日付や量が異なる 5 つの Cashflow オブジェクトを挿入します。

25.9. 銀行の例:Class SimpleDate

public class SimpleDate extends Date {
    private static final SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
    
    public SimpleDate(String datestr) throws Exception {             
        setTime(format.parse(datestr).getTime());
    }
}
  • 便利なクラス SimpleDatejava.util.Date を拡張し、String を入力として取り、日付形式を定義するコンストラクターを提供します。

25.10. 銀行の例:Example4.drl のルール

rule "Rule 04"   
    when
        $cashflow : Cashflow( $date : date, $amount : amount )
        not Cashflow( date < $date)
    then
        System.out.println("Cashflow: "+$date+" :: "+$amount);  
        retract($cashflow);
end
出力:
Loading file: Example4.drl
Inserting fact: Cashflow[date=Mon Jan 01 00:00:00 GMT 2007,amount=300.0]
Inserting fact: Cashflow[date=Fri Jan 05 00:00:00 GMT 2007,amount=100.0]
Inserting fact: Cashflow[date=Thu Jan 11 00:00:00 GMT 2007,amount=500.0]
Inserting fact: Cashflow[date=Sun Jan 07 00:00:00 GMT 2007,amount=800.0]
Inserting fact: Cashflow[date=Tue Jan 02 00:00:00 GMT 2007,amount=400.0]
Cashflow: Mon Jan 01 00:00:00 GMT 2007 :: 300.0
Cashflow: Tue Jan 02 00:00:00 GMT 2007 :: 400.0
Cashflow: Fri Jan 05 00:00:00 GMT 2007 :: 100.0
Cashflow: Sun Jan 07 00:00:00 GMT 2007 :: 800.0
Cashflow: Thu Jan 11 00:00:00 GMT 2007 :: 500.0
  • Cashflow を特定し、日付と時刻が抽出されます。
  • ルールの 2 行目では、見つかったものよりも前の日付の Cashflow がないかどうかを判断します。
  • その結果、Cashflow が出力されます。これはルールを満たしてから取り除くことで、次の Cashflow を最も早いものにします。

25.11. 銀行の例:Class TypedCashflow

public class TypedCashflow extends Cashflow {
    public static final int CREDIT = 0;
    public static final int DEBIT  = 1;

    private int             type;

    public TypedCashflow() {
    }

    public TypedCashflow(Date date, int type, double amount) {
        super( date, amount );
        this.type = type;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String toString() {
        return "TypedCashflow[date=" + getDate() +
               ",type=" + (type == CREDIT ? "Credit" : "Debit") +
               ",amount=" + getAmount() + "]";
    }
}
  • Cashflow を拡張すると、TypedCashflow が発生します。これはクレジットまたはデビット操作になります。

25.12. 銀行の例:Example5.java

クラス:
public class Example5 {
    public static void main(String[] args) throws Exception {      
        Object[] cashflows = {
            new TypedCashflow(new SimpleDate("01/01/2007"),    
                              TypedCashflow.CREDIT, 300.00),
            new TypedCashflow(new SimpleDate("05/01/2007"),
                              TypedCashflow.CREDIT, 100.00),
            new TypedCashflow(new SimpleDate("11/01/2007"),
                              TypedCashflow.CREDIT, 500.00),
            new TypedCashflow(new SimpleDate("07/01/2007"),
                              TypedCashflow.DEBIT, 800.00),
            new TypedCashflow(new SimpleDate("02/01/2007"),
                              TypedCashflow.DEBIT, 400.00),
        };
        
        new RuleRunner().runRules( new String[] { "Example5.drl" },
                                   cashflows );
    }
}
rule:
rule "Rule 05"  
    when
        $cashflow : TypedCashflow( $date : date,
                                   $amount : amount,
                                   type == TypedCashflow.CREDIT )
        not TypedCashflow( date < $date,
                           type == TypedCashflow.CREDIT )
    then
        System.out.println("Credit: "+$date+" :: "+$amount);   
        retract($cashflow);
end
出力:
Loading file: Example5.drl
Inserting fact: TypedCashflow[date=Mon Jan 01 00:00:00 GMT 2007,type=Credit,amount=300.0]
Inserting fact: TypedCashflow[date=Fri Jan 05 00:00:00 GMT 2007,type=Credit,amount=100.0]
Inserting fact: TypedCashflow[date=Thu Jan 11 00:00:00 GMT 2007,type=Credit,amount=500.0]
Inserting fact: TypedCashflow[date=Sun Jan 07 00:00:00 GMT 2007,type=Debit,amount=800.0]
Inserting fact: TypedCashflow[date=Tue Jan 02 00:00:00 GMT 2007,type=Debit,amount=400.0]
Credit: Mon Jan 01 00:00:00 GMT 2007 :: 300.0
Credit: Fri Jan 05 00:00:00 GMT 2007 :: 100.0
Credit: Thu Jan 11 00:00:00 GMT 2007 :: 500.0
  • クラスと .drl の両方がルールエンジンに提供されます。
  • クラスでは、Cashflow オブジェクトのセットが作成され、クレジットまたはデビット操作のいずれかになります。
  • Cashflow ファクトは、CREDIT のタイプで識別され、日付と時刻を抽出します。ルールの 2 行目では、見つかったものよりも前の日付と同じタイプの Cashflow がないことを確認します。その結果、パターンを満たしている cashflow を出力してから取り消し、次に最も早いタイプの cashflow に向けてします。

25.13. 銀行の例:クラスアカウント

public class Account {
    private long   accountNo;
    private double balance = 0;

    public Account() {
    }

    public Account(long accountNo) {
        this.accountNo = accountNo;
    }

    public long getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(long accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public String toString() {
        return "Account[" + "accountNo=" + accountNo + ",balance=" + balance + "]";
    }
}
  • 2 つの別個の Account オブジェクトが作成され、Rule Engine に渡される前に Cashflows オブジェクトに挿入されます。

25.14. 銀行の例:Class AllocatedCashflow

public class AllocatedCashflow extends TypedCashflow {
    private Account account;

    public AllocatedCashflow() {
    }

    public AllocatedCashflow(Account account, Date date, int type, double amount) {
        super( date, type, amount );
        this.account = account;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public String toString() {
        return "AllocatedCashflow[" +
               "account=" + account +
               ",date=" + getDate() + 
               ",type=" + (getType() == CREDIT ? "Credit" : "Debit") + 
               ",amount=" + getAmount() + "]";
    }
}
  • TypedCashflow を拡張すると、Account 参照が含まれる AllocatedCashflow になります。

25.15. 銀行の例:Example5.java の拡張

public class Example6 {
    public static void main(String[] args) throws Exception {      
        Account acc1 = new Account(1);
        Account acc2 = new Account(2);
           
        Object[] cashflows = {
            new AllocatedCashflow(acc1,new SimpleDate("01/01/2007"),
                                  TypedCashflow.CREDIT, 300.00),
            new AllocatedCashflow(acc1,new SimpleDate("05/02/2007"),
                                  TypedCashflow.CREDIT, 100.00),
            new AllocatedCashflow(acc2,new SimpleDate("11/03/2007"),
                                  TypedCashflow.CREDIT, 500.00),
            new AllocatedCashflow(acc1,new SimpleDate("07/02/2007"),
                                  TypedCashflow.DEBIT,  800.00),
            new AllocatedCashflow(acc2,new SimpleDate("02/03/2007"),
                                  TypedCashflow.DEBIT,  400.00),
            new AllocatedCashflow(acc1,new SimpleDate("01/04/2007"),    
                                  TypedCashflow.CREDIT, 200.00),
            new AllocatedCashflow(acc1,new SimpleDate("05/04/2007"),
                                  TypedCashflow.CREDIT, 300.00),
            new AllocatedCashflow(acc2,new SimpleDate("11/05/2007"),
                                  TypedCashflow.CREDIT, 700.00),
            new AllocatedCashflow(acc1,new SimpleDate("07/05/2007"),
                                  TypedCashflow.DEBIT,  900.00),
            new AllocatedCashflow(acc2,new SimpleDate("02/05/2007"),
                                  TypedCashflow.DEBIT,  100.00)           
        };
        
        new RuleRunner().runRules( new String[] { "Example6.drl" },
                                   cashflows );
    }
}
  • この Java コードは 2 つの Account オブジェクトを作成し、それらのいずれかをコンストラクター呼び出しの各 cashflow に渡します。

25.16. 銀行の例:Example6.drl のルール

rule "Rule 06 - Credit"  
    when
        $cashflow : AllocatedCashflow( $account : account,
                                       $date : date,
                                       $amount : amount,
                                       type == TypedCashflow.CREDIT )
        not AllocatedCashflow( account == $account, date < $date)
    then
        System.out.println("Credit: " + $date + " :: " + $amount);     
        $account.setBalance($account.getBalance()+$amount);
        System.out.println("Account: " + $account.getAccountNo() +
                           " - new balance: " + $account.getBalance());          
        retract($cashflow);
end

rule "Rule 06 - Debit"  
    when
        $cashflow : AllocatedCashflow( $account : account,
                            $date : date,
                            $amount : amount,
                            type == TypedCashflow.DEBIT )
        not AllocatedCashflow( account == $account, date < $date)
    then
        System.out.println("Debit: " + $date + " :: " + $amount);      
        $account.setBalance($account.getBalance() - $amount);
        System.out.println("Account: " + $account.getAccountNo() +
                           " - new balance: " + $account.getBalance());           
        retract($cashflow);
end
出力:
Loading file: Example6.drl
Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon Jan 01 00:00:00 GMT 2007,type=Credit,amount=300.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon Feb 05 00:00:00 GMT 2007,type=Credit,amount=100.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Sun Mar 11 00:00:00 GMT 2007,type=Credit,amount=500.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Wed Feb 07 00:00:00 GMT 2007,type=Debit,amount=800.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Fri Mar 02 00:00:00 GMT 2007,type=Debit,amount=400.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Sun Apr 01 00:00:00 BST 2007,type=Credit,amount=200.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Thu Apr 05 00:00:00 BST 2007,type=Credit,amount=300.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Fri May 11 00:00:00 BST 2007,type=Credit,amount=700.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon May 07 00:00:00 BST 2007,type=Debit,amount=900.0]
Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Wed May 02 00:00:00 BST 2007,type=Debit,amount=100.0]
Debit: Fri Mar 02 00:00:00 GMT 2007 :: 400.0
Account: 2 - new balance: -400.0
Credit: Sun Mar 11 00:00:00 GMT 2007 :: 500.0
Account: 2 - new balance: 100.0
Debit: Wed May 02 00:00:00 BST 2007 :: 100.0
Account: 2 - new balance: 0.0
Credit: Fri May 11 00:00:00 BST 2007 :: 700.0
Account: 2 - new balance: 700.0
Credit: Mon Jan 01 00:00:00 GMT 2007 :: 300.0
Account: 1 - new balance: 300.0
Credit: Mon Feb 05 00:00:00 GMT 2007 :: 100.0
Account: 1 - new balance: 400.0
Debit: Wed Feb 07 00:00:00 GMT 2007 :: 800.0
Account: 1 - new balance: -400.0
Credit: Sun Apr 01 00:00:00 BST 2007 :: 200.0
Account: 1 - new balance: -200.0
Credit: Thu Apr 05 00:00:00 BST 2007 :: 300.0
Account: 1 - new balance: 100.0
Debit: Mon May 07 00:00:00 BST 2007 :: 900.0
Account: 1 - new balance: -800.0
  • この例では、日付順序で各 cashflow が適用され、計算され、バランスを出力します。
  • クレジットとデビットの個別のルールがありますが、以前の cashflows をチェックするときに型が指定されていません。これは、cashflow タイプに関係なく、すべての cashflows が日付順に適用されるためです。
  • アカウントが特定され、その結果として cashflow の量が更新されます。

第26章 課金ルールの例

26.1. 課金ルールの例:価格ルールの例の実行

手順26.1 タスク

  1. コンソールを開きます。
  2. PricingRuleDTExample.java ファイルを開き、Java アプリケーションとして実行します。コンソールウィンドウで以下の出力が生成されます。
    Cheapest possible
    BASE PRICE IS: 120
    DISCOUNT IS: 20
  3. 以下のコードを使用してサンプルを実行します。
    DecisionTableConfiguration dtableconfiguration =
        KnowledgeBuilderFactory.newDecisionTableConfiguration();
            dtableconfiguration.setInputType( DecisionTableInputType.XLS );
    
            KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    
            Resource xlsRes = ResourceFactory.newClassPathResource( "ExamplePolicyPricing.xls",
                                                                    getClass() );
            kbuilder.add( xlsRes,
                          ResourceType.DTABLE,
                          dtableconfiguration );
    
    DecisionTableConfiguration オブジェクトのタイプは DecisionTableInputType.XLS に設定されます。
    この例では、DriverPolicy の 2 つのファクトタイプが使用されます。いずれもデフォルト値で使用されます。Driver は 30 歳で、以前の請求機能がなく、現在リスクプロファイルは LOW です。適用される PolicyCOMPREHENSIVE で、まだ承認されていません。

26.2. 課金ルールの例:デシジョンテーブルの設定

図26.1 デシジョンテーブルの設定

デシジョンテーブルの設定
  • RuleSet 宣言はパッケージ名を提供します。ここに配置できる他のオプション項目もあります。たとえば、グローバル変数の場合は Variables、クラスをインポートする場合は Imports です。この場合、ルールの名前空間はファクトクラスと同じであるため、省略できます。
  • RuleTable 宣言の後の名前(価格括弧)は、生成されたすべてのルールの接頭辞として使用されます。
  • CONDITION または ACTION は、条件の一部または生成されるルールの結果を形成するかどうかに関係なく、列の目的を示します。
  • ドライバーのデータは 3 つのセルに分散されます。つまり、その下のテンプレート式が適用されます。ドライバーの年齢範囲(コンマ区切りの値で $1 および $2 を使用)、locationRiskProfile、およびそれぞれの列で確認することができます。priorClaims
  • ポリシーベース価格とメッセージログを Action 列に設定できます。

26.3. 課金ルールの例:ベース価格の計算例

図26.2 基本価格計算の例

基本価格計算の例
  • カテゴリーの括弧は、左端の列のコメントで示されます。
  • ドライバーの詳細は、以前に誤って識別されず 30 歳であるため、行番号 18 に一致します。これにより、120 のベース価格が提供されます。

26.4. 課金ルールの例:ディスカウント計算の例

図26.3 割引計算の例

割引計算の例
  • Age ブラケット、以前の請求数、およびポリシータイプからの割引結果。
  • ドライバーは以前の要求のない 30 であり、COMPREHENSIVE ポリシーに適用されます。これは、20% の割引を適用することができることを意味します。これは実際には同じワークシート内の別個のテーブルであるため、異なるテンプレートが適用されることに注意してください。
  • ルールエンジンのすべての通常のメカニズムが適用されるため、ルールの評価は必ずしも指定の順序で行われるわけではありません。

第27章 ペットショップの例

27.1. ペットショップの例

ペットショップの例のすべての Java コードは PetStore.java ファイルに含まれます。Swing イベントを処理する複数のクラスに加えて、以下のプリンシパルクラスを定義します。
  • Petstore には main() メソッドが含まれます。
  • PetStoreUI Swing ベースの GUI を作成して表示します。これにはいくつかの小さなクラスが含まれており、マウスボタンのクリックなど、さまざまな GUI イベントに主に応答します。
  • TableModel テーブルデータを保持します。Swing クラス AbstractTableModel を拡張する JavaBean です。
  • CheckoutCallback GUI がルールと対話できるようにします。
  • Ordershow お客様が購入したいアイテムを保持します。
  • Purchase 注文の詳細と、お客様が購入した製品を保存します。
  • Product は、購入可能な製品とその価格の詳細を保持する JavaBean です。

27.2. ペットショップの例:PetStore.main での PetStore RuleBase の作成

KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

kbuilder.add( ResourceFactory.newClassPathResource( "PetStore.drl",
                                                    PetStore.class ),
              ResourceType.DRL );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

// Create the stock.
Vector<Product> stock = new Vector<Product>();
stock.add( new Product( "Gold Fish", 5 ) );
stock.add( new Product( "Fish Tank", 25 ) );
stock.add( new Product( "Fish Food", 2 ) );

// A callback is responsible for populating the
// Working Memory and for firing all rules.
PetStoreUI ui = new PetStoreUI( stock,
                                new CheckoutCallback( kbase ) );
ui.createAndShowGUI();
  • 上記のコードは、クラスパスの DRL ファイルからルールを読み込みます。これは、コンストラクターを使用して PetStoreUI オブジェクトが作成される 2 番目の行を介して行われます。これは、製品を収集する Vector オブジェクト stock を受け入れます。
  • CheckoutCallback クラスには、読み込まれた Rule Base が含まれます。

27.3. ペットショップの例:CheckoutCallBack.checkout ()からのファアリングルール

public String checkout(JFrame frame, List<Product> items) {
    Order order = new Order();

    // Iterate through list and add to cart
    for ( Product p: items ) {
        order.addItem( new Purchase( order, p ) );
    }

    // Add the JFrame to the ApplicationData to allow for user interaction

    StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
    ksession.setGlobal( "frame", frame );
    ksession.setGlobal( "textArea", this.output );

    ksession.insert( new Product( "Gold Fish", 5 ) );
    ksession.insert( new Product( "Fish Tank", 25 ) );
    ksession.insert( new Product( "Fish Food", 2 ) );

    ksession.insert( new Product( "Fish Food Sample", 0 ) );

    ksession.insert( order );
    ksession.fireAllRules();

    // Return the state of the cart
    return order.toString();
}
  • ルールを実行する Java コードは CheckoutCallBack.checkout() メソッド内にあります。これは、ユーザーが Checkout ボタンを押すと(最終的に)トリガーされます。
  • 2 つの項目がこのメソッドに渡されます。1 つは、JFrame Swing コンポーネントのハンドルで、GUI の下部にある出力テキストフレームを囲みます。2 つ目は注文アイテムの一覧です。これは、GUI の右上にある "Table" エリアからの情報を保存する TableModel から取得されます。
  • for ループは GUI からの注文アイテム一覧を Order JavaBean に変換します。これは、PetStore.java ファイルにも含まれています。
  • この例のすべての状態は Swing コンポーネントに保存されます。ルールは事実上ステートレスです。
  • "Checkout" ボタンが押すたびに、コードは Swing TableModel の内容をセッションのワーキングメモリーにコピーします。
  • ワーキングメモリーには 9 つの呼び出しがあります。まず、ナレッジベースから、ステートフルナレッジセッションとして新しい作業メモリーを作成します。次の 2 つのオブジェクトは、ルールにグローバル変数として保持される 2 つのオブジェクトを渡します。Swing テキスト領域とメッセージの書き込みに使用される Swing フレーム。
  • 作業メモリーと注文一覧に製品の情報をさらに挿入します。最後の呼び出しは、標準 fireAllRules() です。

27.4. ペットショップの例:PetStore.drl からのパッケージ、インポート、グローバル、および Dialect

package org.drools.examples

import org.drools.WorkingMemory
import org.drools.examples.petstore.PetStoreExample.Order
import org.drools.examples.petstore.PetStoreExample.Purchase
import org.drools.examples.petstore.PetStoreExample.Product
import java.util.ArrayList
import javax.swing.JOptionPane;

import javax.swing.JFrame 
        
global JFrame frame 
global javax.swing.JTextArea textArea
  • PetStore.drl ファイルの最初の部分には、さまざまな Java クラスをルールで使用できるようにするための標準のパッケージおよびインポートステートメントが含まれます。
  • 2 つの globals frametextArea は、Swing コンポーネント JFrame と、その参照が xhtml メソッドを呼び出す Java コードによって以前に渡された JTextArea コンポーネントへの参照を保持します。setGlobal()これらのグローバル変数は、Session の存続期間中値を保持します。

27.5. ペットショップの例:PetStore.drl から抽出されたルールの Java 関数

function void doCheckout(JFrame frame, WorkingMemory workingMemory) {
    Object[] options = {"Yes",
                        "No"};
                            
    int n = JOptionPane.showOptionDialog(frame,
        "Would you like to checkout?",
        "",
        JOptionPane.YES_NO_OPTION,
        JOptionPane.QUESTION_MESSAGE,
        null,
        options,
        options[0]);

    if (n == 0) {
        workingMemory.setFocus( "checkout" );
    }   
}

function boolean requireTank(JFrame frame, WorkingMemory workingMemory, Order order, Product fishTank, int total) {
    Object[] options = {"Yes",
                        "No"};
                            
    int n = JOptionPane.showOptionDialog(frame,
        "Would you like to buy a tank for your " + total + " fish?",
        "Purchase Suggestion",
        JOptionPane.YES_NO_OPTION,
        JOptionPane.QUESTION_MESSAGE,
        null,
        options,
        options[0]);
                                             
    System.out.print( "SUGGESTION: Would you like to buy a tank for your "
                      + total + " fish? - " );

    if (n == 0) {
        Purchase purchase = new Purchase( order, fishTank );
        workingMemory.insert( purchase );
        order.addItem( purchase );
        System.out.println( "Yes" );
    } else {
        System.out.println( "No" );
    }      
    return true;
}
  • これらの関数をルールファイルに設定すると、ペットショップの例がよりコンパクトになります。
  • 関数は、同じルールパッケージ内に、または標準の Java クラスの静的メソッドとして独自のファイルに関数を追加し、import function my.package.Foo.hello を使用してインポートできます。
  • doCheckout() チェックアウトするかどうかをユーザーに尋ねるダイアログボックスを表示します。その場合、フォーカスは checkOut agenda-group に設定され、そのグループのルールが(今後)実行できるようにします。
  • requireTank() 同時を購入するかどうかをユーザーに尋ねるダイアログボックスを表示します。その場合は、新しい fish tank Product が作業メモリーの順序リストに追加されます。

27.6. ペットショップの例:PetStore.drl からの作業メモリーへのアイテム

// Insert each item in the shopping cart into the Working Memory 
rule "Explode Cart"
    agenda-group "init"
    auto-focus true
    salience 10
    dialect "java"
when
    $order : Order( grossTotal == -1 )
    $item : Purchase() from $order.items
then
    insert( $item );
    kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "show items" ).setFocus();
    kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "evaluate" ).setFocus();
end
  • 最初の抽出は、auto-focus 属性が true に設定されているため、最初に実行されます。
  • このルールは、grossTotal が計算されていない全注文に対して照合されます。この順序で購入アイテムごとにループします。"Explode Cart" ルールの一部は、ルール名、顕著性(ルールが実行される順序を提案)、およびダイアレクトを java に設定する必要があります。
  • agenda-group init は、アジェンダグループの名前を定義します。この場合、グループにはルールが 1 つしかありません。ただし、Java コードもルール結果も、このグループにフォーカスされていないため、次の属性に依存することはありません。
  • auto-focus true は、アジェンダグループの唯一のルールですが、Java コードから fireAllRules() が呼び出されると、このルールが実行されるようにします。
  • kcontext....setFocus() show items および evaluate のアジェンダグループにフォーカスを設定し、ルールを実行できるようにします。実際には、注文上のすべての項目をループしてメモリーに挿入し、挿入するたびに他のルールを実行できます。

27.7. ペットショップの例:PetStore.drl からの GUI のアイテムの表示

rule "Show Items"
    agenda-group "show items"
    dialect "mvel"
when
    $order : Order( )
    $p : Purchase( order == $order )
then
   textArea.append( $p.product + "\n");
end
  • show items agenda-group には "Show Items" と呼ばれるルールが 1 つしかありません(ケースの違いに注意)。現在作業メモリー(またはセッション)の注文で購入しているたびに、GUI の下部にあるテキストエリアに詳細がログに記録されます。これに使用される textArea 変数はグローバル変数です。
  • evaluate Agenda グループは、Explode Cart ルールからフォーカスを取得します。

27.8. ペットショップの例:PetStore.drl からのアジェンダグループの評価

// Free Fish Food sample when we buy a Gold Fish if we haven't already bought 
// Fish Food and don't already have a Fish Food Sample
rule "Free Fish Food Sample"
    agenda-group "evaluate"
    dialect "mvel"
when
    $order : Order()
    not ( $p : Product( name == "Fish Food") && Purchase( product == $p ) )
    not ( $p : Product( name == "Fish Food Sample") && Purchase( product == $p ) )
    exists ( $p : Product( name == "Gold Fish") && Purchase( product == $p ) )
    $fishFoodSample : Product( name == "Fish Food Sample" );
then
    System.out.println( "Adding free Fish Food Sample to cart" );
    purchase = new Purchase($order, $fishFoodSample);
    insert( purchase );
    $order.addItem( purchase ); 
end

// Suggest a tank if we have bought more than 5 gold fish and don't already have one
rule "Suggest Tank"
    agenda-group "evaluate"
    dialect "java"
when
    $order : Order()
    not ( $p : Product( name == "Fish Tank") && Purchase( product == $p ) )
    ArrayList( $total : size > 5 ) from collect( Purchase( product.name == "Gold Fish" ) )
    $fishTank : Product( name == "Fish Tank" )
then
    requireTank(frame, drools.getWorkingMemory(), $order, $fishTank, $total); 
end
"Free Fish Food Sample" のルールは、以下の場合にのみ実行されます。
  • ストアには fish food があり ません
  • ストアには無料の fish food サンプルがあり ません
  • ストアにはその順序で Gold Fish があります。
"Suggest Tank" のルールは、以下の場合にのみ実行されます。
  • ストアの順序で Fish Tank はあり ません
  • ストアには、5 つ以上の Gold Fish 製品が順番に 含ま れます。
  • ルールが実行されると、requireTank() 関数 を呼び出します。これにより、ユーザーにダイアログが表示され、確認された場合は Tank が注文と作業メモリーに追加されます。
  • requireTank()関数を呼び出すと、このルールはグローバル フレーム 変数を渡して、関数が Swing GUI にハンドルを持つようにします。
  • ルールが実行されると、新しい製品(Fish Food Sample)を作成し、作業メモリー の順序でこれを追加します。

27.9. ペットショップの例:PetStore.drl からのチェックアウトの展開の実行

rule "do checkout"
    dialect "java"
    when
    then
        doCheckout(frame, drools.getWorkingMemory());
end
  • ルール "do checkout" にはアジェンダグループが設定されておらず、auto-focus 属性はありません。そのため、デフォルト(MAIN)アジェンダグループの一部とみなされます。このグループは、明示的にフォーカスを設定した agenda-groups のすべてのルールで、そのユニットを実行した場合に、デフォルトで重点を置いています。
  • ルールには LHS がないため、RHS は常に doCheckout() 関数を呼び出します。
  • doCheckout() 関数を呼び出す場合、ルールはグローバル frame 変数を渡して、関数に Swing GUI にハンドルを付与します。
  • doCheckout() 関数には、ユーザーに確認ダイアログが表示されます。確認された場合、関数はフォーカスを checkout agenda-group に設定し、次の多くのルールを実行できます。

27.10. ペットショップの例:PetStore.drl からのチェックアウトルール

rule "Gross Total"
    agenda-group "checkout"
    dialect "mvel"
when
   $order : Order( grossTotal == -1)
   Number( total : doubleValue )
       from accumulate( Purchase( $price : product.price ), sum( $price ) )
then
    modify( $order ) { grossTotal = total };
    textArea.append( "\ngross total=" + total + "\n" );
end

rule "Apply 5% Discount"
    agenda-group "checkout"
dialect "mvel"
when
   $order : Order( grossTotal >= 10 && < 20 )
then
   $order.discountedTotal = $order.grossTotal * 0.95;
   textArea.append( "discountedTotal total=" + $order.discountedTotal + "\n" );
end


rule "Apply 10% Discount"
    agenda-group "checkout"
    dialect "mvel"
when
   $order : Order( grossTotal >= 20 )
then
   $order.discountedTotal = $order.grossTotal * 0.90;
   textArea.append( "discountedTotal total=" + $order.discountedTotal + "\n" );
end
checkout agenda-group には 3 つのルールがあります。
  • Gross Total 製品の価格を合計に累積し、作業メモリーに配置し、textArea グローバル変数を使用して Swing JTextArea 経由で表示します。
  • 総計が 10 から 20 までの場合、Apply 5% Discount は割引合計を計算し、作業メモリーに追加して、テキストエリアに表示します。
  • 総計が 20 未満の場合は、Apply 10% Discount は割引合計を計算し、作業メモリーに追加して、テキストエリアに表示します。

27.11. ペットショップの例:PetStore.java の実行

PetStore.java を使用するには、以下の条件を満たしている必要があります。
  1. main() メソッドが実行され、ルールベースのロードが完了しましたが、ルールを実行していません。今のところ、これが、実行されたルールに関連する唯一のコードになります。
  2. 新しい PetStoreUI オブジェクトが作成され、後で使用できるように、ルールベースへのハンドルが付与されます。
  3. Swing コンポーネントはデプロイされ、コンソールはユーザー入力を待機します。
  • PetStore.java ファイルには main() メソッドが含まれているため、コマンドラインまたは IDE を介して標準の Java アプリケーションとして実行することができます。これは、クラスパスが正しく設定されていることを前提としています。
  • 表示される最初の画面はペットショップデモです。利用可能な製品のリスト、選択した製品の空のリスト、チェックアウトおよびリセットボタン、および空のシステムメッセージエリアがあります。
Checkout ボタンを押すと、ビジネスルールが実行されます。
  1. メソッド CheckOutCallBack.checkout() は、"Checkout" ボタンをクリックします。これにより、TableModel オブジェクトからのデータが挿入され、セッションのワーキングメモリーに挿入されます。次にルールを実行します。
  2. 最初に実行するルールは、auto-focus が true に設定されたルールになります。カート内のすべての製品をループして、製品がワーキングメモリーにあることを確認し、Show Items および Evaluation アジェンダグループに実行する機会を与えます。これらのグループのルールは、カートの内容をテキストエリア(ウィンドウ下部)に追加し、ユーザーに空き fish food を提供するかどうかを決定し、fish tank を購入するかどうかを尋ねます。

27.12. ペットショップの例:Do Checkout ルール

  1. Do Checkout ルールは、デフォルト(MAIN)アジェンダグループの一部です。これは常に doCheckout ()関数を呼び出し、'Would you like to Checkout?' ダイアログボックスを表示します。
  2. doCheckout() 関数は、フォーカスを checkout agenda-group に設定し、そのグループのルールに実行するオプションを提供します。
  3. checkout アジェンダグループのルールはカートの内容を表示し、適切な割引を適用します。
  4. Swing は、他の商品をチェックアウトする(およびルールを再度実行する)か、GUI を閉じるまで待機 します。

第28章 数独の例

28.1. 数独例:例の読み込み

手順28.1 タスク

  1. IDE で sudoku.drl を開きます。
  2. java org.drools.examples.DroolsExamplesApp を実行し、SudokuExample をクリックします。ウィンドウには空のグリッドが含まれていますが、プログラムには内部に保存されたグリッドが多数含まれており、ロードおよび解決が可能です。
  3. FileSamplesSimple をクリックして、例の 1 つを読み込みます。グリッドが読み込まれるまで、すべてのボタンが無効になります。Simple の例を読み込むと、パズルの初期状態に応じてグリッドが埋められます。
  4. Solve ボタンをクリックすると、JBoss Rules エンジンが残りの値を入力します。ボタンは再度非アクティブになります。
  5. または、Step ボタンをクリックして、ルールセットが見つかった次の数字を表示します。Console ウィンドウには、以下の例のような読み取り可能な形式で手順を解決するために実行されているルールに関する詳細情報が表示されます。
    single 8 at [0,1]
    column elimination due to [1,2]: remove 9 from [4,2]
    hidden single 9 at [1,2]
    row elimination due to [2,8]: remove 7 from [2,4]
    remove 6 from [3,8] due to naked pair at [3,2] and [3,7]
    hidden pair in row at [4,6] and [4,4]
    
  6. Dump ボタンをクリックして、グリッドの状態を表示します。セルには、確立された値または残りの候補が表示されます。以下の例を参照してください。
    Col: 0     Col: 1     Col: 2     Col: 3     Col: 4     Col: 5     Col: 6     Col: 7     Col: 8     
    Row 0:   2 4  7 9   2 456        4567 9   23 56  9  --- 5 ---  --- 1 ---    3  67 9  --- 8 ---     4 67   
    Row 1:  12    7 9  --- 8 ---  1    67 9   23  6  9  --- 4 ---   23  67    1 3  67 9    3  67 9  --- 5 --- 
    Row 2:  1  4  7 9  1  456     --- 3 ---      56 89      5 78       5678   --- 2 ---     4 67 9  1  4 67   
    Row 3:  1234       12345      1  45      12  5  8   --- 6 ---   2  5 78       5 78      45 7    --- 9 --- 
    Row 4:  --- 6 ---  --- 7 ---      5      --- 4 ---   2  5  8   --- 9 ---      5  8   --- 1 ---  --- 3 --- 
    Row 5:  --- 8 ---  12 45      1  45   9  12  5      --- 3 ---   2  5 7        567       4567     2 4 67   
    Row 6:  1 3   7    1 3  6     --- 2 ---    3 56 8       5  8     3 56 8   --- 4 ---    3 567 9  1    678  
    Row 7:  --- 5 ---  1 34 6     1  4 678     3  6 8   --- 9 ---    34 6 8   1 3  678   --- 2 ---  1    678  
    Row 8:    34       --- 9 ---     4 6 8   --- 7 ---  --- 1 ---   23456 8     3 56 8     3 56          6 8
    

28.2. 数独例:破損した例のデバッグ

手順28.2 タスク

  1. IDE で sudoku.drl を開きます。
  2. FileSamples!DELIBERATLEY BROKEN! をクリックします。JBoss Rules エンジンはグリッドを検査し、以下の出力を生成します。
    cell [0,8]: 5 has a duplicate in row 0
    cell [0,0]: 5 has a duplicate in row 0
    cell [6,0]: 8 has a duplicate in col 0
    cell [4,0]: 8 has a duplicate in col 0
    Validation complete.
    
  3. Solve ボタンをクリックして、この無効なグリッドに解決ルールを適用します。これらのルールは、問題の解決にセルの値を使用します。これらの状況を検出するルールは、指定したセルのソリューション値を含む Setting ファクトを挿入します。このファクトは、グループのすべてのセルから誤った値を削除します。

28.3. 数独例:Java ソースおよびルール

  • Java ソースコードは /src/main/java/org/drools/examples/sudoku ディレクトリーにあります。この 2 つの DRL ファイルは、/src/main/rules/org/drools/examples/sudoku ディレクトリーにあるルールを定義します。
  • パッケージ org.drools.examples.sudoku.swing には、数独パズルのフレームワークを実装するクラスセットが含まれています。このパッケージには、JBoss Rules ライブラリーの依存関係はありません。
  • SudokuGridModel Cell オブジェクトの 9x9 グリッドとして数独パズルを格納するために実装できるインターフェイスを定義します。
  • SudokuGridView Swing コンポーネントで、SudokuGridModel の実装を視覚化できます。
  • SudokuGridEventSudokuGridListener は、モデルとビューの間の状態の変化の通信に使用されます。セルの値が解決または変更されると、イベントが実行されます。
  • SudokuGridSamples デモンストレーションの目的で、部分的に埋め込まれた数独パズルを複数提供します。
  • パッケージ org.drools.examples.sudoku.rules には、DRL ファイルをコンパイルするメソッドを持つユーティリティークラスが含まれています。
  • パッケージ org.drools.examples.sudoku には、重要な Cell オブジェクトとそのさまざまな集約を実装するクラスのセットが含まれています。これには、CellFile サブタイプ( CellRowCellCol、および CellSqr が含まれており、これらはすべてサブタイプになります。CellGroup

28.4. 数独例:Cell Objects

  • Cell および CellGroup は、SetOfNine のサブクラスで、プロパティー free を提供するタイプになります。Set<Integer>Cell の場合は、個別の候補セットを表します。CellGroup の場合、セットはセルの全候補セットの統合、または割り当ての必要がある数字のセットです。
  • セルに値を割り当てたり、候補セットから値を削除したりする特定の状況を検出するルールを作成できます。たとえば、81 Cell および 27 CellGroup オブジェクトを含む Cell オブジェクトの一覧を作成できます。Cell プロパティー cellRowcellCol、および PROMPT プロパティーで提供されるリンクを組み合わせることもできます。cellSqr CellGroup cells

28.5. 数独例:クラスおよびオブジェクト

  • Setting クラスに属するオブジェクトは、値の割り当てに伴う操作をトリガーするために使用されます。Setting ファクトの存在は、プロセスの変更を検出する必要があるすべてのルールで使用されます。これは、一貫性のない中間状態に対する反応を回避するためです。
  • クラス Stepping のオブジェクトは、優先度の低いルールで使用され、"Step" が予期せず終了すると緊急停止を実行します。これは、パズルをプログラムで解決できないことを示します。
  • クラス org.drools.examples.sudoku.SudokuExample は、上記のコンポーネントを組み合わせた Java アプリケーションを実装します。

28.6. 数独例:Validate.drl

  • 数独バリデータールール(validate.drl)は、セルグループの重複番号を検出します。アジェンダグループに統合され、パズルの読み込み後にアクティベートできます。
  • セルで 3 つのルールが重複 しているのは非常に似ています。最初のパターンでは、値が割り当てられたセルを見つけます。2 つ目のパターンは、セルが属する 3 つのセルグループのいずれかをプルします。最後のパターンでは、それぞれ同じ行、列、または角の中の最初のセルと同じ値を持つセルを検索します。
  • ルール 終了グループ は最後に実行されます。メッセージを出力し、halt を呼び出します。

28.7. 数独例:Sudoku.drl

  • 数独.drl には、3 種類の解決ルールがあります。1 つのグループは、セルへの数字の割り当てを処理し、2 つ目のグループは実行可能な割り当てを検出し、3 番目のグループは候補セットからの値を削除します。
  • ルール set a valueeliminate a value from Cell、および retract setting は、このオブジェクトの存在によって異なります。Setting
  • Set a value セルへの割り当てと、セルの 3 つのグループの free セットから値を削除する操作を処理します。また、ゼロの場合は、fireUntilHalt() を呼び出した Java アプリケーションに制御を戻します。
  • Eliminate a value from Cell 新しく割り当てられたセルに関連する全セルの候補リストを減らします。
  • Retract setting すべての除外が完了したら、トリガー Setting ファクトを取り消します。
  • セルに数字を割り当てることができる状況を検出するルールは 2 つだけです。ルール single は、単一の数字を含む候補セットで Cell に対して実行されます。ルール hidden single は、候補が含まれるセルがある場合に実行されますが、この候補は、セルが属するグループの 1 つにある他のすべてのセルに存在しない場合に実行されます。どちらのルールも Setting ファクトを作成して挿入します。
  • ルールの最大グループからのルールは、数独パズルを手動で解決する際に使用される 2 つまたは 3 つのグループのグループで実装されます。
  • ルール naked pair は、グループの 2 つのセルで同じ候補セットを 2 つ検出します。これらの 2 つの値は、対象グループにあるその他のすべての候補セットから削除できます。
  • hidden pair in ルールでは、ルールはグループの 2 つのセルで 2 つの数字のサブセットを検索しますが、このグループの他のセルには値が発生しません。つまり、他の候補はすべて、隠れたペアを持つ 2 つのセルから取り除かれることができます。
  • ルールのペアは、行と列で X-wings を処理します。2 つの異なる行(または列)のそれぞれに値にセルが 2 つしかなく、これらの候補が同じ列(または行)にある場合、列(または行)のこの値の他の候補はすべて除外できます。条件 same または only の結果、適切な制約があるパターン、または not の接頭辞が付けられます。
  • ルールペア intersection removal... は、1 つの行または 1 つの列のいずれかで、角の中の数字の制限に基づいています。これは、この数字が行または列の 2 つまたは 3 つのセルのいずれかにある必要があることを意味します。これは、グループの他のすべてのセルの候補セットから削除できます。このパターンは、発生制限を確立し、同じセルファイル内で、角の外にあるセルごとに実行されます。
  • 非常に困難のグリッドを解決するには、より複雑なルールでルールセットを拡張する必要があります。(最終的には、トライアルとエラーを除いて解決できないパズルがあります。)

第29章 番号に関するガイドの例

29.1. Number Guess の例:サンプルの読み込み

final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.drl",
                                                    ShoppingExample.class ),
              ResourceType.DRL );
kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.rf",
                                                    ShoppingExample.class ),
              ResourceType.DRF );

final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
  • NumberGuess.drl にある Number Guess の例は、ルールが実行される順序を制御する方法である Rule Flow の使用を示しています。上記のようにロードされます。

29.2. Number Guess の例:RuleFlow の起動

final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

KnowledgeRuntimeLogger logger =
  KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/numberguess");

ksession.insert( new GameRules( 100, 5 ) );
ksession.insert( new RandomNumber() );
ksession.insert( new Game() );

ksession.startProcess( "Number Guess" );
ksession.fireAllRules();

logger.close();

ksession.dispose();
  • 上記のコードは、パッケージの作成とルールの読み込みを示しています( add() メソッドを使用)。
  • 同じナレッジベースに異なるルールフローを指定するオプションを提供する Rule Flow (NumberGuess.rf)を追加する他の行があります。
  • ナレッジベースの記事が作成されると、ステートフルセッションの取得に使用できます。その後、ファクトが挿入されます。

29.3. Number Guess の例:クラスおよびメソッド

注記
Number Guess Example クラスはすべて NumberGuessExample.java ファイルに含まれます。
  • クラス GameRules は、最大範囲と許容される数字の数を提供します。
  • クラス RandomNumber は、0 から 100 までの数字を自動的に生成し、ルールで利用できるようにします。これは、getValue() メソッド経由で挿入することで行います。
  • クラス Game は、行われた数字の数を追跡します。
  • プロセスを開始するために、startProcess() メソッドが呼び出されます。
  • ルールを実行するには、fireAllRules() メソッドが呼び出されます。
  • 作業メモリーセッションを削除するには、dispose() メソッドが呼び出されます。

29.4. Number Guess Example: Observing the RuleFlow

手順29.1 タスク

  1. Drools IDE で NumberGuess.rf ファイルを開きます。標準のフローチャートと同様に機能する図が表示されます。
  2. ダイアグラムを編集するには、IDE のダイアグラムの左側にある利用可能なコンポーネントのメニューを使用します。これは パレット と呼ばれます。
  3. XML でダイアグラムを保存します。(インストールされている場合は、XStream を使用してこれを行うことができます。)
  4. まだ開いていない場合は、Properties View が IDE に表示されることを確認します。これを開くには、WindowShow ViewOther をクリックし、Properties ビューを選択できます。ルールフローのアイテムを選択する にこれを行うと、プロパティーが表示されます。これらのプロパティーを使用してプロセスを特定し、変更を確認できます。

29.5. Number Guess 例:RuleFlow ノード

Number Guess RuleFlow には、複数のノードタイプがあります。
  • Start node (緑色の円の左矢印)および End node (red box)のマークは、ルールフローの開始と終了です。
  • Rule Flow Group ボックス(アイコンのない黄色)は、ルール(DRL)ファイルで定義された Rule Flow Groups を表します。たとえば、フローが Rule Flow Group "Too High" に到達すると、ruleflow-group "Too High" の属性でマークが付けられたルールのみが実行される可能性があります。
  • アクションノード(黄色の cog-shaped アイコン)は、標準の Java メソッド呼び出しを実行します。この例のほとんどのアクションノードは、System.out.println() を呼び出して、プログラムの進行状況をユーザーに示します。
  • Split and Join Nodes (青い ovals, no icon)(Guess Correct? や More guesses Join のマークで、コントロールのフローが分割および再参加できる場所)。
  • 矢印は、さまざまなノード間のフローを示します。

29.6. Number Guess の例:numberGuess.drl の特定のポイントでのルールのフェデレーション

rule "Get user Guess"
    ruleflow-group "Guess"
    no-loop
    when
        $r : RandomNumber()
        rules : GameRules( allowed : allowedGuesses )
        game : Game( guessCount < allowed )
        not ( Guess() )
    then
        System.out.println( "You have " + ( rules.allowedGuesses - game.guessCount )
                            + " out of " + rules.allowedGuesses
                            + " guesses left.\nPlease enter your guess from 0 to "
                            + rules.maxRange );
        br = new BufferedReader( new InputStreamReader( System.in ) );
        i = br.readLine();        
        modify ( game ) { guessCount = game.guessCount + 1 }
        insert( new Guess( i ) );
end
  • ルールと組み合わせてさまざまなノードにより、Number Guess ゲームが機能します。たとえば、"Guess" Rule Flow Group では、"Get user Guess" ルールのみが ruleflow-group "Guess" の一致する属性を持つため、ルール get user Guess のみを実行できます。
  • ルールの LHS セクション(の 場合は)は、作業メモリーに挿入される各 RandomNumber オブジェクトに対してアクティベートされることを示します。ここで、guessCountGameRules オブジェクトからの allowedGuesses 未満であり、ユーザーは正しい番号を推測していません。
  • RHS セクション(または の に)は、ユーザーにメッセージを出力し、System.in からユーザー入力を待ちます。この入力を取得した後(戻り値が押すまで readLine() メソッド呼び出しがブロックされる)は、推測数を変更し、新しい数字を挿入し、両方を作業メモリーで利用できるようにします。
  • パッケージは dialect を MVEL として宣言し、さまざまな Java クラスがインポートされます。
合計すると、このファイルには 5 つのルールがあります。
  1. 上記で確認したルールであるユーザーガイドを取得します。
  2. 最も高い数字を記録するルール。
  3. 最も低い数字を記録するルール。
  4. 正しくない場合に推測を検査し、メモリーから取り消すルール。
  5. すべての数字が使用されていることをユーザーに通知するルール。

29.7. Number Guess の例:RuleFlow 制約の表示

手順29.2 タスク

  1. IDE で Properties ビューに移動し、Constraints プロパティー行をクリックして Constraints Editor を開きます。
  2. To node Too High の横にある Edit ボタンをクリックして、さまざまなオプションと共に表示されるダイアログを開きます。Textual Editor ウィンドウの値は、LHS の標準ルール形式に従い、Working Memory でオブジェクトを参照できます。結果(RHS)は、LHS 式が true と評価されると、制御のフローがこの ノード(つまり Too High)に従うことです。

29.8. Number Guess 例:コンソール出力

You have 5 out of 5 guesses left.
Please enter your guess from 0 to 100
50
Your guess was too high
You have 4 out of 5 guesses left.
Please enter your guess from 0 to 100
25
Your guess was too low
You have 3 out of 5 guesses left.
Please enter your guess from 0 to 100
37
Your guess was too low
You have 2 out of 5 guesses left.
Please enter your guess from 0 to 100
44
Your guess was too low
You have 1 out of 5 guesses left.
Please enter your guess from 0 to 100
47
Your guess was too low
You have no more guesses
The correct guess was 48 

  • NumberGuess.java ファイルには main() メソッドが含まれるため、コマンドラインまたは IDE を介して標準の Java アプリケーションとして実行できます。一般的なゲームにより、上記の対話が発生する可能性があります。太字の数字はユーザーによって入力されました。
  • NumberGuessExample.javamain() メソッドは、Rule Base を読み込み、Stateful Session を作成し、GameGameRules、および RandomNumber (ターゲット番号を含む)オブジェクトを挿入します。この メソッドは、使用するプロセスフローも設定し、すべてのルールを実行します。コントロールは RuleFlow に渡されます。
  • RuleFlow ファイル NumberGuess.rf は起動ノードで開始します。
  • Guess ノードで、適切な Rule Flow Group (Get user Guess)が有効になります。この場合、ルール Guess.drl (number Guess.drl ファイル内)がトリガーされます。このルールはユーザーにメッセージを表示し、応答を取得して、Working Memory に配置します。フローは次の Rule Flow ノードに渡されます。
  • 次のノードである Guess Correct 制約は、現在のセッションを検査して、取得するパスを決定します。
    ステップ 4 の数字が高すぎる、または低すぎると、フローは、通常の Java コードを持つアクションノードがあるパスと、適切なメッセージと Rule Flow Group を出力するパスに進みます。これにより、最大推測または最小推測ルールがトリガーされます。フローはこれらのノードからステップ 6 に渡されます。
    ステップ 4 の推測が右側である場合は、RuleFlow の最後へのパスに進みます。この前は、通常の Java コードを持つアクションノードが "you guessed correctly" ステートメントを出力します。ここに結合ノードがあります(ルールフローの終了の直前に)ます。したがって、no-more-gueses パス(ステップ 7)も RuleFlow を終了することができます。
  • コントロールは、結合ノードを介して RuleFlow に従って、"guess incorrect" RuleFlow Group (作業メモリーから推測を取り消しるルールをトリガーする)、および "More guesses" デシジョンノードに渡します。
  • "More guesses" デシジョンノード(ルールフローの右側)は制約を使用し、ルールがワーキングメモリーに置かれた値を再度確認し、ユーザーがより多くの推測を持つかどうかを判断します。その場合は、ステップ 3 に移動します。そうでない場合には、ユーザーは RuleFlow グループを介して RuleFlow の最後に進みます。このグループで、"you have more guesses" というルールをトリガーします。
  • ステップ 3 から 7 のループは、数字が正しく推測されるか、ユーザーが推測するまで継続されます。

付録A 更新履歴

改訂履歴
改訂 5.3.1-73.4002013-10-31Rüdiger Landmann
Rebuild with publican 4.0.0
改訂 5.3.1-73Tue Feb 05 2013 David Le Sage
コンテンツ仕様:11912、Revision: 371716 (Dlesage によるビルド)