第2章 クイックスタート
2.1. クイックスタートの基本
2.1.1. ステートレスナレッジセッション
- ステートレスセッション
- 推論のないセッション。
- 検証。例:「この申請者は住宅ローンの資格があるか」
- 計算。例: 「住宅ローンの保険料を計算する」
- ルーティングとフィルタリング。例:「受信メッセージをフォルダーへフィルターする」または「受信メッセージを宛先へ送信する」
- 必要なデータを収集します。これはルールに渡される ファクト のセットを形成します。この例では、1 つのデータのみが存在します。
package com.company.license; public class Applicant { private String name; private int age; private boolean valid; public Applicant (String name, int age, boolean valid) { this.name = name; this.age = age; this.valid = valid; } //add getters & setters here } - この時点でデータモデルが存在するため、最初のルールを記述します。このルールの目的は、18 歳未満の申請者を拒否することです。
package com.company.license; rule "Is of valid age" when $a : Applicant( age < 18 ) then $a.setValid( false ); endApplicantオブジェクトがルールエンジンに挿入されると、各ルールの制約が評価し、一致するものを探します (常に「オブジェクトタイプ」の暗黙的な制約があり、それ以降は明示的なフィールド制約がいくつでも存在できます)。- パターン
- 制約のコレクションはパターンと呼ばれます。
- パターンマッチング
- 各ルールの制約グループがオブジェクトを評価し、一致するものを探すプロセスです。
- 一致
- 挿入されたオブジェクトがルールのすべての制約を満たす場合、一致したと見なされます。
たとえば、Is of valid ageルールでは次の 2 つの制約があります。- 一致するファクトのタイプは
Applicantでなければならない。 Ageの値は 18 未満でなければならない。
$aはバインディング変数で、ルールの結果 (ここからオブジェクトのプロパティーを更新可能) の一致したオブジェクトの参照を可能にします。注記
ドル記号 ($) の使用は任意です。使用すると変数名とフィールド名を区別できます。注記
ここでは、ルールがクラスと同じフォルダーにあり、最初の ナレッジベース を構築するために クラスパスリソースローダー を使用できると仮定します。- ナレッジベース
- ナレッジベースは
KnowledgeBuilderによってコンパイルされたルールのコレクションです。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add( ResourceFactory.newClassPathResource( "licenseApplication.drl", getClass() ), ResourceType.DRL ); if ( kbuilder.hasErrors() ) { System.err.println( kbuilder.getErrors().toString() ); }上記で引用符で囲まれたコードはnewClassPathResource()メソッドを使用してlicenseApplication.drlファイルのクラスパスを検索します。ResourceTypeは Drools ルール言語 で記述されます。- Drools ルール言語
- Drools ルール言語 (DRL) は JBoss Rules のネイティブルール言語です。
- エラーは
KnowledgeBuilderをチェックします。エラーがない場合、セッションを構築できます。 - ルールに対してデータを実行します (申請者は 18 歳未満であるため、申請は「無効」とマーク付けされます。
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() ); StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession(); Applicant applicant = new Applicant( "Mr John Smith", 16, true ); assertTrue( applicant.isValid() ); ksession.execute( applicant ); assertFalse( applicant.isValid() );
collection など、オブジェクトを実装する iterable に対して実行することが可能です。次の例では、運転免許申請日が含まれる Application という名前の別のクラスを追加する方法を説明します。この他に、valid というブール値フィールドを Application に移動する方法についても説明します。
- コードは次の通りです。
public class Applicant { private String name; private int age; public Applicant (String name, int age) { this.name = name; this.age = age; } // getter and setter methods here } public class Application { private Date dateApplied; private boolean valid; public Application (boolean valid) { this.valid = valid; } // getter and setter methods here } - 申請が正規の期間内に行われたことを確認するために、以下のルールを追加します。
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 - Java アレイは
iterableインターフェースを実装できないため、JDK コンバーターメソッド を代わりに使用します (このメソッドはArrays.asList(...)という行で始まります)。以下のコードはiterableリストに対して実行されます。各コレクション要素は、一致したルールが実行される前に挿入されます。StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession(); kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() ); Applicant applicant = new Applicant( "Mr John Smith", 16 ); Application application = new Application(true); assertTrue( application.isValid() ); ksession.execute( Arrays.asList( new Object[] {application, applicant} )); assertFalse( application.isValid() );注記
execute(Object object)およびexecute(Iterable objects)メソッドは、実際はBatchExecutorインターフェースからのexecute(Command command)というメソッドに対するラッパーです。 CommandFactoryを使用して命令を作成し、以下の内容がexecute( Iterable it )と同等になるようにします。ksession.execute( CommandFactory.newInsertElements(Arrays.asList(new Object[] {application,applicant})) );- 多くのコマンドや結果出力識別子を使用する場合は、特に
BatchExecutorとCommandFactoryが便利です。List<Command> cmds = new ArrayList<Command>(); cmds.add( CommandFactory.newInsertObject(new Person("Mr John Smith"), "mrSmith")); cmds.add( CommandFactory.newInsertObject(new Person( "Mr John Doe" ), "mrDoe" )); ExecutionResults results = ksession.execute( CommandFactory.newBatchExecution(cmds) ); assertEquals( new Person("Mr John Smith"), results.getValue("mrSmith") );
注記
CommandFactory は BatchExecutor で使用できる他のコマンドを多くサポートします。 StartProcess、Query、SetGlobal などがその一例です。
2.1.2. ステートフルナレッジセッション
- ステートフルセッション
- ステートフルセッションでは、経時的にファクトに繰り返し変更を行うことが可能です。
- 監視: 例として、株式市場を監視し、購入処理を自動化できます。
- 診断: 例として、これを使用して障害検出処理を実行できます。また、医療診断処理にも使用できます。
- 物流: 例として、配達物追跡および配送提供に関連する問題に適用できます。
- 法令遵守: 例として、市場売買の合法性を検証するために使用できます。
警告
dispose() メソッドは、必ずステートフルセッションの実行後に呼び出されるようにしてください。これは、ナレッジベースは作成時にステートフルナレッジセッションへの参照を取得するためです。
StatelessKnowledgeSession の場合と同様に、StatefulKnowledgeSession は BatchExecutor インターフェースをサポートします。唯一の違いは、FireAllRules コマンドが最後に自動的に呼び出されないことです。
- 各部屋にスプリンクラーが設定されている家のモデルを作成します。いずれかの部屋で火事が発生します。
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 { } - ルールは複数のオブジェクト間の関係を表す必要があります (特定の部屋にスプリンクラーが存在することなどを定義するため)。これには、パターンでバインディング変数を制約として使用します。これは クロス積 になります。
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」ステートメントのように動作します)。これには、一連のコンマ区切りの Java 式が含まれています。これらはあらゆる意味で、modifyステートメントの 制御式 によって選択されたオブジェクト"setters"への呼び出しです。これらのsettersはデータを変更し、変更内容の「理由付け」を再度行えるよう、engineがその変更を認識するようにします。このプロセスが 推論 と呼ばれ、ステートフルセッションがどのように操作するかを理解するために重要になります (ステートレスセッションは推論を使用しないため、engineがデータの変更を認識する必要はありません)。注記
推論を無効にするには、シーケンシャルモード を使用します。
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());
endAlarm オブジェクトが作成されますが、火元がいくつあっても建物全体で 1 つの Alarm のみが必要になります。ここで、not の補語である exists を導入できます。exists はカテゴリーの 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" );
endnot を使用します。
rule "Cancel the alarm when all the fires have gone"
when
not Fire()
$alarm : Alarm()
then
retract( $alarm );
System.out.println( "Cancel the alarm" );
endrule "Status output when things are ok"
when
not Alarm()
not Sprinkler( on == true )
then
System.out.println( "Everything is ok" );
endfireAlarm.drl という名前のファイルに格納します。このファイルはクラスパスのサブディレクトリに保存します。次に、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();
ksession.fireAllRules()を呼び出します。これにより、一致するルールに実行権限が与えられますが、実行されないため健全性に関するメッセージのみが生成されます。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 つのファイルを作成し、挿入します ( ファクトハンドル は保持されます)。
- ファクトハンドル
- ファクトハンドルは挿入されたインスタンスのへの内部参照です。これにより、後でインスタンスを取り消したり変更したりすることができます。
firesがエンジンにあるため、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
- 鎮火したら、火災オブジェクトが取り消され、スプリンクラーがオフになります。この時点で火災報知器はキャンセルされ、状態メッセージが再度表示されます。
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 ok
> Turn off the sprinkler for room office > Turn off the sprinkler for room kitchen > Cancel the alarm > Everything is ok
2.2. 理論の概要
2.2.1. メソッドとルール
- 直接呼び出される
- 特定のインスタンスが渡される
- 1 つの呼び出しによって 1 つの実行が行われる
public void helloWorld(Person person)
{
if ( person.getName().equals( "Chuck" ) )
{
System.out.println( "Hello Chuck" );
}
}rule "Hello World"
when
Person( name == "Chuck" )
then
System.out.println( "Hello Chuck" );
endengineに挿入されたデータに一致することにより実行される- 直接呼び出すことができない
- 特定のインスタンスをルールに渡すことはできない
- 一致によって、ルールは一度または複数回実行され、全く実行されないこともある
2.2.2. クロス積
- クロス積
- 2 つ以上のデータセットが組み合わされた時、その結果はクロス積と呼ばれます。
rule "show sprinklers in rooms"
when
$room : Room()
$sprinkler : Sprinkler()
then
System.out.println( "room:" + $room.getName() +
" sprinkler:" + $sprinkler.getRoom().getName() );
endselect * from Room, Sprinkler への SQL (Structured Query Language) コマンドと同じで、Room テーブル内の各行が Sprinkler テーブル内の各行と結合するよう指示します。結果して、以下が出力されます。
room:office sprinker: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 "show sprinklers in rooms"
when
$room : Room()
$sprinkler : Sprinkler( room == $room )
then
System.out.println( "room:" + $room.getName() +
" sprinkler:" + $sprinkler.getRoom().getName() );
endSprinkler が各 Room に割り当てられます。SQL で書かれた通り、対応するクエリは select * from Room, Sprinkler where Room == Sprinkler.room になります。
room:office sprinkler:office room:kitchen sprinkler:kitchen room:livingroom sprinkler:livingroom room:bedroom sprinkler:bedroom
2.2.3. アクティベーション、アジェンダおよび競合セット
rule engine には結果の実行を管理する方法が必要となります。JBoss Rules は アクティベーション、アジェンダ および 競合解決ストラテジー を使用してこれを実現します。
注記
knowledge bases の作成に必要な Java コードの知識があることを前提とし、コードが繰り返されないように StatefulKnowledgeSession にファクトを投入します。
rule engine の状態を示します。
Cashflow、 Account、および AccountPeriod の 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
}knowledge bases の作成方法と、ファクトをインスタンス化して StatefulKnowledgeSession に投入する方法は理解されているはずです。したがって、説明を明確にするため、表を使用して挿入されたデータの状態を示します。下表は単一のファクトが Account に対して挿入されたことを表しています。2 四半期にわたる入出金も Cashflow オブジェクトとして Account に挿入されています。

