2007.09.07 17:47

Drools 룰엔진

1. Drools 룰엔진 개요


Drools 는 "Authoring" 과 "Runtime" 두개의 메인 파트로 나누어진다. 저작 프로세스는 (Antlr3 문법에 의해 정의된) 파서에 들어갈 룰을 위한 XML이나 DRL 파일을 만드는 것을 의미한다. 파서는 올바르게 문법규칙이 지켜졌는지 체크하고  "descr"를 위한 중간 결과구조를 만든다. 여기서 "descr" 이란 룰을 "describes"하는 AST(Abstract Syntax Tree)를 가르킨다. AST는 생성된 패키지를 패키지 빌더에 넘겨준다. 패키지 빌더는 또한 어떤 코드 집합도 취할수 있고 패키지의 생성을 위해  필요한 컴파일을 수행한다.

 

여기서 AST란 언어의 토큰들이 문법규칙에 따라 해석된 결과로 추상문법 구조를 말한다. 일반적으로 Internal 노드는 오퍼레이터에 의해 분류되고 (라벨링), leaf 노드는 오퍼레이터의 연산항으로 표현되고 유도된 트리이다. AST에 의해 생성된 패키지 오브젝트는 자체적으로 포함되어 배포가능하며, 이것은 하나 이상의 룰로 구성된 직렬화된 오브젝트다.


Authoring.png

 

RuleBase는 하나 이상의 패키지로 이루어져 있는 런타임 컴포넌트를 의미한다. 패키지는 언제건 룰베이스에서 추가되거나 삭제될 수 있다.

룰베이스는 언제든 하나 이상의 WorkingMemory들을 인스턴스화 할 수 있다. 이것들은 느슨한 참조로 관리되고, 다른방법으로 설정될 필요는 없다.


WorkingMemory는 여러개의 하위 컴포넌트들로 구성되어 있는데, WorkingMemory Event Support를 포함하여, Truth Maintenance System, Agenda, 그리고 Agenda Event Support로 구성된다.

룰엔진에 Object 추가는 하나 이상의 Activations를 생성하는 결과를 유발하고, Agenda는 이런 Activation들의 실행 스케줄을 위한 책임이 있다.

 

Runtime.png

 

2. Authoring

PackageBuilder.png


저작을 위해 사용되는 클래스는 세가지이다. DrlParser, XmlParser, 그리고 PackageBuilder. 이중 두개의 파서 클래스는 주어진 Reader 인스턴스로부터 "descr" AST 모델들을 만들어 낸다. 패키지 빌더는 편리한 API들을 제공하는데 그래서 대부분 이러한 클래스들에 대해서는 잊게 될것이다.

두개의 편리한 매서드가 있는데 "addPackageFromDrl"과 "addPackageFromXml" 이다. 이 둘 모두 Reader의 인스턴스를 인자로 받는다. 아래의 예제를 보면 클래스 패스안에 있는 XML 이나 Drl 룰 파일을 가지고 어떻게 패키지를 만드는지 알 수 있을 것이다. 현재 PackageBuilder의 인스턴스에 같은 패키지 이름공간안으로 모든 패키지의 소스가 추가되는 사항을 주의해서 보기 바란다.


  1. PackageBuilder builder = new PackageBuilder();
  2. builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "package1.drl" ) ) );
  3. builder.addPackageFromXml(new InputStreamReader( getClass().getResourceAsStream( "package2.xml" ) ) );
  4. Package pkg = builder.getPackage();

 

PackageBuilder 는 PackageBuilderConfiguration을 사용해 설정된다. 기본값은 setter 나 property 셋팅을 프로그램적으로 오버라이드 할 수 있다. 현재 사용할 내장 컴파일러(Janino, Eclipse JDT), 다른 JDK의 소스 버전 (1.4, 1.5), 그리고 class loader 의 지정이 가능하다. 기본 컴파일러는 이클립스의 JDT 코어이며(JDK 버전 1.4 지원), 상위 클래스 로더는 Thread.currentThread().getContextClassLoader() 이다

 

아래의 예제는 어떻게 JANINO 컴파일러를 프로그램상에서 지정하는지 보여준다.


  1. PackageBuilderConfiguration conf = new PackageBuilderConfiguration();
  2. conf.setCompiler( PackageBuilderConfiguration.JANINO );
  3. PackageBuilder builder = new PackageBuilder( conf );

이것은 또한 프로퍼티 파일의 "drools.compiler=JANINO" 셋팅을 통해서도 가능하다.

 

