1. Обзор
Использование механизма правил-отличный способ отделить бизнес-логику от нашего шаблонного кода и защитить код приложения от бизнес-изменений.
В предыдущей статье о Движках правил Java мы упоминали спецификацию JSR 94. Механизм правил Jess имеет особое значение | как реализация драйвера эталонных правил для JSR 94 , поэтому давайте взглянем на него.
2. Механизм правил Джесс
Jess – один из самых ранних движков правил, который легко интегрируется с Java. Джесс использует улучшенную реализацию высокоэффективного алгоритма Rete , что делает его намного быстрее, чем простой цикл Java для большинства сценариев.
Правила могут выполняться из наборов правил, написанных на родном языке правил Jess, расширенном синтаксисе на основе Lisp, или из более подробного формата 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 .
Давайте начнем с рассмотрения собственной интеграции Java Джесса, прежде чем мы перейдем к тому, как уровень JSR 94 меняет это.
3. Приведенные Примеры
Прежде чем мы начнем интегрировать Jess в наш код, давайте убедимся, что мы загрузили его и сделали доступным в нашем пути к классам. Нам нужно будет зарегистрироваться для бесплатной 30-дневной пробной загрузки, если у нас уже нет лицензии.
Итак, давайте скачаем Джесс , распакуем скачанное Jess71p2.jar , и запустите один из его примеров, чтобы убедиться, что у нас есть рабочая версия.
3.1. Автономная Джесс
Давайте заглянем в каталог Jess71p2/examples , где в каталоге jesus содержатся некоторые примеры наборов правил. Каталог pricing_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
Теперь, когда у нас есть Иисус, давайте загрузим JSR 94 , а затем распакуем его, чтобы создать каталог jsr 94-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.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: [email protected] ... Runtime API Acquired RuleRuntime: [email protected] 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, hello jess.clip, из кода Java, а затем рассмотрим другой файл правил, bonus.clp , который будет использовать и изменять некоторые из наших объектов.
4.1. Зависимость Maven
Для Джесса нет зависимости Maven, поэтому, если мы еще этого не сделали, давайте загрузим и распакуем Jess jar ( jess.jar ) и mvn установите его в наш локальный репозиторий Maven:
mvn install:install-file -Dfile=jess.jar -DgroupId=gov.sandia -DartifactId=jess -Dversion=7.1p2 -Dpackaging=jar -DgeneratePom=true
Затем мы можем добавить его в качестве зависимости обычным способом:
gov.sandia jess 7.1p2
4.2. Файл правил Hello Jess
Затем давайте создадим простейшие файлы правил для распечатки сообщения. Мы сохраним файл правил как hello jess.clp :
(printout t "Hello from Jess!" crlf)
4.3. Механизм правил Джесса
Теперь давайте создадим экземпляр механизма правил Jess Rete , |/reset() его в исходное состояние, загрузим правила в hello jess.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 в предложение main метода 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 под названием bonus.clip , в который мы передадим Вопрос и получим Ответ от.
Сначала мы импортируем наши Вопросы и Ответы классы, а затем используем функцию deftemplate Джесса, чтобы сделать их доступными для механизма правил:
(import com.baeldung.rules.jsr94.jess.model.*) (deftemplate Question (declare (from-class Question))) (deftemplate Answer (declare (from-class Answer)))
Обратите внимание на использование круглых скобок, которые обозначают вызовы функций Jess.
Теперь давайте используем defrule для добавления одного правила avoid-overdraft в расширенном формате Lisp Джесса, который дает нам бонус в размере 50 долларов, если баланс в нашем Вопросе ниже нуля:
(defrule avoid-overdraft "Give $50 to anyone overdrawn" ?q <- (Question { balance < 0 }) => (add (new Answer "Overdrawn bonus" (+ ?q.balance 50))))
Здесь, в ” ?” привязывает объект к переменной q , когда условия в правой части ” <-“ совпадают. В этом случае именно тогда механизм правил находит Вопрос , который имеет баланс меньше 0.
Когда это происходит, то действия справа от ” =>” запускаются, поэтому движок добавляет s новый ответ объект в рабочую память. Мы даем ему два обязательных аргумента конструктора: “Перерасход бонуса” для параметра 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 сработает как по волшебству и вернется, когда все соответствующие правила будут выполнены. В нашем случае у нас будет Ответ для проверки.
Давайте использовать Джесс.Фильтр для извлечения нашего Ответа из механизма правил в итерируемый объект результатов :
Iterator results = engine.getObjects(new jess.Filter.ByClass(Answer.class)); while (results.hasNext()) { Answer answer = (Answer) results.next(); // process our Answer }
В нашем простом примере у нас нет никаких справочных данных, но когда мы это сделаем, мы можем использовать WorkingMemoryMarker и engine.mark () , чтобы отметить состояние рабочей памяти механизма правил после добавления данных. Тогда мы можем вызвать двигатель . 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 для jsr 94 :
jsr94 jsr94 1.1
6.2. API администрирования
Чтобы начать использовать JSR 94, нам нужно создать экземпляр RuleServiceProvider . Давайте создадим его, передав ему наш драйвер правил Джесс:
String RULE_SERVICE_PROVIDER="jess.jsr94"; Class.forName(RULE_SERVICE_PROVIDER + ".RuleServiceProviderImpl"); RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);
Теперь давайте получим JSR 94 Администратор правил Джесса, загрузим наш пример набора правил в 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);
Драйвер Jesse не нуждается в свойствах поставщика карте , которую мы предоставили Администратору правил , но это часть интерфейса, и она может потребоваться другим поставщикам.
Теперь, когда наш ruleengineprovider, Джесс, был инициализирован и наш набор правил зарегистрирован, мы почти готовы запустить наши правила.
Прежде чем мы сможем их запустить, нам нужен экземпляр среды выполнения и сеанс для их запуска. Давайте также добавим заполнитель, calculate Results(), для того, где произойдет волшебство, и освободим сеанс:
RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime(); StatelessRuleSession statelessRuleSession = (StatelessRuleSession) ruleRuntime.createRuleSession(rulesURI, new HashMap(), RuleRuntime.STATELESS_SESSION_TYPE); calculateResults(statelessRuleSession); statelessRuleSession.release();
6.3. API выполнения
Теперь, когда у нас все готово, давайте реализуем вычисление результатов для предоставления наших исходных данных, выполнения наших правил в сеансе без сохранения состояния и извлечения результатов:
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 itr = results.iterator(); while (itr.hasNext()) { Object obj = itr.next(); if (obj instanceof Answer) { int answerBalance = ((Answer) obj).getCalculatedBalance()); } }
В нашем примере мы использовали сеанс без состояния, но мы также можем создать сеанс правила С сохранением состояния , если мы хотим поддерживать состояние между вызовами.
7. Заключение
В этой статье мы узнали, как интегрировать механизм правил Jess в наше приложение, используя собственные классы Jess и, приложив немного больше усилий, используя JSR 94. Мы видели, как бизнес-правила могут быть разделены на отдельные файлы, которые обрабатываются механизмом правил при запуске нашего приложения.
Если у нас есть правила для той же бизнес-логики, написанные для другого механизма правил, совместимого с JSR 94, то мы можем просто добавить драйвер для нашего альтернативного механизма правил и обновить имя драйвера, которое должно использовать наше приложение, и никаких дальнейших изменений кода не потребуется.
Более подробная информация по адресу jess.sandia.gov для Встраивания Jess в Java-приложение , и у Oracle есть полезное руководство для Начала работы с API Java Rule Engine (JSR 94) .
Как обычно, код, который мы рассмотрели в этой статье, доступен на GitHub .