1. 개요

규칙 엔진을 사용하는 것은 상용구 코드에서 비즈니스 로직을 분리하고 비즈니스 변경으로부터 애플리케이션 코드를 보호하는 좋은 방법입니다.

Java 규칙 엔진 에 대한 이전 기사에서 JSR 94 사양에 대해 언급했습니다. Jess 규칙 엔진은 JSR 94에 대한 참조 규칙 드라이버 구현으로서 특히 중요 하므로 살펴보겠습니다.

2. 제스 규칙 엔진

Jess 는 Java와 쉽게 통합되는 최초의 규칙 엔진 중 하나입니다. Jess는 고효율 Rete 알고리즘 의 향상된 구현을 사용하여 대부분의 시나리오에서 단순한 Java 루프보다 훨씬 빠릅니다.

규칙은 기본 Jess 규칙 언어, 확장된 Lisp 기반 구문 또는 더 자세한 XML 형식으로 작성된 규칙 세트에서 실행할 수 있습니다. 기본 형식을 사용합니다.

개발을 위한 Eclipse 기반 IDE (이전 버전의 Eclipse용)와 Jess를 Java와 사용 및 통합하는 방법에 대한 훌륭한 문서 가 있습니다. 규칙 파일을 만들기 전에 아이디어를 시험해 볼 수 있는 REPL 명령줄 인터페이스도 있습니다.

JSR 94의 참조 규칙 엔진인 Jess는 정의상 JSR 94를 준수하지만 더 이상 활발하게 개발되지는 않습니다.

2.1. JSR 94에 대한 간단한 설명

JSR 94는 우리가 선택한 규칙 엔진으로부터 독립성을 부여하는 데 사용할 수 있는 API를 제공합니다. 애플리케이션에서 규칙 엔진과 상호 작용하는 방식을 변경할 필요 없이 모든 JSR 94 호환 규칙 엔진을 코드에 연결하고 일부 규칙을 실행할 수 있습니다.

이것은 규칙 엔진의 기본 규칙이 동일하게 보인다는 것을 의미하지 않습니다. 규칙 엔진을 변경하면 규칙을 다시 작성해야 할 수도 있지만 새 규칙 엔진을 사용하기 위해 응용 프로그램의 일부를 다시 작성할 필요가 없음을 의미합니다. 필요한 유일한 코드 변경은 드라이버 이름과 일부 규칙 파일 이름을 업데이트하는 것입니다.

2.2. Jess JSR 94 드라이버

JSR 94에 포함된 Jess용 참조 규칙 엔진 드라이버 가 있지만 Jess 자체는 라이선스가 부여된 상용 제품이므로 포함되지 않습니다. 참조 드라이버는 org.jcp.jsr94.jess 패키지에 포함되어 있지만 Jess를 다운로드 하면 jess.jsr94 패키지 에 최신 드라이버가 제공됩니다 .

JSR 94 계층이 이를 어떻게 변경하는지 알아보기 전에 Jess의 기본 Java 통합부터 살펴보겠습니다.

3. 제공 예시

Jess를 코드에 통합하기 전에 Jess를 다운로드하고 클래스 경로에서 사용할 수 있는지 확인하십시오. 이미 라이선스가 없으면 30일 무료 평가판 다운로드에 등록해야 합니다.

이제 Jess 를 다운로드하고 다운로드 한 Jess71p2.jar 의 압축을 풀고 예제 중 하나를 실행하여 작동하는 버전이 있는지 확인합니다.

3.1. 독립형 제스

jess 디렉토리에 몇 가지 예제 규칙 세트가 있는 Jess71p2/examples 디렉토리를 살펴보겠습니다 . price_engine 디렉토리 는 ant build.xml 스크립트 를 통해 실행할 수 있는 통합을 보여줍니다 . 디렉터리를 가격 책정 엔진 예제로 변경하고 ant test 를 통해 프로그램을 실행해 보겠습니다 .

cd Jess71p2/examples/pricing_engine
ant test

이것은 예제 가격 책정 규칙 세트를 빌드하고 실행합니다.

Buildfile: Jess71p2\examples\pricing_engine\build.xml
...
test:
[java] Items for order 123:
[java] 1 CD Writer: 199.99
...
[java] Items for order 666:
[java] 1 Incredibles DVD: 29.99
[java] Offers for order 666:
[java] BUILD SUCCESSFUL
Total time: 1 second