아래의 예제는 어떻게 JDK의 버전을 프로그램상에서 지정하는지 보여준다.


  1. PackageBuilderConfiguration conf = new PackageBuilderConfiguration();
  2. conf.setJavaLanguageLevel("1.5");
  3. PackageBuilder builder = new PackageBuilder( conf )

 

이것은 또한 프로퍼티 파일의 "drools.compiler.languagelevel=1.5" 를 통해서도 가능하다. 참고로 PackageBuilderConfiguration 클래스는 아래와 같다.

 

PackageBuilderConfiguration.png

 

3. RuleBase

 

RuleBase.png

RuleBase는 사용할 준비가 되어 있는(예를들면, 문법검증이 끝나고, 컴파일된) 룰들이 모여있는 하나 이상의 패키지로 구성된다. RuleBase는 직렬화가 가능하고 따라서 JNDI나 다른 서비스처럼 배치될 수 있다. 전통적으로, 룰베이스는 처음 사용될때 만들어지고 캐쉬된다. 룰베이스의 지속적인 재생성이 되는 기반에서 저장하는 것은 비싼작업이다. RuleBase는 RuleBaseFactory를 사용해 인스턴스화되고 기본적으로 이것은 ReteOO 기반의 RuleBase 를 리턴한다. 인자로는 ReteOO 나 Leaps 가 명시되어 사용될 수 있다.  패키지를 추가되고, 추가할 패키지들은 차례차례로 addPackage 매서드를 이용한다.


어떤 이름공간의 패키지들을 명시할 수 있고, 여러개의 패키지들을 같은 이름공간을 가지도록 추가할 수도 있다.


  1. RuleBase ruleBase = RuleBaseFactory.newRuleBase();
  2. ruleBase.addPackage( pkg );

 

참고로 RuleBaseFactory 클래스는 아래의 그림을 참조하기 바란다.

RuleBaseFactory.png


RuleBase의 인스턴스는 쓰레드에 안전하다. 이에 기반해 사용자 어플리케이션(예를 들면 웹 어플리케이션) 안에서 여러 쓰레드 간에 하나의 인스턴스를 공유할 수 있다.

RuleBase로 가능한 가장 일반적인 작업은 WorkingMemory를 만드는 것이다. RuleBase는 또한 그것이 담고 있는 어떤 WorkingMemory 들과도 느슨한 참조를 가지고 있다.그래서  만약 룰이 변경된다면 (또는 장기간 실행되는 WorkingMemory를 위해 룰을 추가하거나 삭제한다면) 최신의 변경된 룰로 업데이트가 가능하다. (WorkingMemory의 재시작이 필요없다)

WorkingMemory의 관리를 위해 사용자가 특별히 명시할 수 있는 부분은 없지만, 만약 RuleBase가 업데이트 되지 않도록 하고 싶다면 단지 그렇게 하면 된다.

 

  1. ruleBase.newWorkingMemory(); // maintains a weak reference
  2. ruleBase.newWorkingMemory( false ); // do not maintain a weak reference

 

패키지는 언제건 추가되거나 삭제될 수 있는데, 모든 변경은 이미 존재하는 워킹메모리에 전파될 것이다. Activations 이 FIRE 되는 결과를 얻기 위해서는 fireAllRules() 를 호출하는 것을 잊지 말기 바란다.

 

  1. ruleBase.addPackage( pkg ); // Add a package instance
  2. ruleBase.removePackage( "org.com.sample" ); //remove a package, and all tis part, by it's namespace
  3. ruleBase.removeRule( "org.com.sample", "my rule" ); //remove a specific rule from a namespace

 

개별적인 룰의 삭제를 위한 메서드가 있지만, 개별적인 룰을 추가하는 매서드는 없다. 개별적인 룰을 추가하고 싶다면 하나의 룰을 가지는 패키지를  추가해야 한다.

RuleBaseConfigurator 는 룰베이스에 추가적인 행위를 명시하는데 사용될 수 있다. RuleBaseConfiguration은 룰베이스에 추가된 후에는 변경할 수 없다.

 

  1. RuleBaseConfiguration conf = new RuleBaseConfiguration();
  2. conf.setProperty( RuleBaseConfiguration.PROPERTY_ASSERT_BEHAVIOR, RuleBaseConfiguration.WM_BEHAVIOR_EQUALUTY);
  3. RuleBase ruleBase = new ReteooRuleBase( conf );

 

