Перейти к основному содержимому

Jess Rule Engine и JSR 94

· 10 мин. чтения

1. Обзор

Использование механизма правил — отличный способ отделить бизнес-логику от стандартного кода и защитить код приложения от бизнес-изменений.

В предыдущей статье о Java Rule Engines мы упоминали спецификацию JSR 94. Jess Rule Engine имеет особое значение как реализация эталонного драйвера правил для JSR 94 , поэтому давайте взглянем на него.

2. Двигатель правил Джесса

Jess — один из первых механизмов правил, легко интегрируемых с Java. Jess использует расширенную реализацию высокоэффективного алгоритма Rete , что делает его намного быстрее, чем простой цикл Java для большинства сценариев.

Правила могут выполняться из наборов правил, написанных на собственном языке правил Jess, расширенном синтаксисе на основе Лиспа или из более подробного формата XML. Мы будем использовать родной формат.

Существует IDE на основе Eclipse для разработки (для более старых версий 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.jsr94 при загрузке Jess .

Давайте начнем с изучения встроенной в Jess интеграции с Java, а затем перейдем к рассмотрению того, как уровень JSR 94 меняет ее.

3. Приведенные примеры

Прежде чем мы начнем интегрировать Jess в наш код, давайте удостоверимся, что мы загрузили его и сделали доступным в нашем пути к классам. Нам нужно будет зарегистрироваться для бесплатной 30-дневной пробной загрузки, если у нас еще нет лицензии.

Итак, давайте загрузим Jess , распакуем загруженный Jess71p2.jar и запустим один из его примеров, чтобы убедиться, что у нас есть рабочая версия.

3.1. Автономная Джесс

Давайте заглянем в каталог Jess71p2/examples , где каталог jess содержит несколько примеров наборов правил. В каталоге 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. Джесс с JSR 94

Теперь, когда у нас работает Jess, давайте загрузим JSR 94 , а затем разархивируем его, чтобы создать каталог jsr94-1.0 с каталогами ant, doc, lib и src внутри.

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

Это дает нам API JSR 94 и эталонный драйвер Jess, но не поставляется с лицензированной реализацией Jess, поэтому, если мы попытаемся запустить пример сейчас, мы получим следующую ошибку:

Error: The reference implementation Jess could not be found.

Итак, давайте добавим эталонную реализацию Jess, jess.jar , которая была частью скачанного ранее Jess71p2, и скопируем ее в каталог lib JSR 94, а затем запустим пример:

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.

В нашем примере мы начнем с выполнения простого файла правил Jess, hellojess.clp, из кода Java, а затем посмотрим на другой файл правил, bonus.clp , который будет использовать и изменять некоторые из наших объектов.

4.1. Зависимость от Maven

Для Jess нет доступных зависимостей Maven, поэтому, если мы еще этого не сделали, давайте загрузим и распакуем jar Jess ( jess.jar ) и mvn установим его в наш локальный репозиторий Maven:

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 Rete , сбросим его в исходное состояние, загрузим правила в 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;
<i><span style="font-weight: 400">// getters and setters</span></i>

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

public class Answer {
private String answer;
private int newBalance;
<i><span style="font-weight: 400">// getters and setters</span></i>

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

5.2. Правило Джесса с вводом и выводом

Теперь давайте создадим простой набор правил Jess с именем Bonus.clp , в который мы будем передавать вопрос и получать от него ответ .

Сначала мы импортируем наши классы вопросов и ответов , а затем используем функцию Jess deftemplate , чтобы сделать их доступными для механизма правил:

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

Обратите внимание на использование круглых скобок, обозначающих вызовы функций Jess.

Теперь давайте воспользуемся defrule , чтобы добавить единственное правило избежать овердрафта в расширенном формате Лиспа Джесса, которое дает нам бонус в размере 50 долларов, если баланс в нашем вопросе ниже нуля:

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

Здесь « привязывает объект к переменной q , когда условия справа от « <-» совпадают. В этом случае механизм правил находит Вопрос , баланс которого меньше 0.

Когда это происходит, запускаются действия справа от « =>» , поэтому движок добавляет новый объект ответа в рабочую память. Мы даем ему два обязательных аргумента конструктора: «Бонус за перерасход» для параметра ответа и функцию (+) для вычисления параметра newAmount .

5.3. Манипулирование данными с помощью механизма правил Джесса

Мы можем использовать add() для добавления одного объекта в рабочую память нашего механизма правил или addAll() для добавления набора данных. Давайте используем add() , чтобы добавить один вопрос:

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

Имея все наши данные, давайте выполним наши правила:

engine.run();

Движок Jess Rete сработает и вернется, когда все соответствующие правила будут выполнены. В нашем случае у нас будет ответ для проверки.

Давайте используем jess.Filter , чтобы извлечь наш ответ из механизма правил в объект результатов Iterable :

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

В нашем простом примере у нас нет справочных данных, но когда они есть, мы можем использовать WorkingMemoryMarker и engine.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 стандартизирует взаимодействие нашего кода с обработчиком правил. Это упрощает изменение нашего механизма правил без существенного изменения нашего приложения, если появится лучшая альтернатива.

API JSR 94 поставляется в двух основных пакетах:

  • javax.rules.admin — для загрузки драйверов и правил
  • javax.rules — для запуска правил и извлечения результатов

Мы рассмотрим, как использовать классы в обоих из них.

6.1. Зависимость от Maven

Во-первых, давайте добавим зависимость Maven для jsr94 :

<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);

Теперь давайте получим JSR 94 RuleAdministrator от Jess , загрузим наш пример набора правил в 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/foreach/rules/bonus";
ruleAdministrator.registerRuleExecutionSet(rulesURI, ruleExecutionSet, vendorProperties);

Драйверу Jess не нужна карта vendorProperties , которую мы предоставили RuleAdministrator , но она является частью интерфейса и может потребоваться другим поставщикам.

Теперь, когда наш провайдер механизма правил, 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 в наше приложение, используя собственные классы Jess и, приложив немного больше усилий, с помощью JSR 94. Мы увидели, как бизнес-правила могут быть разделены на отдельные файлы, которые обрабатываются. механизмом правил при запуске нашего приложения.

Если у нас есть правила для той же бизнес-логики, написанные для другого механизма правил, совместимого с JSR 94, то мы можем просто добавить драйвер для нашего альтернативного механизма правил и обновить имя драйвера, которое должно использовать наше приложение, и никаких дальнейших изменений кода не должно быть. необходимый.

Дополнительные сведения о встраивании Jess в приложение Java можно найти на сайте jess.sandia.gov , а у Oracle есть полезное руководство по началу работы с Java Rule Engine API (JSR 94) .

Как обычно, код, который мы рассмотрели в этой статье, доступен на GitHub .