3.2. Jess와 JSR 94

이제 Jess가 작동 하므로 JSR 94 를 다운로드한 다음 압축을 풀고 내부에 ant, doc, lib 및 src 디렉토리가 있는 jsr94-1.0 디렉토리를 생성합니다.

unzip jreng-1_0a-fr-spec-api.zip

이렇게 하면 JSR 94 API 및 Jess 참조 드라이버가 제공되지만 허가된 Jess 구현과 함께 제공되지 않으므로 지금 예제를 실행하려고 하면 다음 오류가 발생합니다.

Error: The reference implementation Jess could not be found.

따라서 이전에 다운로드한 Jess71p2의 일부로 제공된 Jess 참조 구현인 jess.jar 를 추가하고 JSR 94 lib 디렉토리에 복사한 다음 예제를 실행해 보겠습니다.

cp Jess71p2/lib/jess.jar jsr94-1.0/lib/
java -jar jsr94-1.0/lib/jsr94-example.jar

이 예에서는 송장이 지불될 때 고객의 남은 신용을 결정하기 위해 몇 가지 규칙을 실행합니다.

Administration API Acquired RuleAdministrator: org.jcp.jsr94.jess.RuleAdministratorImpl@63947c6b
...
Runtime API Acquired RuleRuntime: org.jcp.jsr94.jess.RuleRuntimeImpl@68fb2c38
Customer credit limit result: 3000
...
Invoice 2 amount: 1750 status: paid
Released Stateful Rule Session.

4. Jess와 Java 통합

이제 Jess와 JSR 94를 다운로드하고 기본적으로 JSR을 통해 몇 가지 규칙을 실행했으므로 Jess 규칙 세트를 Java 프로그램에 통합하는 방법을 살펴보겠습니다.

이 예제에서는 Java 코드에서  간단한 Jess 규칙 파일인 hellojess.clp 를 실행한 다음 일부 개체를 사용하고 수정하는 또 다른 규칙 파일인 bonus.clp 를 살펴봅니다.

4.1. 메이븐 의존성

Jess에 사용할 수 있는 Maven 의존성이 없으므로 아직 수행하지 않은 경우 Jess jar( jess.jar )를 다운로드하여 압축을 풀고 로컬 Maven 저장소에 mvn 설치 합니다.

mvn install:install-file -Dfile=jess.jar -DgroupId=gov.sandia -DartifactId=jess -Dversion=7.1p2 -Dpackaging=jar -DgeneratePom=true

그런 다음 일반적인 방법으로 의존성으로 추가할 수 있습니다.

<dependency>
    <groupId>gov.sandia</groupId>
    <artifactId>jess</artifactId>
    <version>7.1p2</version>
</dependency>

4.2. Hello Jess 규칙 파일

다음으로 메시지를 출력하기 위한 가장 간단한 규칙 파일을 생성해 보겠습니다. 규칙 파일을 hellojess.clp 로 저장합니다 .

(printout t "Hello from Jess!" crlf)

4.3. Jess 규칙 엔진

이제 Jess Rete 규칙 엔진 의 인스턴스를 만들고 초기 상태로 reset() 하고 hellojess.clp 에 규칙을 로드한 다음 실행해 보겠습니다.

public class HelloJess {
    public static void main(String[] args) throws JessException {
    Rete engine = new Rete();
    engine.reset();
    engine.batch("hellojess.clp");
    engine.run();
}

이 간단한 예를 위해 잠재적인 JessException기본 메서드의 throws 절에 추가했습니다.

프로그램을 실행하면 다음과 같은 결과가 표시됩니다.

Hello from Jess!

5. Jess와 Java를 데이터와 통합

이제 모든 것이 올바르게 설치되었고 규칙을 실행할 수 있으므로 규칙 엔진이 처리할 데이터를 추가하는 방법과 결과를 검색하는 방법을 살펴보겠습니다 .

먼저 작업할 일부 Java 클래스와 이를 사용하는 새 규칙 세트가 필요합니다.

5.1. 모델

몇 가지 간단한 질문답변 클래스 를 만들어 보겠습니다 .

public class Question {
    private String question;
    private int balance;
    // getters and setters

    public Question(String question, int balance) {
        this.question = question;
        this.balance = balance;
    }
}

public class Answer {
    private String answer;
    private int newBalance;
    // getters and setters