図2.1 Cash Flow および Account
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.setBalance(acc.getBalance() + $amount);
endrule "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.setBalance(acc.getBalance() - $amount);
endCashflow オブジェクトに制約されます。

図2.2 Cash Flow および Account
- データは挿入段階で照合されますが、これはステートフルセッションであるため、ルールの結果は即座に実行されません。一致したルールと対応するデータは アクティベーション と呼ばれます。
- 各アクティベーションは アジェンダ と呼ばれるリストへ追加されます。
- アジェンダ上の各アクティベーションは、
fireAllRules()メソッドが呼び出された時に実行されます。指定のない限り、アクティベーションは任意の順番で実行されます。

図2.3 Cash Flow および Account

図2.4 Cash Flow および Account
アジェンダ のアクティベーションも 1 つになります。

図2.5 Cash Flow および Account

図2.6 Cash Flow および Account
agenda に 1 つまたは複数のアクティベーションがある場合、アクティベーションは「競合」状態であると言えます。この場合、実行の順序を決定するため、競合解決ストラテジー が使用されます。最も単純なレベルでは、デフォルトのストラテジーは salience を使用してルールの優先度を決定します。各ルールのデフォルトの salience 値はゼロで、この値が大きいほど優先度が高くなります。salience に負の値を使用することも可能です。これにより、相対的にルールの実行順序を適用することができます。
注記
rule "Print balance for AccountPeriod"
salience -50
when
ap : AccountPeriod()
acc : Account()
then
System.out.println( acc.getAccountNo() + " : " + acc.getBalance() );
endagenda を表しています。3 つの入出金ルールが任意の順番で、出力ルールは最後であるため、入出金ルールの後に実行されます。
重要

