Статьи

Слюни каноническая модель — правила чистого Java

Механизмы правил, такие как Drools, обычно используют собственный язык для определения набора правил. Например, компилятор Drools преобразует файл drl во внутреннее представление (KiePackages), которое впоследствии используется для создания сети ReteOO / Phreak, которая будет выполнять оценку правил.

Это внутреннее представление никогда не предназначалось для создания или использования конечными пользователями. Это усложнение затрудняет написание правил программно, и вместо этого предлагается создавать текстовые правила на уровне DRL. Это означает, что сам drl в настоящее время является единственной практической формальной нотацией, определяющей набор правил в Drools.

Внутренние слюни были разработаны с несколькими предположениями в то время, которые больше не соответствуют действительности или не желательны. До Java 8 perm gen был проблемой, поэтому для решения этой проблемы использовались различные решения, такие как MVEL для оценки на основе рефлексии. Java 8 теперь помещает код в кучу, так что в этом больше нет необходимости. На уровне двигателя он также проверял и создавал индексы, которые привязывали его к выражениям, созданным DRL — это делает полиглот непрактичным. Наконец, он широко использует загрузчики классов и рефлексию, что затрудняет транспортировку для выполнения в различных средах.

Модель правил, независимых от двигателя

Чтобы преодолеть это ограничение и предложить возможность программного определения набора правил в чистой Java, мы разработали модель, предназначенную для обеспечения канонического представления набора правил и
свободно DSL для удобного создания экземпляра этой модели. Сама модель полностью независима от Drools и теоретически может быть использована другими двигателями. Он также вводит слои, которые полностью отделяют движок от необходимости знать какой-либо язык. Например, он не будет проверять и генерировать индексы, вместо этого он ожидает, что эти индексы будут предоставлены из вышеперечисленных слоев. Еще одно преимущество означает, что теперь Drools имеет дружественное отношение к разработчикам, поскольку все это просто низкоуровневые правила pojo.

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

Давайте посмотрим на практический пример модели правила, определенной с использованием ранее упомянутой DSL:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
Rule rule = rule( "Persons older than Mark" )
    .view(
        expr("exprA", markV, p -> p.getName().equals("Mark"))
            .indexedBy( String.class, ConstraintType.EQUAL, Person::getName, "Mark" )
            .reactOn( "name", "age" ),
        expr("exprB", olderV, p -> !p.getName().equals("Mark"))
            .indexedBy( String.class, ConstraintType.NOT_EQUAL, Person::getName, "Mark" )
            .reactOn( "name" ),
        expr("exprC", olderV, markV, (p1, p2) -> p1.getAge() > p2.getAge())
            .indexedBy( int.class, ConstraintType.GREATER_THAN, Person::getAge, Person::getAge )
            .reactOn( "age" )
    )
    .then(on(olderV, markV)
         .execute((p1, p2) -> System.out.println( p1.getName() + " is older than " + p2.getName())));

Это эквивалент следующего правила, выраженного в drl:

