Drools 6.1.0.Final CEP Пример: невозможно создать Field Extractor

Я испытываю странную ошибку при попытке преобразовать пример CEP из Drools 5X в 6X: - Drool 6.1.0.Final, чтобы быть точным.

Источник моего вдохновения для этого маленького проекта можно найти по следующей ссылке: => PlugTree.

Полученная мной ошибка указывает на то, что Drools не может создать Field Extractor - ошибка, которая возникает, когда они забывают создать сеттеры / геттеры в своих POJO домена.

SEVERE: Невозможно построить KieBaseModel:rules Невозможно создать извлечение поля для поля "amount". Поле / метод "amount" не найден для класса "com.sample.Sale": [имя правила = "StoreOne - прошел запись о продажах"] java.lang.RuntimeException: поле / метод "количество" не найдено для класса "com.sample.Sale"

Я проследил проблему до заявления "объявлять" в файле правил (полный список приведен ниже):

declare Sale
    @role(event)
end

Использование только этого вызывает ошибку (что не происходит в V5 между прочим), однако использование следующего "измененного" оператора объявления не вызывает ошибку. Это просто ничего не делает...

declare Sale
    @role(event)
    article : String
    amount : long
    quantity : int
end

Что он делает - ничего. Он компилируется, запускается, но факты просто не вставляются (или не распознаются).

Вот мой тестовый комплект Java:

package com.sample.cep;

import org.kie.api.KieBaseConfiguration;
import org.kie.api.KieServices;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.event.rule.DebugAgendaEventListener;
import org.kie.api.event.rule.DebugRuleRuntimeEventListener;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.conf.ClockTypeOption;
import org.kie.api.runtime.rule.EntryPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class CEPExample {

    public static void main(String[] args) {


        try {
            // load up the knowledge base & get the kSession
            KieServices ks = KieServices.Factory.get();
            KieContainer kContainer = ks.getKieClasspathContainer();
            KieSession kSession = kContainer.newKieSession("ksession-rules");

            // CEP - get the KIE related configuration container and set the EventProcessing (from default cloud) to Stream
            KieBaseConfiguration config = ks.newKieBaseConfiguration();
            config.setOption( EventProcessingOption.STREAM );


            // Listeners
            kSession.addEventListener( new DebugAgendaEventListener() );
            kSession.addEventListener( new DebugRuleRuntimeEventListener() );

            // To setup a file based audit logger, uncomment the next line 
            // KieRuntimeLogger loggerKie = ks.getLoggers().newFileLogger( kSession, "./logger" );
            // KieRuntimeLogger consoleLogger = ks.getLoggers().newConsoleLogger(kSession); 

            Logger logger = LoggerFactory.getLogger(CEPExample.class);
            logger.info("\n*********************************>>>> Drools CEP Example \n");   

            // Each Event is Inserted into WorkingMemory through an *EntryPoint*
            EntryPoint entryPointStoreOne = kSession.getEntryPoint( "StoreOne" );
            EntryPoint entryPointStoreTwo = kSession.getEntryPoint( "StoreTwo" );


            // Insert EventData into WM for StoreOne
            entryPointStoreOne.insert(new Sale("meat", 40, 5) );
            entryPointStoreOne.insert(new Sale("bananna", 5, 10) );
            entryPointStoreOne.insert(new Sale("pear", 5, 10) );
            entryPointStoreOne.insert(new Sale("yogurt", 5, 50) );
            entryPointStoreOne.insert(new Sale("led TV", 10000, 1) );

            // Insert EventData into WM for StoreTwo
            entryPointStoreTwo.insert(new Sale("meat", 40, 5) );
            entryPointStoreTwo.insert(new Sale("bananna", 5, 10) );
            entryPointStoreTwo.insert(new Sale("pear", 5, 10) );
            entryPointStoreTwo.insert(new Sale("yogurt", 5, 50) );



            // Fire all Rules
            kSession.fireAllRules();


            // Close Logger
            //logger.close();

            // Close the session
            kSession.destroy();

            System.out.println("*** DONE *** ");

        } catch (Throwable t) {
            t.printStackTrace();
        } 

    } // End Method - MAIN




//  // Helper Class to INSERTEVENT
//  private static void insertEvent(EntryPoint entryPoint, Sale sale, String article, long amount, int quantity) {
//  
//      sale.setArticle(article);
//      sale.setAmount(amount);
//      sale.setQuantity(quantity);
//      entryPoint.insert(sale);
//      
//  } // End Class insertEvent  



}// End Class CEPExample