    public Answer(String answer, int newBalance) {
        this.answer = answer;
        this.newBalance = newBalance;
    }
}

5.2. 입력 및 출력이 있는 Jess 규칙

이제 질문 을 전달하고 답변 을 받을 간단한 Jess 규칙 집합인 bonus.clp 를 만들어 보겠습니다 .

먼저 질문답변 클래스를 가져온 다음 Jess의 deftemplate 함수 사용하여 규칙 엔진에서 사용할 수 있도록 합니다.

(import com.baeldung.rules.jsr94.jess.model.*)
(deftemplate Question     (declare (from-class Question)))
(deftemplate Answer       (declare (from-class Answer)))

Jess 함수 호출을 나타내는 괄호 사용에 유의하십시오.

이제 defrule 을 사용하여 질문 의 잔액 이 0 미만일 경우 $50의 보너스를 제공하는 Jess의 확장 Lisp 형식에 초과 인출 방지 라는 단일 규칙을 추가해 보겠습니다.

(defrule avoid-overdraft "Give $50 to anyone overdrawn"
    ?q <- (Question { balance < 0 })
    =>
    (add (new Answer "Overdrawn bonus" (+ ?q.balance 50))))

여기서 " ?" " <-" 의 오른쪽에 있는 조건이  일치 할 때  개체를 변수 q 에 바인딩합니다. 이 경우 규칙 엔진은 잔액 이 0보다 작은 질문 을 찾습니다.

그럴 때 " =>" 오른쪽에 있는 작업  이 트리거되어 엔진 새 Answer 개체를 작업 메모리에 추가 합니다. 두 개의 필수 생성자 인수를 제공합니다. 응답 매개변수 에 대한 "과도한 보너스" 와 newAmount 매개변수 를 계산하기 위한 (+) 함수입니다.

5.3. Jess 규칙 엔진으로 데이터 조작

add() 를 사용 하여 규칙 엔진의 작업 메모리에 한 번에 하나의 개체를 추가하거나 addAll() 을 사용하여 데이터 컬렉션을 추가할 수 있습니다. add() 를 사용하여 단일 질문을 추가해 보겠습니다 .

Question question = new Question("Can I have a bonus?", -5);
engine.add(data);

모든 데이터가 제자리에 있으면 규칙을 실행해 보겠습니다.

engine.run();

Jess Rete 엔진은 마법을 부리고 모든 관련 규칙이 실행되면 돌아옵니다. 우리의 경우 검사할 답변 이 있습니다.

jess.Filter 를 사용 하여 규칙 엔진에서 Iterable 결과 객체 로 Answer 를 추출해  보겠습니다 .

Iterator results = engine.getObjects(new jess.Filter.ByClass(Answer.class));
while (results.hasNext()) {
    Answer answer = (Answer) results.next();
    // process our Answer
}

간단한 예제에는 참조 데이터가 없지만 데이터를 추가한 후 WorkingMemoryMarkerengine.mark() 를 사용 하여 규칙 엔진의 작업 메모리 상태를 표시할 수 있습니다. 그런 다음 engine 을 호출할 수 있습니다 . 작업 메모리를 "로드된" 상태로 재설정하고 다른 개체 집합에 대해 규칙 엔진을 효율적으로 재사용하기 위한 마커와 함께 resetToMark :

WorkingMemoryMarker marker;
// load reference data
marker = engine.mark();
// load specific data and run rules
engine.resetToMark(marker);

이제 JSR 94를 사용하여 이 동일한 규칙 세트를 실행하는 방법을 살펴보겠습니다.

6. JSR 94를 사용하여 Jess 규칙 엔진 통합

JSR 94는 코드가 규칙 엔진과 상호 작용하는 방식을 표준화합니다. 이렇게 하면 더 나은 대안이 나올 경우 응용 프로그램을 크게 변경하지 않고도 규칙 엔진을 쉽게 변경할 수 있습니다.

JSR 94 API는 두 가지 기본 패키지로 제공됩니다.