두개의 메인 프로퍼티를 알아야 하는데 PROPERTY_ASSERT_BEHAVIOR 와 마지막 섹션에서 설명되는 PROPERTY_LOGICAL_OVERRIDE_BEHAVIOR 이다. 모든 속성들과 속성값은 RuleBaseConfiguration 의 public static 필드이다.

RuleBaseConfiguration.png


1.4 WorkingMemory

WorkingMemory.png

워킹메모리는 룰엔진이 런타임시에 사용하는 메인 클래스이다. 그것은 "ASSERTED" 된 모든 데이타를 위한 참조를 가지고 있고(제거되기 전까지) 어플리케이션과 상호작용하는 장소이다. 워킹메모리들은 상태값을 가질수 있는 객체들이며(stateful objects) 그것들은 길게 유지되거나 짧게 유지될 수 있다. 만약 상태값이 없는 방식의 (stateless manner) 워킹메모리를 가진 엔진과 상호작용 하고자 한다면, 이것은 매 세션을 위해서 RuleBase 객체를 이용해 newWorkingMemory를 사용해야 하는것을 의미한다. 그리고 사용이 끝났을때 해당 워킹메모리를 폐기한다. (워킹메모리 생성하는 작업은 비용이 큰 작업은 아니다.) 다른 패턴은 오랫동안(대화식처럼) 유지해야 하는 워킹메모리이다. 그리고 이것은 새로운 FACT로 업데이트가 항상 유지되어야 한다.  워킹메모리를 정리하기를 원한다면, 좋은 방법은 dispose() 매서드를 사용하는 것이다. 그러면 워킹메모리가 속한 룰베이스에서 워킹메모리에 대한 참조가 삭제된다. 어쨋건, 이것은 약한 참조관계이며, 결국에는 가비지로 처리될 것이다. WorkingMemory Action 항목은 워킹메모리에 FACT의 추가, 삭제, 수정을 설명하는데 사용된다.

 

1.4.1 Facts

FACT는 워킹메모리 안으로 추가되는 어플리케이션의 객체(beans)이다. FACT는 룰이 접근할 수 있는 어떤 자바 오브젝트도 될수 있다. 룰엔진에서 FACT 들은 클론이 아니며 종국에 가서 FACT 들은 결국 모두 참조와 포인터이다. FACT 는 어플리케이션의 데이타다. String 클래스나 getter, setter가 없는 다른 클래스들은 올바른 FACT가 아니며, 오브젝트와 상호작용하는 표준 getter, setter의 자바빈 방식으로 필드 상수를 직접 사용할 수 없다.


1.4.2 Assertion

Assertion은 facts에 대해서 워킹메모리에 말해주는 행위을 한다. 예를 들면 WorkingMemory.assertObject(yourObject) 처럼.  FACT가 ASSERT될 때, 그것은 룰들이나 기타사항에 매치되는지 검사된다. 이 작업의 모든것은 ASSERTION 동안에 수행된다. 어쨋건, 사용자가 fireAllRules()를 호출하기 전에는 어떤 룰도 실행되지 않는다. 따라서 사용자가 FACT들의 ASSERTION이 끝날때까지 "fireAllRules()"를 호출하지 않을수 있다. 이것은 "fireAllRules()"를 호출할때 작업이 발생할거라고 생각하는 사람들에 의해 흔히 잘못 이해된다.


객체가 워킹메모리에 추가되면(ASSERTED) FactHandle을 리턴한다. 이 FactHandle은 워킹메모리 안으로 추가된 객체를 상징하는데 사용되는 표식이다. 그것은 또한 사용자가 객체를 수정하거나 삭제하기를 원할때 워킹메모리와 상호작용하는데 사용할 수 있다.

 

  1. Cheese stilton = new Cheese("stilton");
  2. FactHandle stiltonHandle = workingMemory.assertObject( stilton );

 

RuleBase 섹션에서 이미 언급한 것처럼 워킹메모리는 두개의 ASSERTION 모드(equality, identity) 에서 작업한다. identity 모드가 기본값이다.

Identity란 워킹메모리가 모든 추가된 객체를 저장하는데 IdentityHashMap을 사용하는걸 의미한다. 새로운 ASSERTION 인스턴스가 추가될때 항상 새로운 Facthandle을 리턴한다. 그러나 만약 인스턴스가 두번째로 추가되면, 그것은 이전의 FactHandle을 리턴한다. 쉽게 말하면 같은 FACT를 위한 두번째 ASSERTION 은 무시된다.


Equality는 워킹메모리가 모든 오브젝트를 저장하는데 HashMap을 사용하는걸 의미하며, 새로운 ASSERTION 인스턴스는 같은 클래스들이 추가되더라도 단지 새로운 FactHandle을 리턴한다.