И вот мой файл правил:

//created on: Nov 28, 2014
package com.sample

import com.sample.Sale;


// Declarations

declare Sale
    @role(event)

    //article : String
    //amount : long
    //quantity : int
end



rule "StoreOne - Has Passed it's Sales Record"
    when
        Number( $totalSalesAmount : intValue, intValue > 1000 )
        from accumulate ( Sale($amount : amount, $quantity : quantity) 
        from entry-point "StoreOne", sum( $amount*$quantity ))
    then
        System.out.println("StoreOne - Has passed its Sales Record!");
end


rule "StoreTwo - has Passed its Sales Record"
    when
        Number( $totalSalesAmount : intValue, intValue > 1000 )
        from accumulate ( Sale($amount : amount, $quantity : quantity) from entry-point "StoreTwo", sum( $amount * $quantity ))
    then
        System.out.println("StoreTwo - Has passed its Sales Record!");  
end

И мой файл kmodule.xml:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="rules" packages="rules">
        <ksession name="ksession-rules"/>
    </kbase>
</kmodule>

Я действительно думаю, что это также актуально - вывод Consult с использованием слушателя (для измененного оператора объявления (перечисляющего атрибуты):

Nov 29, 2014 4:31:10 PM org.drools.compiler.kie.builder.impl.ClasspathKieProject notifyKieModuleFound
INFO: Found kmodule: file:/C:/Users/versaggi/workspace-spring-framework/CEPProject/target/classes/META-INF/kmodule.xml
Nov 29, 2014 4:31:10 PM org.drools.compiler.kie.builder.impl.KieRepositoryImpl addKieModule
INFO: KieModule was added:FileKieModule[ ReleaseId=com.versaggi:CEPProject:0.0.1-SNAPSHOTfile=C:\Users\versaggi\workspace-spring-framework\CEPProject\target\classes]
Nov 29, 2014 4:31:14 PM com.sample.cep.CEPExample main
INFO: 
*********************************>>>> Drools CEP Example 

==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:1:24780333:24780333:1:StoreOne:NON_TRAIT:com.sample.cep.Sale@17a1e2d], getObject()=com.sample.cep.Sale@17a1e2d, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreOne, factHandle=[fact 0:1:24780333:24780333:1:StoreOne:NON_TRAIT:com.sample.cep.Sale@17a1e2d], leftTuple=null, originOffset=-1, propagationNumber=2, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:2:20723018:20723018:2:StoreOne:NON_TRAIT:com.sample.cep.Sale@13c354a], getObject()=com.sample.cep.Sale@13c354a, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreOne, factHandle=[fact 0:2:20723018:20723018:2:StoreOne:NON_TRAIT:com.sample.cep.Sale@13c354a], leftTuple=null, originOffset=-1, propagationNumber=3, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:3:16489063:16489063:3:StoreOne:NON_TRAIT:com.sample.cep.Sale@fb9a67], getObject()=com.sample.cep.Sale@fb9a67, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreOne, factHandle=[fact 0:3:16489063:16489063:3:StoreOne:NON_TRAIT:com.sample.cep.Sale@fb9a67], leftTuple=null, originOffset=-1, propagationNumber=4, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:4:33509294:33509294:4:StoreOne:NON_TRAIT:com.sample.cep.Sale@1ff4fae], getObject()=com.sample.cep.Sale@1ff4fae, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreOne, factHandle=[fact 0:4:33509294:33509294:4:StoreOne:NON_TRAIT:com.sample.cep.Sale@1ff4fae], leftTuple=null, originOffset=-1, propagationNumber=5, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:5:27946348:27946348:5:StoreOne:NON_TRAIT:com.sample.cep.Sale@1aa6d6c], getObject()=com.sample.cep.Sale@1aa6d6c, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreOne, factHandle=[fact 0:5:27946348:27946348:5:StoreOne:NON_TRAIT:com.sample.cep.Sale@1aa6d6c], leftTuple=null, originOffset=-1, propagationNumber=6, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:6:26643062:26643062:6:StoreTwo:NON_TRAIT:com.sample.cep.Sale@1968a76], getObject()=com.sample.cep.Sale@1968a76, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreTwo, factHandle=[fact 0:6:26643062:26643062:6:StoreTwo:NON_TRAIT:com.sample.cep.Sale@1968a76], leftTuple=null, originOffset=-1, propagationNumber=7, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:7:26870209:26870209:7:StoreTwo:NON_TRAIT:com.sample.cep.Sale@19a01c1], getObject()=com.sample.cep.Sale@19a01c1, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreTwo, factHandle=[fact 0:7:26870209:26870209:7:StoreTwo:NON_TRAIT:com.sample.cep.Sale@19a01c1], leftTuple=null, originOffset=-1, propagationNumber=8, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:8:1161635:1161635:8:StoreTwo:NON_TRAIT:com.sample.cep.Sale@11b9a3], getObject()=com.sample.cep.Sale@11b9a3, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreTwo, factHandle=[fact 0:8:1161635:1161635:8:StoreTwo:NON_TRAIT:com.sample.cep.Sale@11b9a3], leftTuple=null, originOffset=-1, propagationNumber=9, rule=null, type=0]]
==>[ObjectInsertedEventImpl: getFactHandle()=[fact 0:9:2257152:2257152:9:StoreTwo:NON_TRAIT:com.sample.cep.Sale@227100], getObject()=com.sample.cep.Sale@227100, getKnowledgeRuntime()=org.drools.core.impl.StatefulKnowledgeSessionImpl@527389, getPropagationContext()=PhreakPropagationContext [entryPoint=EntryPoint::StoreTwo, factHandle=[fact 0:9:2257152:2257152:9:StoreTwo:NON_TRAIT:com.sample.cep.Sale@227100], leftTuple=null, originOffset=-1, propagationNumber=10, rule=null, type=0]]
*** DONE *** 