図2.7 Cash-Flow および Account
2.2.4. 推論
age フィールドと年齢ポリシー制御を提供するルールを持つ Person ファクトの場合、Person が成人または子供であるかを推論し、その推論に基づいたアクションを実行できます。
例2.1 成人の推論
rule "Infer Adult" when $p : Person( age >= 18 ) then insert( new IsAdult( $p ) ) end
Person には、IsAdult のインスタンスが挿入されます。このようなファクトは関係と呼ばれます。この推論された関係はすべてのルールで使用できます。
$p : Person() IsAdult( person == $p )
2.2.4.1. 使用する推論

図2.8 モノリシック決定テーブル

図2.9 ルールテーブルの年齢ポリシー
IsAdult ファクトはポリシールールから推論されます。中央政府が IsAdult ファクトを保持するため、ID 部門は成人であるかどうかのみを知る必要があり、ルールが現在のポリシーに適合するよう維持する必要はありません。

図2.10 ルールテーブルの ID カード
2.2.5. 推論および TruthMaintenance
logicalInsert) は、関心の分離を提供するために使用されます。以下の例は、子供または成人用のバス乗車パスを発行します。
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
logicalInsert を使用して実現できます。
rule "Infer Child" when
$p : Person( age < 16 )
then
logicalInsert( new IsChild( $p ) )
end
rule "Infer Adult" when
$p : Person( age >= 16 )
then
logicalInsert( new IsAdult( $p ) )
endwhen 節の真偽に基づいて論理的に挿入されます。when 節の真偽が false に変更された場合、ファクトは自動的に取り消しされます。これは、相互排他的なルールには適切です。たとえば、年齢が 15 から 16 に変わった場合、IsChild ファクトは自動的に取り消され、IsAdult ファクトが挿入されます。
rule "Issue Child Bus Pass" when
$p : Person( )
IsChild( person =$p )
then
logicalInsert(new ChildBusPass( $p ) );
end
rule "Issue Adult Bus Pass" when
$p : Person( age >= 16 )
IsAdult( person =$p )
then
logicalInsert(new AdultBusPass( $p ) );
endLogicalInsert を not 条件要素と組み合わせて通知を処理できます。この場合、バス乗車パスの返却に対する要求を送信することができます。ChildBusPass オブジェクトが取り消されると、ルールがトリガーされ、要求が対象者に送信されます。
rule "Return ChildBusPass Request "when
$p : Person( )
not( ChildBusPass( person == $p ) )
then
requestChildBusPass( $p );
end2.3. 構築およびデプロイに関するその他のコメント
2.3.1. Change-Sets を使用したルールの追加
knowledge bases を構築してきました。各ルールは手作業で追加しました。 JBoss Rules では、リソースを knowledge bases に追加するよう XML ファイルより宣言することもできます。この機能は change-set と呼ばれます。
XML ファイルには、knowledge base に追加できるルールリソースのリストが含まれています。このファイルを他のファイルに示すことも可能です。
重要
<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
drools-change-set-5.0.xsd' >
<add>
<resource source='http://hostname/myrules.drl' type='DRL' />
</add>
</change-set>classpath と呼ばれるプロトコルの使用も可能です。このプロトコルは、リソースの current processes クラスパスを参照します。
重要
注記
CHANGE_SET に変更されていること以外は、前述のコードとほぼ同じであることに注意してください。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newClasspathResource( "myChangeSet.xml",
getClass() ), ResourceType.CHANGE_SET );
if ( kbuilder.hasErrors() ) {
System.err.println( kbuilder.getErrors().toString() );
}<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='http://hostname/myrules.drl' type='DRL' />
<resource source='classpath:data/IntegrationTest.xls' type="DTABLE">
<decisiontable-conf input-type="XLS" worksheet-name="Tables_2" />
</resource>
</add>
</change-set><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://rules/' type='DRL' />
</add>
</change-set>2.3.2. ナレッジエージェント
KnowledgeAgent は自動的にルールリソースをロード、再ロード、およびキャッシュします。 properties ファイルより設定します。
knowledge base によって使用されるリソースに変更があった場合、KnowledgeAgent は knowledge base を更新または再構築できます。これらの更新にストラテジーを設定するには、KnowledgeAgentFactory を再設定します。
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent("MyAgent");
kagent.applyChangeSet( ResourceFactory.newUrlResource( url ) );
KnowledgeBase kbase = kagent.getKnowledgeBase();KnowledgeAgent は、追加された各リソースをスキャンします。デフォルトのポーリング間隔は60 秒です。リソースの「最終変更」日が変更された場合、 KnowledgeAgent は knowledge base を再構築します (ディレクトリがリソースの 1 つとして設定された場合は、そのディレクトリのすべての内容が変更に対してスキャンされます)。
重要
knowledge base の参照は変更後も存在するため、新たに構築された knowledge base へアクセスするには getKnowledgeBase() を呼び出す必要があります。
KnowledgeAgent と knowledge bases がどのように対話するか説明しました。これにより、本ソフトウェアの仕組みをより深く理解していただけたと思います。また、ステートレスセッションとステートフルセッションについてより包括的な知識を得られたことと思います。

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.