1.4.3 Retraction

Retraction은 사용자가 워킹메모리에서 FACT를 취소할때(제거할때) 사용된다. 그것은 더이상 추적하고 매치되지 않고, 그것에 의존적이고 활성화된 어떤 룰도 취소되는것을 의미한다. 물론 FACT가 존재하지 않는 것에 기반한 룰들을 가질수 있다. 이런 경우 FACT의 취소는 룰의 활성화에 원인이 될것이다.

Retraction은 추가될때 리턴된 FactHandle을 사용한다.

 

  1. Cheese stilton = new Cheese("stilton");
  2. FactHandle stiltonHandle = workingMemory.assertObject( stilton );
  3. ...
  4. workingMemory.retractObject( stiltonHandle );


1.4.4 Modification

룰엔진은 반드시 수정된 FACT들이 통지되어야 한다. 그래서 그것은 재 프로세스가 가능하다(매칭을 다시하는 것). Modification은 내부적으로는 실제 제거와 추가작업을 통해 이루어진다. 그래서 워킹메모리를 깨끗하게 만들고 다시 시작하며 워킹메모리에 변경된 객체들을 알려주기 위해서 modifyObject 매서드를 이용한다. 객체를 위해서 워킹메모리가 그들 스스로 변경된 객체를 알수는 없다. modifyObject는 항상 두번째 파라미터처럼 변경된 객체를 받는다는것을 주의하라. 이것은 변하지 않는 오브젝트들을 위해 사용자가 새로운 인스턴스들을 명세하는 것을 가능하게 한다.

 

  1. Cheese stilton = new Cheese("stilton");
  2. FactHandle stiltonHandle = workingMemory.assertObject( stilton );
  3. ...
  4. stilton.setPrice( 100 );
  5. workingMemory.modifyObject( stiltonHandle, stilton);


 1.4.5 Property Change Listener

만약 사용자의 FACT객체가 자바빈즈라면, 사용자는 속성 변경 리스너를 구현할 수 있다. 그리고 룰엔진에게 그것을 말해줄 수 있다. 이것의 의미는 룰엔진이 자동으로 FACT가 변경되었을때 이사실을 알수 있다는 말이다. 그리고 그것의 변경사항에 상응해서 행동한다. (사용자가 그것을 알릴 필요가 없다) 이것을 자동으로 도와줄수 있는 프록시 라이브러리가 있다. (Drools의 나중 버전에는 이것을 좀 더 쉽게 만들어 주는것이 번들로 들어갈 것이다)

동적인 모드로 객체를 사용하기 위해서는 assertObject의 두번째 파라미터를 true로 명시하면 된다.

 

  1. Cheese stilton = new Cheese("stilton");
  2. FactHandle stiltonHandle = workingMemory.assertObject( stilton, true); //specifies that this is a daynamic fact


자바빈즈에 동적으로 PropertyChangeSupport 필드를 추가로 만들려면, 두가지 추가/삭제 매서드를 기억해라. 그리고 각각의 setter 가 PropertyChangeSupport 에게 변경사항을 알려준다는 것을 명심하라.

 

  1. private final PropertyChangeSupport changes = new PropertyChangeSupport( this );
  2. ...
  3. public void addPropertyChangeListener(final PropertyChangeListener l) {
    1. this.changes.addPropertyChangeListener( l );
  4. }
  5.  
  6. public void removePropertyChangeListener(final PropertyChangeListener l) {
    1. this.changes.removePropertyChangeListener( l );
  7. }
  8.  
  9. public void setState(final String newState) {
    1. String oldState = this.state;
    2. this.state = newState;
    3. this.changes.firePropertyChange("state", oldState, newState);
  10. }


1.4.6 Globals

Globals는 ASSERT가 필요 없이 룰엔진 안으로 넘겨질 수 있는 오브젝트의 이름이다. 가장 자주 사용되는 부분은 정적인 정보를 위해 사용하거나, 룰의 RHS에서 사용되는 서비스, 또는 아마도 룰엔진으로 부터 리턴받는 오브젝트 정도로 사용된다.

 

  1. new List list = new ArrayList;
  2. workingMemory.setGlobal("list", list);

 

Global의 정의는 반드시 룰베이스 안에서 같은 타입으로 정의되어야만 한다. 그렇지 않다면 런타임오류가 발생할 것이다. 만약에 사용자가 그것을 설정하기 전에 룰안에서 Global을 평가한다면, 사용자는 NullPointException을 얻을 것이다.