Поскольку в V5 очень мало примеров использования CEP в V5, и еще меньше в V6 KIE, я был бы признателен за любые мысли о причинах этой ошибки и способах ее исправления.

2 ответа

declare Изложение языка ДХО можно использовать для двух довольно разных целей:

  1. Добавить метаданные (например, @role) к POJO
  2. Объявите класс (не требующий никакого кода Java) для использования в правилах (и коде приложения Java)

Различие определяется наличием одного или нескольких полей в declare заявление.

При использовании второй формы Java-код не может создавать объекты объявленного класса обычным способом. Вам нужно будет использовать метод обхода, чтобы найти класс из пакета, используя (KieBase.getFactType()), создать экземпляр (FactType.newInstance()) и добавить значения атрибутов с помощью универсальных сеттеров (FactType.set(), setFromMap()).

Вполне возможно, что что-то пойдет не так, если у вас одновременно есть продажа POJO и заявленная продажа класса. Я не пробовал сборку с использованием kmodule.xml (я никогда этого не делаю), но вызов KieBuilder из Java и проверка на наличие ошибок приводит к (Поля продажи не прокомментированы):

[main] ERROR org.drools.compiler.kie.builder.impl.AbstractKieModule - Unable to build KieBaseModel:defaultKieBase
New declaration of sale.Sale can't declaredeclares a different set of fields  
existing : ...
declared : ...

Кстати, в этом примере не используются никакие функции обработки событий, и проблема вообще не связана с cEP.

Потом

Единственное отличие от вставки "стандартного факта" и "факта события" состоит в том, что Механизму, возможно, придется добавить поле метки времени (если ваши метаданные не говорят, что он уже существует в качестве атрибута).

Технически, то, как вы кодируете вставку, зависит от того, как определен тип факта: либо с помощью класса Java (с добавлением аромата "события", объявленного объявлением в соответствии с пунктом 1), либо с помощью полномасштабного объявления, как описано ранее. Больше нет никакой разницы по вставке между простым фактом и фактом события.

