Ruleのライフサイクル

jBossRulesでは用語として、Fact(事実)というのがよくでてくる。1行概要。jBossRulesでは、推論エンジンがルール(仕様)に事実(データ)をMatchingして解を導くといったことをします。
jBossRulesの利用は大きく2つのpartに分かれる。ルールのコンパイルと、実行。主な登場人物は以下のとおり。
PackageBuilder: drl,dslをReaderとして読み込み、Packageオブジェクトを作成する。
Package: Ruleの集まりみたいなもの。
RuleBase:実行コンポーネントでPackageオブジェクトを保持。
WorkingMemory:RuleBaseのSession.(HibernateのSessionに近い)

依存jarは以下のとおり。
drools-core.jar:コア
drools-compiler.jar:Ruleをプレコンパイルしてしかつかわない場合は必要ない。
drools-jsr94.jar:JSR94用impl.
drools-decisiontables.jar:decisionTableを利用する場合、必要。

まずはサンプル(恒例のハロワー)

public class HelloWorld {

    public static final void main(final String[] args) {
        try {

            //RuleBaseのコンパイル/読み込み
            final RuleBase ruleBase = readRule();
            final WorkingMemory workingMemory = ruleBase.newWorkingMemory();

            //ハロワー用のPOJO
            final Message message = new Message();
            message.setMessage( "Hello World" );
            message.setStatus( Message.HELLO );

            //オブジェクトをWorkingMemoryに渡す。
            workingMemory.assertObject( message);

            //ルール実行依頼。
            workingMemory.fireAllRules();

            //状態を変更
            message.setStatus( Message.GOODBYE );

            //状態が変更したことを明示的に通知
            workingMemory.modifyObject(workingMemory.getFactHandle(message), message);

            //再度ルール実行依頼。
            workingMemory.fireAllRules();

        } catch ( final Throwable t ) {
            t.printStackTrace();
        }
    }

    /**
     * Ruleのコンパイル/読み込み処理.
     */
    private static RuleBase readRule() throws Exception {

        final Reader source = new InputStreamReader( HelloWorld.class.getResourceAsStream( "HelloWorld.drl" ) );        
        final Reader dsl = new InputStreamReader( HelloWorld.class.getResourceAsStream( "helloWorld.dsl" ) );

        //PackageをつくるBuilderを用意.
        final PackageBuilder builder = new PackageBuilder();

        //Packageを読み込んだReaderを元に作成
        builder.addPackageFromDrl( source,dsl );

        //Package取得
        final Package pkg = builder.getPackage();

        //RuleBaseの作成.
        final RuleBase ruleBase = RuleBaseFactory.newRuleBase();

        //Packageの追加
        ruleBase.addPackage( pkg );

        return ruleBase;
    }

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

        private String          message;

        private int             status;

        public String getMessage() {
            return this.message;
        }

        public void setMessage(final String message) {
            this.message = message;
        }

        public int getStatus() {
            return this.status;
        }

        public void setStatus(final int status) {
            int oldStatus = this.status;
            this.status = status;
            this.changes.firePropertyChange( "status",
                                              oldStatus,
                                              this.status );
        	
        }
}
        

仕様はこちら

package org.drools.examples
expander helloWorld.dsl
 
import org.drools.examples.HelloWorld.Message;
 
rule "Hello World"
	when
		m : Message( status == Message.HELLO, message : message )
	then
		System.out.println( message ); 
		m.setMessage( "Goodbye cruel world" );
end

rule "GoodBye"
	no-loop true
	when
		m : Message( status == Message.GOODBYE, message : message )
	then
		m.setMessage( message );
		System.out.println( message ); 
end

出力されるのは

Hello World
Goodbye cruel world

です。

2回、fireしているのでこう出力されましたが、実は、workingMemory.modifyObjectで明示的にしているのがポイントです。
1回fireされると、そのRuleはAgendaGroup(Activation)に、その状態を保持してFactのオブジェクトの属性に変更がないかぎりRuleを再Fireしません。
(HibernateのSessionにオブジェクトがPersistentている状態みたい)

んで、そのひとつの方法がこのworkingMemory.modifyObjectで明示的に属性の変更通知をするやり方です。

または、

workingMemory.assertObject( message, true);

にして、Messageクラスにも

private final PropertyChangeSupport changes = new PropertyChangeSupport( this );
    public void addPropertyChangeListener(final PropertyChangeListener l) {
        this.changes.addPropertyChangeListener( l );
    }

    public void removePropertyChangeListener(final PropertyChangeListener l) {
        this.changes.removePropertyChangeListener( l );
    }

って追加したPropertyChangeListenerを利用するやり方でもOK。workingMemory.modifyObjectしなくてすみます。

また、drl(Rule)上でもm.setStatus(),m.modify()と記述しても実はOK。
ただ、これは仕様にデータの制御が入っていていかがなものかなと。

ちなみにHibernateのSessionみたい、といったように、detachに対応するメソッドもあります。