1.4.7 Shadow Facts

Shadow Facts 는 ASSERT된 객체의 얕은 카피이다. Shadow Facts는 워킹메모리 안으로 ASSERT된 객체의 카피로써 캐쉬된다. 이 항목은 일반적으로 JESS의 기능으로 알려져있다 (Java Expert System Shell) 원래 Shadow Fact는 Ttruth Maintenance의 컨셉을 위해 만들어진 흔적이다. 기본적인 아이디어는 전문가 시스템은 이끌어낸 결론이 정확하다는 것을 보장해야 한다는 것이다.

 

실행되는 시스템은 평가동안 FACT가 변할 수 있다. 이런 경우가 발생했을때, 룰엔진은 변경이 발생했다는 것을 반드시 알아야 하고 타당하게 변경사항을 조정해야 한다. 일반적으로 이런 사항을 보장하기 위해 두가지 방법이 있다. 첫번째는 추론 프로세스가 진행되는 동안 모든 FACT를 강제적으로 잠그는 것이다. 두번째 방법은 객체의 카피를 캐쉬하고 강제로 모든 변경사항을 룰엔진에 반영시키는 것이다. 이 방법 같은 경우, 변경사항은 일시적 유행처럼 처리된다. Shadow Fact는 다중 세션에 의해 엔진이 공유되는 멀티 쓰레드 환경에서 매우 중요하다. Truth Maintenance 없이는 시스템은 정확한 결과를 입증하기 위해서는 어려운 시간을 가진다.

 

Shadow Fact의 메인 이점은 그것이 개발을 더 쉽게 만든다는 것이다. 개발자가 FACT의 변경사항 추적을 강제적으로 유지할때, 그것은 에러를 유발할 수 있고 디버그하기 매우 어렵다. 룰엔진을 사용해 적당히 복잡한 시스템을 만드는 것은 FACT의 변경사항 추적과 그것을 룰엔진에 알려야 되는 무거운 짐을 더하지 않아도 충분히 어렵다.

 

Drools 3.0 에서는 Shadow Fact가 구현되지 않았다. 그러나 미래를 위해 계획중이다. 어쨋건, 사용자는 그것이 정말 필요하다면  그 자신의 프록시 구현을 작성하는것을 통해 이 편리성을 스스로 구현할 수 있을것이다.

 

1.4.8 Initial Fact

NOT과 같은 조건항을 추가하기 위해서 Initial Fact로 알려진 뭔가와 함께 룰엔진은 "seed"가 필요하다. 이 FACT는 사용자에게 보여질 것으로 예정되지 않은 특별한 FACT이다. 깨끗한 워킹메모리에 최초의 엑션(assert, fireAllRules)이 이루어질떄, Initial Fact는 RETE네트웍을 통해 전파된다. 이것은 LHS가 없는 룰이나, 일반 FACT들을 사용하지 않는것을 허용한다. (외부소스에서 자료를 가져오는것을 사용하는 룰과 같은)

예를 들면, 만약 새로운 WorkingMemory가 만들어지고 아무런 FACT도 assert된 게 없다면, fireAllRules를 호출하는 것은 Initial Fact를 전파하여 룰을 활성화 하는 것을 가능하게 한다. (그렇지 않으면 시작하는 부분에서 아무것도 일어나지 않을 것이다?)


1.4.9 Stateless and Statefull Sessions

룰엔진은 기술적으로 상태값을 가지는 RETE 같은 알고리즘에 기반해, 상태값을 가지는 룰엔진이다. 이것은 RETE네트웍이 더 오래 존재하고, 변경을 감지하고, FACT가 축적되었을때 그것이 가장 효율적이라는걸 의미한다. 그것이 가장 빛을 발할때이다.

 

어쨋건, 많은 시나리오가 간단하게 상태값이 없는(stateless) 모드를 요구한다. 모든 FACT들은 룰엔진에 새롭게 제공된다. 그리고 룰들이 불려진다. Drools의 경우 이런 시나리오에서도 잘 작동한다. 사실은 RETE 알고리즘이 룰들의 엑션들이 다른 룰의 Fire를 유도하는, 또는 많은 수의 FACT들과 룰들이 매칭되는 stateless 세션 시나리오에서도 여전히 유용하다.

JSR-94 는 statefule 과 stateless 모드를 모두 명시하고 있다. 그러나 내장 API를 사용하는 측면에서 새로운 워킹메모리의 인스턴스를 만들고 세션이 종료될때, 버리는 측면에서는 두 모두가 동등하다.

 