1
2
3
4
5
6
rule "Persons older than Mark" when
    $p1 : Person(name == \"Mark\")
    $p2 : Person(name != \"Mark\", age > $p1.age)
then
    System.out.println($p2.getName() + \" is older than \" + $p1.getName());
end

Очевидно, что версия DSL гораздо более многословна и требует указания гораздо большего количества деталей, которые, наоборот, выводятся компилятором drools, когда он анализирует правило, написанное в drl.

Как и предполагалось, это было сделано специально, потому что модель должна явно содержать всю информацию, необходимую для создания раздела сети ReteOO / Phreak, предназначенного для оценки этого правила, без необходимости какой-либо проверки байт-кода или использования каких-либо других сложных, хрупких и непереносимая методика самоанализа. В этих простых примерах индекс содержит ту же логику, что и выражение, поскольку выражения DRL могут содержать не только индекс. Индекс будет выведен и неявно добавлен верхними уровнями языка.

Чтобы прояснить этот аспект, давайте немного подробнее проанализируем одно ограничение LHS:

1
2
3
4
5
expr("exprA",                                                                 [1]
     markV,                                                                   [2]
     p -> p.getName().equals("Mark") )                                        [3
    .indexedBy( String.class, ConstraintType.EQUAL, Person::getName, "Mark" ) [4]
    .reactOn( "name", "age" )                                                 [5]

В этом утверждении вы можете заметить следующие части:

[1] Это метка для ограничения, используемого для определения его идентичности. Два идентичных ограничения должны иметь одинаковую метку, а две разные должны иметь разные метки, чтобы механизм мог правильно реализовать совместное использование узла, где это возможно. Эта метка может быть опционально опущена, и в этом случае она будет сгенерирована из автоматического самоанализа байт-кода лямбда-выражения, реализующего ограничение. Однако, как и ожидалось, предпочтительно избегать любого механизма самоанализа, а затем явно указывать метку ограничения, когда это возможно.

[2] Это переменная, определенная до создания правила и используемая для связывания фактического факта с формальным параметром лямбда-выражения, используемым для оценки условия в нем. Это эквивалент объявления переменной в правиле, написанном с помощью нотации drl.

[3] Лямбда-выражение, выполняющее оценку ограничения.

[4] Спецификация типа этого ограничения и как оно должно быть проиндексировано механизмом.

[5] Имя свойств объекта Person, для которого это ограничение должно реагировать.

Построение и выполнение модели

Вы можете программно добавить это и другие правила вашей базы правил в модель:

1
Model model = new ModelImpl().addRule( rule );

Если у вас есть эта модель, которая снова полностью независима от алгоритмов и структур данных Drools, вы можете использовать второй проект , зависящий от времени и от Drools, и от самой модели, чтобы создать из нее KieBase.

1
KieBase kieBase = KieBaseBuilder.createKieBaseFromModel( model );

Внутри этого компоновщика создается воссоздание KiePackages из модели, а затем их сборка с использованием существующего компилятора drools. Полученный KieBase идентичен тому, который вы можете получить, скомпилировав соответствующий drl, и затем вы можете использовать его точно таким же образом, создав из него KieSession и наполнив его фактами вашего домена.
Здесь вы можете найти больше тестовых примеров, показывающих функции Drools, которые в настоящее время поддерживаются моделью, и конструкции DSL, позволяющие их использовать, а здесь вы можете найти более полный пример.

Встраивание исполняемой модели в кьяр

На данный момент kjar содержит несколько предварительно сгенерированных классов, реализующих ограничения и последствия. Однако все файлы drl необходимо анализировать и компилировать с нуля, что позволяет сэкономить на сборке только из исходного кода. Исполняемая модель также будет полезна для ускорения этого процесса. Внедрение классов, реализующих модель базы правил внутри kjar, позволяет быстро воссоздать KiePackages вместо того, чтобы перезапускать весь процесс компиляции из файлов drl. Доступно доказательство того, как это может работать
здесь

DRL компилятор для исполняемой модели

Мы работаем над новым компилятором, который будет принимать существующий DRL и напрямую выводить исполняемую модель, которая хранится в kjar. Это приведет к гораздо более быстрому времени загрузки и сборки. Мы также ищем другие способы ускорить этот процесс, предварительно кэшируя дополнительную информацию в kjar, которую можно определить во время начальной сборки kjar.

Правила Pojo, Polyglot и DRLX

Исполняемая модель является низкоуровневой, и поскольку вам необходимо предоставить всю необходимую информацию, это делает ее немного многословной. Это сделано специально для поддержки инноваций на уровне языка. Хотя это технически все еще правила pojo, это нежелательно для повседневного использования. В ближайшем будущем мы будем работать над менее подробным слоем правил pojo, который будет использовать процессор аннотаций для внедрения таких вещей, как индексы и ReactionOn. Мы также надеемся, что это приведет к нескольким языкам правил, а не только к одному — правилам scala, правилам закрытия, правилам mini-dsl и т. Д. Также не обязательно, чтобы язык раскрыл все возможности движка, люди могут писать мини-dsl, разоблачающие подмножество.

Наконец, мы также работаем над языком DRL следующего поколения (DRLX), который будет надмножеством Java. Это все еще находится в стадии разработки и не будет доступно в течение некоторого времени, но мы опубликуем некоторые предложения для этого, как только будет подготовлен предварительный проект спецификации.

Ссылка: Каноническая модель Drools — правила чистого Java от нашего партнера JCG Джеффри Де Смета в блоге Drools & jBPM .