  • javax.rules.admin – 드라이버 및 규칙 로드용
  • javax.rules – 규칙 실행 및 결과 추출

이 두 가지 모두에서 클래스를 사용하는 방법을 살펴보겠습니다.

6.1. 메이븐 의존성

먼저 jsr94 에 대한 Maven 의존성을 추가해 보겠습니다 .

<dependency>
    <groupId>jsr94</groupId>
    <artifactId>jsr94</artifactId>
    <version>1.1</version>
</dependency>

6.2. 관리 API

JSR 94 사용을 시작하려면 RuleServiceProvider 를 인스턴스화해야 합니다 . 하나를 만들어서 Jess 규칙 드라이버에 전달해 보겠습니다.

String RULE_SERVICE_PROVIDER="jess.jsr94";
Class.forName(RULE_SERVICE_PROVIDER + ".RuleServiceProviderImpl");
RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);

이제 Jess의 JSR 94 RuleAdministrator 를 가져오고 예제 규칙 세트를 JSR 94 RuleExecutionSet에 로드 하고 선택한 URI로 실행을 위해 등록합니다.

RuleAdministrator ruleAdministrator = serviceProvider.getRuleAdministrator();

InputStream ruleInput = JessRunner.class.getResourceAsStream(rulesFile);
HashMap vendorProperties = new HashMap();

RuleExecutionSet ruleExecutionSet = ruleAdministrator
  .getLocalRuleExecutionSetProvider(vendorProperties)
  .createRuleExecutionSet(ruleInput, vendorProperties);

String rulesURI = "rules://com/baeldung/rules/bonus";
ruleAdministrator.registerRuleExecutionSet(rulesURI, ruleExecutionSet, vendorProperties);

Jess 드라이버에는 우리가 RuleAdministrator 에 제공한 vendorProperties 맵 이 필요하지 않지만 인터페이스의 일부이며 다른 공급업체에서 필요할 수 있습니다.

이제 규칙 엔진 Provider인 Jess가 초기화되고 규칙 세트가 등록되었으므로 규칙을 실행할 준비가 거의 다 되었습니다.

그것들을 실행하기 전에 그것들을 실행할 런타임 인스턴스와 세션이 필요합니다. 마법 같은 일이 일어날 위치에 대해 calculateResults() 자리 표시자를 추가하고 세션을 해제합니다.

RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();
StatelessRuleSession statelessRuleSession
  = (StatelessRuleSession) ruleRuntime.createRuleSession(rulesURI, new HashMap(), RuleRuntime.STATELESS_SESSION_TYPE);
calculateResults(statelessRuleSession);
statelessRuleSession.release();

6.3. 실행 API

이제 모든 것이 준비되었으므로 초기 데이터를 제공하고 상태 비저장 세션에서 규칙을 실행하고 결과를 추출하기 위해 calculateResults 를 구현해 보겠습니다.

List data = new ArrayList();
data.add(new Question("Can I have a bonus?", -5));
List results = statelessRuleSession.executeRules(data);

JSR 94는 JDK 5가 출시되기 전에 작성되었으므로 API는 제네릭을 사용하지 않으므로 Iterator 를 사용하여 결과를 확인하겠습니다.

Iterator itr = results.iterator();
while (itr.hasNext()) {
    Object obj = itr.next();
    if (obj instanceof Answer) {
        int answerBalance = ((Answer) obj).getCalculatedBalance());
    }
}

예제에서는 상태 비저장 세션을 사용했지만 호출 간에 상태를 유지하려는 경우 StatefuleRuleSession 을 생성할 수도 있습니다 .

7. 결론

이 기사에서는 Jess의 기본 클래스를 사용하고 JSR 94를 사용하여 Jess 규칙 엔진을 애플리케이션에 통합하는 방법을 배웠습니다. 비즈니스 규칙을 별도의 파일로 분리하여 처리하는 방법을 살펴보았습니다. 애플리케이션이 실행될 때 규칙 엔진에 의해.

다른 JSR 94 호환 규칙 엔진용으로 작성된 동일한 비즈니스 논리에 대한 규칙이 있는 경우 대체 규칙 엔진용 드라이버를 추가하고 응용 프로그램에서 사용해야 하는 드라이버 이름을 업데이트할 수 있으며 추가 코드 변경은 없어야 합니다. 필요한.

Embedding Jess in a Java Application에 대한 자세한 내용은 jess.sandia.gov에 있으며 Oracle은 Getting Started With the Java Rule Engine API(JSR 94) 에 대한 유용한 사용방법(예제)를 제공합니다 .

늘 그렇듯이 이 기사에서 살펴본 코드는 GitHub 에서 사용할 수 있습니다 .

Generic footer banner