1.5 Agenda

Agenda.png

Agenda는 RETE의 기능이다. 워킹메모리의 동작 동안에 룰들은 전부 매치되고 실행을 위해 명료해진다. 하나의 워킹메모리 동작은 많은 명료한 룰들의 결과가 될 수 있다. 룰이 전부 매치 되었을때, 룰과 매치된 FACT들을 참조하는 Activation이 만들어진다. 그리고 Agenda에 위치한다. Agenda는 충동해결 전략을 사용해 이러한 Activation들의 실행 순서를 제어한다.

 

Drools 엔진은 재귀적인 "2 단계" 모드 기반으로 동작한다.

  • Working Memory Actions : Consequence 또는 메인 자바 어플리케이션 프로세스 어느쪽에서든 이것이 일의 대부분이 일어나는 곳이다. 
    일단 Consequence가 끝나거나 메인 자바 어플리케이션 프로세스가 fireAllRule()를 호출하면, 엔진은 Agenda의 평가단계로 넘어간다.
  • Agenda Evaluation : FIRE할 룰을 선정하는 시도. 만약 룰이 발견되지 않으면 단계를 종료하고 그렇지 않으면 발견된 룰을 FIRE를 시도하고, Working Memory Actions 단계로 스위치된다. 그리고 Agenda가 빌때까지 프로세스가 반복된다.

 Two_Phase.png

 

프로세스는 Agenda가 깨끗해질 때까지 반복된다. 이 경우에 컨트롤은 호출한 어플리케이션으로 리턴된다. WorkingMemory Actions이 일어나고 있을때, 어떤 룰도 FIRE 되지 않는다.


1.5.1 Conflict Resolution

Conflict ResolutionAgenda에 많은 룰이 있을 경우에 요구된다. FIRE되는 룰은 워킹메모리에 사이드 이펙트를 미친다. 따라서 룰엔진은 룰들의 FIRE될 순서를 알아야 한다. (예를 들면, A룰이 FIRE 되면, B룰이 Agenda에서 지워질 수 있다)

 

Drools에 적용된 충돌방지 전략들은 SalienceLIFO(last in, first out) 이다.

가장 확연한 전략중의 하나는 Salience 또는 사용자에 의해 어떤 룰이 더 높은 우선순위를 가지고 있는지(더 높은 우선 숫자를 주어서) 알려주는 priority 이다. 이런 경우 더 높은 우선순위의 룰은 항상 먼저 선호될 것이다. LIFO 우선순위는 Working Memory Action 을 통해 카운터 값이 지정한것에 기반한다. 같은 엑션을 하는 많은 룰들이 만들어지면, 같은 값을 가지며, 이들의 실행은 임의로 간주된다. 일반적인 룰처럼 어떤 특정한 순서에서 FIRE하는 룰들을 카운트하지 않는것은 좋은 아이디어다. 따라서 FLOW에 대한 걱정없이 룰을 작성하고, 실행한다.

 

1.5.2. Agenda Group

Agenda Group은 룰(실질적으로는 activation)들을 Agenda 안에서 분할하는 방법이며, 어떤 한시점에 단지 하나의 그룹만이 포커스를 가질수 있다. 이것은 그룹안에 있는 룰들을 위한 activation들이 포커스가 있을 경우에만 오로지 효과가 있다는것을 의미한다. 사용자는 또한 룰들에 "auto focus" 를 사용할 수 있다. 이것은 룰들의 조건이 True일때 그것의 Agenda Group을 위해 자동으로 포커스를 주는것을 의미한다.

 

그들은 종종 CLIPS 용어상으로는 "modules" 로 알려져있다. Agenda Group은 그룹화 된 룰 사이에 FLOW를 만드는 손쉬운 방법이다. 사용자는 룰엔진 안에서나 API를 통해서나 포커스를 가지는 그룹을 스위치할 수  있다. 만약 룰들이 처리를 위한 많은 단계나 순서를 위해 필요하다면, 이것을 위해 agenda-group의 사용을 고려해 보길 바란다.

 

호출된 setFocus(...) 메서드는 각각 Agenda Group을 스택안으로 밀어넣는다. 포커스 그룹이 비었을 때, 그것은 떨어져 나가고, 스택의 다음 하나가 평가된다. 하나의 Agenda Group은 스택의 여러 위치에서 나타날 수 있다. 기본 Agenda Group은 MAIN이며, Agenda Group을 명시하지 않은 모든 룰들은 "MAIN" Agenda에 위치한다. 그것은 또한 항상 스택의 첫번째 그룹이고 기본으로 포커스가 주어진다.


