ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • drools 기본개념
    개발일지 2020. 8. 31. 23:13

    [Drools]Drools 기본 rule 작성법

     

     

     

    ** Drools : BRMS 엔진, 오픈소스 

    ** 배포 : www.jboss.org 통해 배포

     

    ** Drools 의 기본 rule 파일은 DRL 파일이다. 

    * DRL 파일에 대한 기본 작성법을 overview 해 보자.

     

    * rule 파일 기본 구조

    rule "<name>"

        <attribute>*

    when

        <conditional element>*

    then

        <action>*

    end

     

    * name

    룰의 이름이 된다.

     

    * attribute

    룰을 실행하는데 영향을 주는 속성값. 선언적인 방법을 사용한다.

    ** attribute의 종류

    * no-loop

    룰이 다른 실행결과에 따라 변경되어 재실행 되는 경우 무한 루프를 돌 가능성이 생긴다. 

    그런 경우에 대비하여 루프를 막아주는 속성(기본값:false)

    * ruleflow-group

    룰의 실행 시 실행단위로 지정해 줄 수 있는 그룹명을 설정하는 것으로 보인다. 

    기본값은 없다.

    * lock-on-activate 

    정확히 뭘 하는 녀석인지 잘 모르겠다.

    * salience

    룰의 우선순위 설정. 기본값은 0 이고, 양수/음수 값을 사용할 수 있다.

    * agenda-group

    ruleflow-group와 비슷한 개념으로 보인다. 실행단위 설정같은것으로 보임

    * auto-focus

    룰이 활성화 되었으나 agenda-group이 아직 focus를 못 받았을 때

    잠재적으로 focus를 활성화 시켜주는 옵션이라나머라나.

    * activation-group

    매뉴얼만 봐서는 모르겠다. 영어가 짧아서.

    * dialect

    java or mvel 사용언어를 지정하는 놈이다.

    * date-effective

    설정한 날짜에만 룰이 동작한다.

    * date-expires

    설정한 날짜까지만 룰이 동작한다.

    * duration

    룰이 동작하는 기간을 설정해준다. 타입은 long(deprecated 되었다고 함)

    ==> 대신 timer 속성을 쓴다. 다음과 같은 형식을 따른다.(cron 형식도 된다. 대박)

    timer ( int: <initial delay> <repeat interval>? )

    timer ( int: 30s )

    timer ( int: 30s 5m )

    timer ( cron: <cron expression> )

    timer ( cron:* 0/15 * * * ? )



    출처: https://java808.tistory.com/entry/Drools-기본-rule-작성법 [잡동사니 기술연구소^.^]

     

     

    이글은 Drools 메뉴얼을 참고해 작성되었습니다.

    1.  Overview

     

    Drools 3부터는 XML 기반의 텍스트 포맷이 아닌 "native" 룰 언어를 제공한다. 이 포맷은 매우 가볍고 자연스러운 도메인 기반의 언어로의 확장을 지원한다.

    여기서는 이 내장 룰 언어의 주요 개념을 살펴본다.  다이어그램은 "rail road"로 알려진 다이어그램을 사용했으며, 기본적으로 언어 단위의 설명을 위해서는 플로우챠트를 사용한다.

     

    기술적인 사항을 위해서는 룰 언어를 위한 Antlr3 의 문법파일인 "drl.g" 파일을 참조해도 좋다. 만약 룰 관련 IDE를 사용한다면, 많은 수의 룰 구조가 이미 사용자를 위해 제공된다. (contents assistance) 예를 들면 "ru" 라고 입력후 ctrl_space를 누르면, 기본 룰 구조가 생성될 것이다.

     

    1-1. Rule file

    룰 파일은 .drl 확장자를 가진 일반적인 파일이다. 하나의 drl 파일은 여러개의 룰 또는 함수들이 존재할 수 있다. 어쨋건 사용자는 많은 룰파일을 통해 룰들을 스프레드하게 펼쳐서 사용할 수 있다. (이 경우, 확장자는 .rule 이 권고되지만 필요한건 아니다) 여러개의 파일로 존재하는 룰들은 많은 수의 룰들을 관리하는데 도움이 될 수 있다. DRL 파일은 단순한 텍스트 파일이다.

     

    1-2. 룰은 무엇을 만드는가?

    룰은 아래의 러프한 구조를 가진다.

     

    rule "name"

    ATTRIBUTES

    이글은 Drools 메뉴얼을 참고해 작성되었습니다.



    when

    LHS

    then

    RHS

    end

     

    정말 간단하다. 구두점(마침표)도 필요하지 않다. 심지어 룰 이름을 위한 " 역시 옵션이며 new line 역시 옵션이다. ATTRIBUTES 역시 간단하며 항상 옵션이다. LHS 는 룰의 조건파트이다. RHS는 자바 문법코드로 실행될수 있는 코드블럭이며 (곧 다른 언어 문법도 제공될 예정이다. groovy  나 C# 같은)

    단지 특별한 키워드는 FACT를 asserting, retracting, modifying 하는 부분이다. LHS에서 정의된(바인드된) 어떤 변수도 RHS 영역에서 사용할 수 있다.

     

    주의할 점은 drl 자체에서 공백은 중요하지 않지만, Domain Specific Language에서는 예외이다.

     

    1-3. Domain Specific Language

    DSL은 기본 내장 룰언어 기반의 확장처럼 구현된다. 이것은 'expander" 메카니즘을 사용하며, 이것은 확장가능한 API를 의미한다.

    도메인 영역 또는 자연어 기반의 언어와 사용할 도메인 모델과의 매핑을 가진 .dsl 확장자가 사용된다. DSLs/expanders 는 컴파일 타임에 룰 소스안에서 처리된다. 만약 DSL이 만들고자 하는 어플리케이션에 이익이 된다고 믿는다면 시간이 지나면 여러 도메인을 위해 사용할 수 있는 많은 부가적인 DSLs 이 가용하거나 미리 만들어져 있을 거라는 사실이 도움이 될 것이다. 주로 룰작성을 보기 좋게 도와주지만, 어떤 사람들을 위해서는 기본 내장 룰 언어가 좋을수 있다.

    Freedom is good !

     

     1-4. 예약어

    룰엔진에는 룰언어에서 사용하는 약간의 미리 예약된 키워드들이 있다. 룰 작성시나, 도메인 객체, 메서드, 속성등을 정의할때 충돌이 나지 않도록 피하는게 현명할 것이다. 미리 예약된 키워드들의 목록은 아래와 같다.

     

    when, then, rule, end, contains, metches, and, or, modify, retract, assert, salience, function, query, exists, eval, agenda-group, no-loop, duration, ->, not, auto-focus

     

    2. 주석문

    주석문의 경우 룰엔진에 의해 무시되는 문자열의 섹션이다. 룰엔진이 주석문을 만났을때 주석은 효과적으로 제거된다.

     

    2-1. 단일 라인 주석

     

     

     

    2-2. 복수 라인 주석

     

     

     

    3. 패키지

    패키지는 룰과 그리고 다른 관계있는 예를 들면 imports, globals 와 같은 구성물의 집합이다. 패키지의 요소들은 전통적으로 각각 다른것들과 관계가 있다.

    패키지는 이름공간을 표현하고 룰의 그룹핑을 유일하게 유지할 수 있는 좋은 아이디어이다. 패키지의 이름은 그것자체로 이름공간을 의미한다. 어쨋건 그것은 파일이나 폴더와 연관이 있는것은 아니다.

    일반적인 구조에서 패키지를 위한 모든룰들은 패키지 선언이 되어있는 파일에 모두 있을 것이다.

     

    패키지 이름은 일반적인 자바 컨벤션을 사용해 선언되고 이름공간을 가지며, 스페이스를 허용하는 룰 이름과 다르게 패키지 이름은 이름 중간에 스페이스를 허용하지 않는다. "package" 나 "expander" 와 같은 구문은 어떤 룰 정의가 나타나기 전인 파일의 맨 위에 존재한다. 모든 경우에 세미콜론은 옵션이다.

     

     

     

    3-1. import

    import 구문은 자바에서의 import 구문과 같다. 룰에서 사용하기를 원하는 어떤 오브젝트를 위해서 전체 경로와 타입이름을 명시하는게 필요하다. Drools의 경우 같은 이름으로 되어 있는 자바패키지로부터 자동으로 클래스를 import 한다.

     

     

     

    3-2. expander

    expander 구문은 옵션이며, DSL 설정(보통은 별도의 파일로 저장되어 있는)을 명시하기 위해 사용된다. 이것은 해석기가 룰안에서 작성된 룰을 이해하는실마리로 제공된다.

     

     

     

    3-3. global

    Globals 은 전역변수들이다. 만약 복수의 패키지에서 같은 indentifier 의 전역변수를 선언하고 싶다면, 그것은 반드시 같은 타입이어야 하고, 모든 참조는 같은 전역값을 가르킬 것이다. 이것은 전통적으로 리턴값을 위해 사용된다. 예를 들면, 엑션의 로그나, 룰에서 사용될 서비스나 데이타를 제공하기 위해서 사용된다. Globals  는 워킹메모리로 추가되지 않으며, 룰엔진은 Globals가 변경되는지 걱정하지 않는다. 이런 이유때문에 Globals는 마지막으로 사용된 값이 없어 제약조건으로 사용되지 않는다. 제약사항으로서 globals의 올바르지 않은 사용은 놀랄만한 결과를 야기시킬 것이다. 나쁜쪽으로 놀라게 될것인데, 아마도 의사가 당신의 XRay를 보고서 "매우 흥미롭군요" 라로 말하는 느낌일 것이다.

     

    이것은 원한다면 어떤 오브젝트라도 당신이 룰에서 넘겨받아 사용할 수 있다는 것을 의미한다. 서비스 위치지정자, 또는 서비스 자체를 넘겨줄 수 잇다.

    예를 들면 이메일 서비스의 인스턴스를 넘겨줄 수도 있다. DRL안에서 당신은 이메일 서비스 타입의 global을 가질수 있고 그것에 이름을 "email" 이라고 지정해 줄 수 있을 것이다. 그런 다음 룰의 엑션부분에서 당신은 email.sendSMS(number, message) 와 같이 사용할 수 있을 것이다.

     

     

     

     

    3-4. Fnuction

    함수는 룰소스 안에서 일반적인 자바 클래스 안에서 적당하지 않은,  의미있는 코드를 넣는 방법의 하나이다. 물론 핼퍼 클래스를 작성하면 함수는 별 소용이 없을 것이다. (사실은 룰엔진에서 컴파일시에 핼퍼 클래스로 만들어진다)

    룰에서 함수를 사용하는 가장 주요 이점은, 한 장소에 모든 로직을 모아서 유지할 수 있다는 것이다. 그리고 당신은 필요에 따라 함수를 수정해서 전체 룰안에서 변경내용을 반영시킬 수 있다. 함수는 룰의 "then" 파트, 즉 실행영역에서 엑션을 실행시키기 위해 대부분 사용된다.

     

    전통적인 함수의 선언은 다음과 같을 것이다 :

    1. function String calcSomething(String arg) {
      1. return "hola !";
    2. }

     

    function에 인자가 필요없을 경우 인자를 넘겨줄 수 없다. 룰안에서 함수를 호출하기 위해서는 (아마도 consequence 또는 eval 안에서) 간단히 함수의 이름을 적어주고 파라미터를 넘겨주면 된다. method 호출과 비슷하다

     

    function 의 대안중 하나로는 핼퍼 클래스의 static 매서드가 될 수 있다. Foo.doSomething 과 같은, 또는 Global 로 선언된 핼퍼 클래스나 서비스의 인스턴스로 파라미터를 넘겨줄 수도 있다.

     

    3-5. 룰 (Rule)

    룰 구조는 명백하게 가장 중요한 구조이다. 룰은 "IF" 어떤것과 "THEN" 엑션의 폼이다. (물론 우리가 알다시피 키워드는 "when" 과 "then" 이다)

    룰은 반드시 이름을 가져야 하며, 그리고 룰 패키지 안에서 이름은 유니크해야 한다. 만약 룰이름 사이에 공백이 들어간다면 룰 이름은 " 로 감싸줘야 한다.

    속성의 경우 옵션이다.

     

     

    위에서 보는바와 같이 룰의 종료 키워드는 "end" 이며, 룰은 물론 중첩될 수 없다.

     

    3-5-1. LHS

    LHS는 보통 룰의 조건파트라고 불려진다. 자세한 사항은 아래의 다이어그램을 참조하기 바란다.

     

     

     

    패턴은 아래와 같이 설명된다.

     

     

     

    3-5-2. RHS

    RHS는 룰안에서 일반적으로 엑션파트 또는 결과파트로 불려진다. RHS 파트의 목적은 워킹메모리에서 facts 를 추가하거나 수정하거나 삭제하는데 있다. 그리고 또한 어플리케이션을 위해 명시된 엑션을 호출하기도 한다. 실제적으로는 룰이 fire 될때 실행되는 코드블럭이다.

     

    여기에 워킹메모리를 수정하기 위해서 사용가능한 몇가지의 편리한 매서드들이 있다.

    "modify(obj);" 은 룰엔진에 객체가 수정되었음을 알려주고 룰들은 변경된 객체를 기반으로 다시 판단될 것이다.

    "assert(new Something());" 은 워킹메모리 안으로 새로운 객체가 생성되었다는 것을 알려준다.

    "assertLogical(new Something());" 은 assert 와 비슷하다. 그러나 현재 firing 된 룰의 참값을 위해서 더 이상의 facts가 없을때, 자동으로 삭제될 것이다.

    "retract(obj);" 은 워킹메모리 안에서 객체를 제거한다.

     

    이 편리한 매서드들은 기본적으로 KnowledgeHelper 객체의 바로가기를 제공하기 위한 매크로이다. 더 자세한 작동을 알고 싶다면 KnowledgeHelper 클래스를 참조. KnowledgeHelper 인터페이스는 기본적으로 RHS 코드 블럭을 drools 로 불려지는 변수처럼 유용하게 만들어준다. 만약 "Property Change Listeners" 를 자바빈에서 제공한다면, 룰엔진에 해당 객체가 추가된 후에 객체가 변경이 되었을때 "modify" 를 호출해야 하는것을 피할 수 있다.

     

    3-5-3. Rule Attribute

     

     

     

    [ no-loop ] 룰의 consequence 가 facts를 수정할 때, 그것이 룰을 다시 활성화 시키는 원인이 되는가, 즉 recursion 과 같이 작동하는가 여부를 나타내는 속성으로 이 값이 true 이면 룰이 다시 활성화되지 않는다.
    default value : false
    type : Boolean

     

    [ salience ] 각각의 룰은 salience 속성을 가진다. 이것은 Integer 숫자이며, 기본값은 0 이다. Integer 값은 양수이거나 음수일수 있다. 더 높은 값을 가진 룰들이 Activation Queue 에서 정렬될때 더 높은 우선순위로 주어진다.

    default value : 0

    type : integer

     

    [agenda-group]  Agenda 그룹은 더 나은 실행제어를 제공하기 위해서 Agenda를 분할하는 것을 허용한다.  오로지 포커스가 있는 그룹안에서의 룰들만이 fire 될 수 있다.

    default value : MAIN

    type : String

     

    [auto-focus] 룰이 활성화되었을때 만약 이 속성값이 참이고 룰의 agenda-group 이 포커스가 없다면 룰을 잠재적으로  fire 하기 위해서 포커스가 주어진다.

    default value : N/A

    type : String

     

    [activation-group] 룰들이 같은 이름의 activation-group에 속해 있다면, 배타적으로 fire 될것이다. 다른 말로하면 activation-group에서 첫번째 룰이 fire 되면, 다른 룰들의 activations 은 취소될 것이다. 이것은 예전에 Xor Group 로 불려졌다. 그러나 기술적으로 정확히 Xor 라고 보기 어렵다.

    default value : N/A

    type : String

     

    [duration]  지연시간. 해당 룰이 fire 될때, 실제 엑션을 해당 속성만큼 지연된 시간후에 수행한다. 단위는 밀리세컨드이다.

    default value : no default value

    type : long

     

    약간의 속성을 사용한 룰의 사용예를 작성해 보면 다음과 같다.

    1. rule "my rule"
    2. salience 42
    3. agenda-group "number 1"
    4. when
    5. ...

     

    3.5.4 Column

     

     

     

    코드 예시:

    1. Cheese()
    2. Cheese( type == "stilton", price < 10)

     

     룰은 하나 이상의 오브젝트 타입에 따라 필드의 제약조건으로 구성된다. 내부적으로 매치된 각각의 오브젝트 타입의 인스턴스는 배열로 저장된다. 만약 Person, Person, Pet 세개의 오브젝트에 대해 매치를 수행하게 되면, 배열에 세개의 요소가 존재하게 된다. Drools안에서 Tuple 처럼 이 Facts의 리스트를 참조한다. 배열안에 각각의 요소들은 컬럼이다.

    각각의 오브젝트 타입의 인스턴스는 0 이상의 필드 제약조건을 통해 필터링된다. 컬럼항목은 오브젝트 타입상에서 제약조건의 리스트를 위해 참조된다.

    위의 코드 예시에서 첫번째 줄과 같이 어떤 제약조건도 없다면 워킹메모리의 어떤 치즈도 매치가 될것이다. 두번째 줄의 경우는 치즈 오브젝트의 인스턴스에 대해서 두개의 문자필드 제약사항을 참조한다. 제약조건들은 콤마에 의해 구분되고, 묵시적으로 "and" 를 의미한다.

     

     

     

    상기 다이어그램에 표기된 컬럼의 바인딩은 아래와 같이 코드로 표현된다.

    1. cheapStilton : Cheese( type == "stilton", price < 10 )

     

    필드 바인딩은 이전 경우와 비슷하지만, 치즈의 인스턴스에 변수를 바인딩하는 케이스이다. 이것은 cheapStilton 객체를 다른 조건에 사용할 수 있는것을 의미하거나, 아마도 룰의 실행부분에서 사용할 수도 있다. 당신은 또한 그것을 먹을수도 있겠지만, 나는 그러지 않았다.

     

    3-5-4-1. Field Constraints

    필드 제약사항은 룰엔진을 위해 워킹메모리로부터 매치되는 Fact 오브젝트들을 제약한다. 이것은 Fact 오브젝트 인스턴스로부터 "field" 값을 측정하고 비교한다. 여기서 필드는 클래스의 public, private 멤버 변수로서의 필드가 아니다. 필드는 접근가능한 매서드이다. 만약 오브젝트가 자바빈 패턴을 따른다면, 필드는 노출된 "getXXX" 또는 'isXXX" 메서드이다. 이것은 매서드가 인자가 없다는 것이고 무엇인가를 리턴한다는 의미이다. 당신은 빈이름 컨벤션을 사용해서 필드에 접근할 수 있다. (예를 들면 "getType" 은 "type" 으로 접근가능하다)

     

    예를 들면, 우리의 치즈 클래스를 참조할 때, Cheese(type == ...) 라는 것은, 치즈 인스턴스상의 getType() 메서드를 사용한다. 물론 getter 매서드가 아닌것들도 접근가능하다. toString() 같은. 이럴 경우 Cheese(toString == ..) 와 같이 매서드의 전체 이름을 통해 접근하며 괄호는 필요없다. 매서드들을 인자없이 접근한다는 것을 명심하라. 그것이 접근자(accessors) 다. 이 접근자는 룰의 효율성의 도모하기 위한 방법으로 오브젝트의 상태를 변경하지 않는다. 룰엔진은 더 빠른 결과를 위해 invoke되어 매치된 결과를 효율적으로 캐쉬한다는걸 기억하라.

     

    만약 필드를 위해 primitive 타입을 사용했다면 Drools는 그것들을 그것에 상응하는 오브젝트로 autobox 할것이다(심지어 자바 1.4 버전을 사용하더라도). 어쨋든 현재 자바 1.4에서 그것들은 더이상 auto-unboxing 되지 않는다. 전체적으로 룰안에서 사용될 모델 오브젝트 상에서 primitive 타입을 사용하지 않는 것이 아마도 최선일 것이다.

     

    만약에 당신이 Java 5 를 사용한다면 양쪽 측면에서 모두 최선이다. (컴파일러에게 autobox 를 지정할 수 있다)

    만약 당신이 Java 5 를 사용하고 당신이 JDT semantic 컴파일러를 사용한다면 (JANINO 는 아직 Java 5를 지원하지 않는다) Drools 는 당신을 명예롭게 여길것이다.

     

    3-5-4-1-1. JavaBeans as facts

    JavaBeans 노트 : 자바빈즈 컨벤션은 아래와 같다. 그러나 아래에 보여지는 것처럼 메서드 이름을 사용해서 getter 매서드들을 접근할 수 있다. 문법은 대소문자를 구분한다. 대문자를 사용한 "getURL" 같은 getter 매서드들의 경우, 속성의 이름은 "URL" 이다. "get" 이후에 하나 이상의 대문자가 있을 경우, 이렇게 하는것이 정확하게 자바빈즈 표준이다. (사실은, Instrospector 유틸이 사용된다)

     

    3.5.4.1.2 Operator (연산자)

     

     

     

    다양한 필드 제약사항과 함께 사용할 수 있는 여러개의 연산자가 있다. 올바른 연산자는 필드의 타입에 의존한다. 일반적으로 데이타의 타입에 기반해 연산자는 자체적으로 해석될 수 있다. 예를 들면, 날자 필드에 대해 "<" 연산자는 "before"를 의미한다. Matches 연산자만이 스트링 필드에 적용된다. "contains" 와 "exclude" 연산자의 경우 컬렉션 타입의 필드에만 적용된다

     

    3.5.4.1.3 literal Constaints

     

    필드의 제약사항의 가장 기본은 필드의 값이 사용자가 주어진 값을 허용하는지를 다루는 문자를 취급하는 제약사항이다.

    NULL 에 관한 노트 : 당신은 필드에 대해 아마도 NULL 인지 체크할 수 있는데, == 또는 != 를 이용한다면 "null" 키워드를 이용하라.

    1. Cheese(type == null)

     

    문자 제약사항을 다루기 위한 "=="  연산자는 성능상의 향상을 위해 해쉬를 사용해 매우 빠르게 수행된다.

     

     

    Literal Constraints (역자주 : String 의미의 문자열이 아니다)

     

    [Numeric]

    모든 자바 숫자 기본형이 지원된다. 올바른 연산자는 아래와 같다.

    ==, !=. >, <. >=. <=

    1. Cheese( quantity == 5)

     

    [Date]

    현재는 단지 "dd-mmm-yyyy" 데이타 포맷만 기본으로 지원한다. 시스템 프로퍼티("drools.dateformat) 처럼 기본 데이타 포맷 마스크를 사용해서 커스터마이징 할 수 있다. 만약 추가적인 포맷이 요구된다면, 미리 정의해서 사용하기 바라며, 올바른 연산자는 아래와 같다.

    ==, !=, >, <. >=. <=

    1. Cheese( bestBefore < "27-Oct-2007" )

     

    [String]

    어떤 유효한 자바 스트링도 가능하며, 올바른 연산자는 아래와 같다.

    ==, !=

    1. Cheese(type == "stilton")

     

    [Boolean]

    단지 true, 또는 false만 사용이 가능하다. 0 또는 1은 인식되지 않으며 Cheese( smelly )와 같은 표현도 인식되지 않는다. 가능한 연산자는 아래와 같다.

    true, false

    1. Cheese( smelly == true)

     

    [Matchs Operator]

    어떤 자바 정규표현식이 스트링 필드에 대해 매치로 사용가능하다.

    1. Cheese( type matches "(Buffulo)?\\S*Mozerella" )

     

    [Contains, Exclude Operator]

    이것은 특별한 연산자이며, 필드의 컬렉션이 오브젝트를 포함하는지, 포함하지 않는지 체크하는데 사용된다.

    1. CheeseCounter( cheeses contains "stilton" )
    2. CheeseCounter( cheeses excludes "chedder" )

     

    [Bound Variable Constraints]

     

     

     

    변수들은 Fact들, 또는 그것들의 필드들에 대해 바운드 될수 있다. 그리고 이것들은 다른 필드의 제약사항으로 사용될 수 있다. 바운드된 변수들은 Declaration 이라고 불린다.  Declarations 는 "matches" 와 같이 사용될 수 없다. "contains" 연산자와 같이 동작한다. 강요된 필드의 타입에 따라 올바른 연산자가 결정된다. 바운드 변수들은 연산자가 매우 빠른 실행과 향상된 성능을 위해 특별한 "==", "=!" 가 제공된다.

    1. Person( likes : favoriteCheese )
    2. Cheese( type == likes )

     

    likes는 바인딩된 변수, 즉 Declaration 이다. 이것은 어떤 Person 인스턴스에 매칭되는데 사용되기 위해  favorateCheese 필드에 바인딩 되어있다. 자바에서 사용가능한 어떤 유효한 변수명이라도 사용할 수 있으며, 다른곳에서도 필드이름과 차별할 수 있도록 종종 사용되는 '$' 문자를 사용할 수 있다. 아래의 예제 코드에서는 '$'를 사용하고 있고 컬럼 오브젝트 타입의 인스턴스에 바인딩이 되는 것을 보여주고 있다. 그리고 바인딩된 Fact 는 'contains' 연산자를 사용할 수 있다.

    1. $stilton : Cheese( type == "stilton" )
    2. Cheesery( cheeses contains $stilton )

     

    3.5.4.1.5 단정적인 (Predicate) 제약사항들

     

     

     

    단정적인 제약사항은 어떤 유효한(primitive boolean 으로 평가될 수 있는) 자바 표현식이건 사용할 수 있다. Declaration identifiers 처럼 Drools 의 키워드의 사용은 피해야 한다. 이전에 바운드 선언은 표현안에서 사용되었다. 단정적인 제약사항안에서 사용되는 함수는 반드시 리턴 시간이 일정한 결과로 나와야 한다. 아래의 예제는 "Return Value 연산자"를 사용해 남성보다 2살많은 남성/여성의 모든 쌍을 찾는다.

    1. Person( girlAge : age, sex = "F" )
    2. Person( boyAge : age -> (girlAge.intValue() == boyAge.intValue() + 2), sex = "M" )

     

    3.5.4.1.6 Return Value 제약사항

     

     

     

    리턴밸류 제약사항은 오브젝트를 리턴하는 어떤 유효한 자바의 표현식이라도 사용할 수 있다. 그것은 primitives 를 리턴할 수 없다.  Drools의 키워드 사용을 피하고 함수같은 경우 리턴 결과시간이 일정한 함수가 사용된다. 선언적인 제약사항 예제와 같이 모든 남성/여성 쌍을 찾는 아래의 예제는 여성보다 2살많은 남성의 쌍을 찾는다. 여기서는 boyAge 에 바인드를 사용하지 않는것을 주의해서 보기 바란다. 읽기에 좀 더 쉽게 만들었다.

     

    1. Person( girlAge : age, sex == "F" )
    2. Person( age == (new Integer(girlAge.intValue() + 2) ), sex == 'M')

     

    3.5.5 조건항 (Conditional Element)

    조건항은 하나이상의 컬럼과 작업한다. 가장 일반적인 하나는 여러개의 컬럼을 가지는 LHS안에서 묵시적으로 사용되는 "and" 이다. 'and'를 만족했을때 그것이 하나이상의 fact와 매치되어도 선언은 단지 하나의 Fact만 참조가능하다. 어떤 fact가 바인드를 위해 참조되는가?

     

    3.5.5.1. ' and'

     

     

    유효한 하위 조건 : and, or, not, exists, column

     

    1. Cheese( cheeseType : type) && Person( favouriteCheese == cheeseType)
    2. Cheese( cheeseType : type) and Person( favoriteCheese == cheeseType)

     

    3.5.5.2. 'or'

     

     

    유효한 하위 조건 : and, or, not, exists, column

     

    1. Person( sex == "f", age > 60) || Person( sex == "m", age > 65 )
    2. Person( sex == "f", age > 60) or Person( sex == "m", age > 65)

     

     

    바인딩과 같이 사용되는 or

    1. pensioner : ( Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 ) )

     

    'or' 조건항 은 sub rule 이라고 불리는 여러개의 룰을 생성하는 결과를 낳는다. 위의 예제는 내부적으로 두개의 룰을 생성한다. 이 두개의 룰은 워킹메모리 안에서 독립적으로 작동한다. 이것의 의미는 각각이 매치되어 활성화되고, fire되는 것을 의미한다. 이것에 대해 지름길은 없다.

     

    가장 좋은 방법은 OR 조건항의 경우 두개의 룰을 생성하는 간단한 방법이라고 생각하는 것이다.

     

    3.5.5.3 'eval'

    유효한 하위 : none

     

    Eval 은 어떤 의미적인 코드(primitive boolean 을 리턴하는)가 실행되는 것은 허용하기 위해서는 필수적이다. 이것은 LHS에서 바이드된 변수를 참조할 수 있다. 그리고 룰 패키지안의 함수역시 사용가능하다. eval 은 또한 룰의 LHS의 마지막 조건항이 될수도 있고, 룰안에서 여러개의 eval 을 가질수도 있다.

    일반적으로 어떤 컬럼 제약조건과 더불어 eval 을 조합해 사용할 것이다.

    eval 은 인덱스화 될 수 없기때문에 최적화된 필드 제약조건 같지는 않다. 어쨋건 필드의 제약조건에서 허용되지 않는 리턴값의 시간이 변하는 함수가 있을때 사용할 수 있는 좋은 아이디어다.  룰의 모든 다른 조건항들에 eval은 각각의 시간을 체크할 수 있다.

    Drools 2.x 대 버전과 유사한 형태로, 에전 Drools의 파라미터와 조건 태그는 접근가능한 타입으로 변수와 바인딩되는 것과 동일하다. 그리고 그것을 eval 노드안에서 사용한다.

    1. p1 : Parameter()
    2. p2 : Parameter()
    3. eval(p1.getList().containsKey(p2.getItem()) )
    4. eval(isValid(p1, p2) ) //이것은 isValid 라고 이름된 함수가 있다는걸 가정한다.

     

    3.5.5.4. 'not'

     

     

    유효한 하위요소 : Column

     

    'not'은 로직의 존재상 기호의 첫번째 순서이며, 워킹메모리안에서 어떤것의 존재성 여부를 체크한다. 현재 단지 컬럼만이 'not' 연산안에 포함될 수 있지만 다음 버전에서는 'and' 그리고 'or' 역시 허용될 것이다.

    1.  not Bus()

     

    1. not Bus(color == "red")
    2. not ( Bus(color == "red", number == 42) ) //괄호는 옵션이다

     

    3.5.5.5. 'exists'

    유효한 하위 : Column

    'exists'는 로직의 존재상 기호의 첫번째 순서이며, 워킹메모리 안에서 어떤것의 존재성을 체크한다. "exists" 를 의미론적으로 생각해보면 "적어도 하나.."라고 생각할 수 있다. 그 자신의 컬럼을 단지 가질수 있는점에서 차별화 된다. 만약 컬럼과 같이 exist를 사용한다면, 룰은 워킹메모리 안에서 얼마나 많은 데이타가 있는지 가이드가 없이 단지 조건과 매치되는 룰이 활성화 될것이다.

     

    현재 컬럼만 'exists' 연산안에 포함될 수 있지만, 다음 버전에서는 'and' 와 'or' 역시 허용될 것이다. 적어도 하나의 버스를 표현하는 코드는

    1. exists Bus()

    적어도 하나의 빨간 버스를 표현하는 코드는

    1. exists Bus(color == "red")

     

    3.5.5.6 'group'

     

     

     

    그룹핑은 대수학에서 괄호를 사용하는 것과 유사하다. 그것은 연산의 순서를 명시적으로 만드는데 사용된다.

    1. ...
    2. (
    3.     Message( status == Message.HELLO ) and Message(message != null)
    4. or Message(status == null)
    5. )
    6. ...

     

    마지막으로 룰 예제하나를 보면 아래와 같다

    1. rule "Approve if not rejected"
    2.   alience -100
    3.   genda-group "approval"
    4.   when
    5.     not Rejection()
    6.     p : Police(approved == false, policyState : status)
    7.     exists Driver(age > 25)
    8.     Process(status == policyState)
    9.   then
    10.     log("APPROVED: due to no objections.");
    11.     p.setApproved(true);
    12. end

     

    [노트]

    자바5는 primitives와 적당한 타입 사이에 autoboxing 과 unboxing 을 지원한다. 이것은 코드를 읽기 매우 쉽고 편리하게 만든다. 어쨋건 Drools의 경우 J2SE 1.4 이상에서 구동된다. 그러므로 우리는 autobox 을 가지게 되었다. 참조된 필드는 자동으로 적절한 오브젝트 타입으로 autobox 된다.

    이미 객체 타입이라면 아무런 변화도 발생하지 않는다. 어쩃건, unboxing 이 자동으로 되지 않는 사실은 중요하다. 그러므로 객체 모델에서 int 타입의 필드가 바인드 되었다면, 그것은 룰안에서 객체처럼 행동할 것이다.

     

    일반적인 룰안에서 가능하다면 필드를 객체 타입을 사용해라. (적어도 자바5가 아니라면) 또한 적어도 필드를 오브젝트 타입처럼 생각해라. 다른 특별한 사항으로는 리턴값을 위한 제약사항이 있다. 리턴값의 코드조각은 반드시 primitive 가 아니고 객체를 리턴해야 한다. 자바안에서는 모든것이 오브젝트가 아니라는 사실은 이처럼 두통을 유발한다.

     

    3.6 Query

     

     

    쿼리는 단지 룰의 LHS 구조를 포함한다. 'when'이나 'then' 을 명시하지 않는다. 그것은 워킹메모리 안에서 조건상태와 매치되는 facts를 질의하기 위한 간단한 방법이다.

     

    결과값을 받기 위해서는 WorkingMemory.getQueryResults("name") 구문을 사용하라. 여기서 "name"은 당연히 쿼리의 이름이다. 쿼리의 이름은 RuleBase에서 전역으로 사용할 수 있다. 그러므로 같은 RuleBase 를 위해 다른 패키지 상에서도 같은 이름의 쿼리를 추가하지 말아야 한다.

    쿼리 결과의 리스트는 쿼리와 매치된 객체들을 얻을수 있도록 허용한다.

     

    아래의 예제는 나이가 30 이상인 모든 사람들을 가져오는 간단한 쿼리이다.

    1. query "people over the age of 30"
    2.     person : Person( age > 30 )
    3. end

     

    'for' 루프를 이용해서 리턴된 쿼리의 결과를 반복할 수 있다. 리턴된 쿼리결과에서 각각의 행은 Tuple 안에서 각 컬럼에 접근하는데 사용될 수 있다. 이 컬럼은 인덱스 위치나 바운드된 선언명에 의해 접근 가능하다. 아래의 예제를 참고하라

    1. QueryResults results = workingMemory.getQueryResults("people over the age of 30");
    2. System.out.println("we have " + results.size() + "people over the age of 30");
    3.  
    4. System.out.println("These people are over 30:");
    5.  
    6. for (Iterator it = results.iterator; it.hasNext();) {
    7.   QueryResult result = (QueryResult) it.next();
    8.   Person person = (Person) result.get("person");
    9.   System.out.println( person.getName());
    10. }

     

    3.7 Domain Specific Language

    이전에 이미 얘기했듯이, DSL은 당신의 문제영역을 위해 룰언어를 확장될 수 있는 방법이다. 이것은 사용자를 위해 룰언어 안으로 연결되고 모든 모호한 룰언어와 룰엔진의 기능을 사용할수 있도록 돕는다.

     

    3.7.1 언제 DSL을 사용하는가?

    생략.

     

    3.7.2 수정 그리고 DSL 관리하기

    1. [when] This is {something}=Something(something=={something})

    생략

    1. [when] This is "{something}" and "{another}"=Something(something=="{something}", another=="{another}")
    2. [hwen] This is {also} valid=Another(something=="{also}")

    생략

    1. [when]There is a Person with name of "{name}"=Person(name=="{name}")
    2. [when]Person is at least {age} years old and lives in "{location}"=Person(age > {age}, location=="{location}")
    3. [then]Log "{message}"=System.out.println("{message}");
    4. [when]And = and

    생략

    1. There is a Person with name of "kitty" --> Person(name="kitty")
    2. Person is at least 42 years old and lives in "atlanta" ---> Person(age > 42, location="atlanta")
    3. Log "boo" ---> System.out.println("boo")
    4. There is a Person with name of "bob" and Person is at least 30 years old and lives in "atlanta"
    5.  
    6.     ---> Person(name="kitty") and Person(age > 30, location="atlanta")

    생략

     

    3.7.3 룰안에서 DSL  사용하기

     

    컴파일 시점에 DSL을 사용하고 룰을 실행하기 위해서는 DSL 설정소스와 룰소스를 같이 넘겨줘야 한다

    1. PackageBuilder builder = new PackageBuilder();
    2. builder.addPackageFromDrl( source, dsl);
    3. //Source is a reader for the rule source, dsl is a reader for the DSL configuration

     

    또한 룰 소스파일에는 아래처럼 특정 expander 선언이 필요하다

    1. expander your-expander.dsl

    생략

     

    조건에 제약조건을 추가할때는 아래와 같이 처리한다.

    1. [when]There is a Cheese with=Cheese()
    2. [when]- age is less than {age}=age<{age}
    3. [when]- type is '{type}'=type=='{type}'
    4. [when]- country equal to '{country}'=country=='{country}'

     

    중요한 점은 "-" (대쉬)를 반드시 위와 같이 사용해야 한다. 위와 같이 DSL이 정의되어 있으면 룰에서는 아래와 같이 제약조건을 추가할 수 있다

    1. There is a Cheese with
    2.     - age is less than 42
    3.     - type is 'stilton'

     

    3.7.5 어떻게 작동하는가?

    생략

    3.7.6 메모나 낙서로부터 DSL 만들기

    생략

     

    3.8 XML 룰 언어

    생략

    나중에 XML을 꼭 사용해야 할 때 설명할 것

     

    3.8.1 언제 XML을 사용하는가?

    생략

     

    3.8.2 XML 포맷

    XML 포맷으로 된 룰의 샘플. 확인차 봐둘 것

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <package name="com.sample"
    3. xmlns="http://drools.org/drools-3.0"
    4. xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
    5. xs:schemaLocation="http://drools.org/drools-3.0drools-3.0.xsd">
    6.  
    7. <import name="java.util.HashMap" />
    8. <import name="org.drools.*" />
    9.  
    10. <global identifier="x" type="com.sample.X"/>
    11. <global identifier="yada" type="com.sample.Yada" />
    12.  
    13. <function return-type="void" name="myFunc">
    14.     <parameter identifier="foo" type="Bar" />
    15.     <parameter identifier="bada" type="Bing" />
    16.     <body>
    17.         System.out.println("hello world");
    18.     </body>
    19. </function>
    20.  
    21. <rule name="my rule">
    22.   <rule-attribute name="salience" value="10" />
    23.   <lhs>
    24.     <column object-type="Foo" />
    25.   </lhs>
    26.   <rhs>
    27.     System.out.println("hello");
    28.   </rhs>
    29. </package>



    출처: https://enomix.tistory.com/9 [Double 'e' by rainbow]

    댓글

Designed by Tistory.