Кроме того, нет разницы в доступе (для извлечения атрибутов для оценки шаблона), за исключением того, что у событий есть дополнительный атрибут отметки времени (если он не сопоставлен с явным атрибутом), который неявно извлекается при применении временных операторов.

Существуют различия в обработке, особенно временные операторы применимы к самим событиям (в отличие от атрибутов). Кроме того, события могут иметь право на автоматический отвод, и они доступны для выбора в окнах.

Я разместил это в Google Drools Board и получил достойный ответ - вот репост:

Дэвид Соттара:

Немного истории необходимо. Начиная с версии 6.2 и предыдущих версий: К сожалению, метод "объявить" исторически был перегружен двумя (!) Разными вариантами использования. Одним из них является определение новых встроенных классов в ДХО. Другой заключается в том, чтобы повторно объявить существующие классы для расширения и аннотации. В первом генерируется новый байт-код. В последнем Drools использует существующий байт-код и просто собирает метаданные об использовании класса.

К сожалению, из соображений совместимости мы не смогли использовать два ключевых слова для разграничения вариантов использования. Увы, цена за это - двусмысленность - причина, по которой мы пытаемся отказаться от всей функции в пользу более надежной. Более того, историческая способность объявлять "чужие" классы в пакетах, к которым они не принадлежат, еще более усугубила ситуацию.

(Слабая) стратегия, которую мы используем для устранения неоднозначности, заключается в следующем. Если объявление не может быть прослежено до какого-либо существующего класса в пути к классам, это считается "определить" и генерируется байт-код. Если существует потенциальное столкновение имен, применяются следующие правила. Если в объявлении нет полей, оно считается "переобъявленным". если объявление имеет ТОЧНО те же поля, что и класс в пути к классам, оно считается "переопределенным". В противном случае ошибка генерируется и сообщается.

При этом в вашем коде может произойти ряд вещей. - если правила написаны в терминах класса "Sale" на пути к классам, и этот класс не имеет всех необходимых методов получения / установки, объявления не помогут вам, так как ваш основной код Java все еще будет создавать этот класс - No -field объявлять будет считаться "redeclare", поэтому DRL будет использовать класс на пути к классам. Это может быть тот случай, когда 5.5 просто ищет геттеры, в то время как более поздние версии требуют ОБА геттера и сеттера. - Интересно, действительно ли объявление с атрибутами рассматривается как "определение", в результате чего получается класс с тем же именем и атрибутами, но который на самом деле не соответствует экземплярам, ​​которые вы предоставляете из кода Java.

[РЕДАКТИРОВАТЬ: После проверки сайта Plugtree и двойной проверки вашего кода]

Класс "Sale" был в org.nicozan.examples.droolsfusion. Вы используете его в своем классе com.sample.cep.CEPExample без импорта, поэтому я предполагаю, что он находится в com.sample.cep (! Подтверждено трассировкой слушателя!); Однако ваш DRL использует пакет com.sample AND для импорта com.sample.Sale, а не com.sample.cep.Sale. Итак, ваши объявления всегда "определяют": первый создает com.sample.Sale без атрибутов, и компилятор жалуется. Во втором случае правила компилируются с использованием com.sample.Sale (который имеет соответствующие атрибуты), но вы вставляете экземпляры com.sample.cep.Sale - другого класса, поэтому правила не запускаются.

Импорт самоконтроля в локально определенном классе, поэтому вы не получите никакой ошибки времени компиляции. Это может быть что-то, что мы могли бы исправить. @Mario, не могли бы вы помочь отследить это, основываясь на результатах?

Диагностируя, как я пишу, я надеюсь, что это проясняет текущее поведение "объявляет" для других в списке, тоже Спасибо за отчет Davide

РЕШЕНИЕ:

В файле правил я изменил:

**import com.sample.Sale;**

чтобы:

**import com.sample.cep.Sale;**

Это исправило это. Однако то, что я узнал в процессе, было гораздо более ценным, чем простое исправление. Я надеюсь передать это другим....

Другие вопросы по тегам