1.5.3. Agenda Filters

AgendaFilter.png

필터들은 FIRE 로부터 activation을 허용하거나 거부하는데 사용할 필터 인터페이스의 옵션 구현이다.  Drools는 아래의 편한 기본 구현체를 제공한다.

  • RuleNameEndWithAgendaFilter
  • RuleNameEqualAgendaFilter
  • RuleNameStartsWithAgendaFilter

 

필터를 사용하기 위해서는 fireAllRules()를 호출할 때 그것을 명시해야 한다. 아래의 예는 "Test"로 끝나는 모든 룰들을 필터로 제외시킬 것이다.

 

  1. workingMemory.fireAllRules( new RuleNameEndsWithAgendaFilter( "Test" );


1.6 Truth Maintenance with Logical Objects

보통의 ASSERTION에서, 사용자는 명시적으로 FACT를 삭제하는걸 필요로 한다. 논리적인 ASSERTION에서, FACT는 자동으로 제거될수 있도록 ASSERTED 된다. ASSERTED된 FACT는 그것의 조건이 더이상 참 값이 아닐때, 제거될 것이다. (그것은 실제로는 더 영리하다. 만약에 논리적인 ASSERTION이 가능했던 조건이 없다면, 아마로 그것은 제거될 것이다.)

일반적인 ASSERTION은 STATED 하다. (단지 직관적인 개념처럼 FACT가 진술되었다) hashMap을 사용하고 있을때 우리가 얼마나 많은 시간을 특정한 동일여부를 확인하는지 세어보라. 이것의 의미는 우리가 다른 인스턴스가 같은지 많이 카운트 해본다는 의미이다. 우리가 어떤 객체를 논리적으로 ASSERT 할때, 우리는 그것을 정당화하고 FIRE된 룰에 의해 정당화된다. 각각의 논리적인 ASSERTION은 다만 하나의 같은 객체를 가질수 있다. 하위 단계의 같은 논리적인 ASSERTION은 이 논리적인 ASSERTION을 위해 카운트하는 것을 정당화하는 작업을 증가시킨다. 각각의 정당화는 우리가 더이상의 논리적인 객체가 자동으로 제거되도록 정당화할 수 없을 때 제거된다.

 

만약에 우리가 같은 STATED 객체를 논리적으로 ASSERT 한다면 그것은 실패할 것이고, null 을 리턴할 것이다. 만약에 우리가 이미 존재하는 객체에 대해 상태를 변경하면, 그것은 JUSTIFIED된다. 우리가 FACT를 재작성해서. 설정정보의 셋팅을 통해 이와 같이 오버라이딩 작업을 하는 방법은, WM_BEHAVIOR_PRESERVE를 이용한다. 이 속성이 무시되게 셋팅될때, 우리는 존재하는 핸들을 사용할 수 있다. 그리고 존재하는 인스턴스를 새로운 객체로 바꿀수 있다. 이것이 기본 방식이다. 그렇지 않으면 우리는 그것을 STATED로 오버라이드 하고 이것은 새로운 FactHandle을 만들 것이다.


이것은 처음 읽을때 혼동스러울 수 있다. 그러나 희망적으로 아래의 플로우차트가 도와줄 것이다. 새로운 FactHandle이 리턴될때, 이것은 또한 그 객체가 네트워크를 통해 전파되었다는 것을 가르킨다.

Stated_Assertion.png

 

위의 그림은 Stated Assertion을 설명한다. 아래의 그림은 Logically Assertion 을 설명한다.

 

Logical_Assertion.png


1.6.1  예제 시나리오

예제는 일을 명쾌하게 만든다. 크레딧 카드를 처리하는 어플리케이션을 이미지화 해보자. 주어진 특정 계정을 위해 트렌젝션을 처리한다. (그리고 우리는 단일 계정 트렌젝션에 대한 지식이 축적된 워킹메모리를 가지고 있다) 룰엔진은 만약 트렌젝션이 가능한 사기성인지 아닌지 결정하기 위해 최선의 방법으로 처리할 것이다.  기본적으로 룰들을 가지고 있는 이 룰엔진을 상상해보면, 그것은 의심스러운 이유가 있다면 트렌젝션을 걷어찰 것이고, 모든것이 정상이면 처리할 것이다.


물론 연관없이 운영되는 많은 룰들이 있다. (일반적인 계산을 수행하는). 현재 의심스러운 이유들에 대한 트리거가 가능한 많은 이유들이 있고 이에 대한 처리가 누군가는 은행에 알릴것이고, 누군가는 커다란 트렌젝션들의 시퀀스로 신용카드의 도둑질의 지리적인 위치, 또는 보고를 위한 거래등을 처리할 것이다. 그러나 많은 룰안에서 이러한 모든 작은 조건들을 겉핥기로 하기보다는 "의심스로운 계정" 이라 불리는 FACT 클래스가 있다고 상상해보라.


그러면 업무는 의심스러운 점을 알리는 일들을 찾기 위한 룰들의 시리즈가 될 수 있다. 그리고 많약에 그들이 FIRE되면, 그들은 단순히 새로운 "의심스러운 계정"을 ASSERT하면 된다.  모든 다른 룰들은 그들의 필요에 의해 "의심스러운 계정" 인지, 또는 "의심스럽지 않은 계정" 인지 조건을 가질 것이다.

 

이것에는 다른 룰들을 손볼 필요 없이 의혹을 불러일으키는 점 주위로 많은 규칙들이 모여있는 이점을 가진다는 점을 명심하라. FACT들이 의심스러운 계정의 ASSERTION이 제거되는 것을 유발하면, 룰엔진은 작동상 일반모드로  돌아간다.


사용자가 이런 방식의 사용과 거리를 둘 경우에, 실세계에서와 같은 작은 행위들을 위한 룰을 허용하고 주로 더 관리가 필요한 룰들을 많이 만들수 있으므로 사용자는 Truth Maintenance를 주의해야 한다.


1.6.2 중요사항 : Equality for Java Object

결국 올바로 일하기 위해서는 Truth Maintenance를 위해 주요한 사항들을 명심해야 한다. 사용자의 FACT 객체들은(아마도 자바빈즈) equal과 hashCode 매서드를 오버라이드 할 수 있다. 그리고 올바른 Truth Maintenance 시스템에 따르면 2개의 다른 물리적인 객체가 같은 값으로 같다는 것을 아는것이 필수적이다. equal 와 hashCode 모두 반드시 정확히 오버라이드 되어야 한다.


자바 표준에 의거해 두개의 오브젝트가 같다는 것은, 그들의 equal 매서드가 각각의 비교되는 오브젝트에 대해 참값을 리턴하고, 그들의 hashCode가 같은 값을 리턴하는 것이다. 더 자세한 사항은 Java API를 보기 바란다. (그러나 equals와 hashCode를 둘다 같이 오버라이드 해야 하는것은 명심해라. )


1.7 Event Model

Event Pacakage는 룰엔진에게 이벤트를 알려준다. 룰이 FIRE 된 것을 포함해서. 객체가 ASSERTED 된 것등. 이것은 사용자가 로그나 추적감시하는 행위를 당신의 어플리케이션, 또는 룰들에서 분리할 수 있도록 허용해준다. WorkingMemoryEventListener, AgendEventListener 와 같은 두 타입의 이벤트 리스너가 있다.

WorkingMemoryEventListener.png

 

상기 그림은 WorkingMemoryEventListener 이고 AgendaEventListener는 아래 그림을 참고하기 바란다.

 

AgendaEventListener.png

 

이 두개의 Event Listener 모두 각각의 메서드를 구현한 기본적인 구현체를 가지고 있다 . 그러나 이것들은 단지 당신이 각각의 매서드를 구현하기 위해 상속받을 수 있는(DefaultAgendaEventListener, DefaultWorkingMemoryEventListener) 편리한 클래스들이다. 아래는 어떻게 DefaultAgendaEventListener를 확장하고 그것을 WorkingMemory에 추가하는지를 보여준다. 예제는 단지 룰들이 FIRED 될때 FIRE된 이벤트를 출력한다.

 

  1. workingMemory.addEventListener( new DefaultAgendaEventListener() {
    1. pulbicb void afterActivationFired(AfterActivationFiredEvent event) {
      1. super.afterActivationFired( event );
      2. System.out.println( event );
    2. }
  2. });


Drools는 또한 debug print 구문으로 각각의 매서드를 구현한 DebugWorkingMemoryEventListener, DebugAgendaEventListener를 제공한다.

 

  1. workingMemory.addEventListener( new DebugWorkingEventListener() );


Drools 는 또한 이클립스의 룰 IDE는 또 하위 로그추적감시와 뷰어를 제공한다.

 

이 글은 스프링노트에서 작성되었습니다.

Trackback 0 Comment 1