Drools 6.1.0.Final CEP Example: Unable to create Field Extractor

2.8k views Asked by At

I'm experiencing an odd error while trying to convert a a CEP example from Drools 5X to 6X: - Drool 6.1.0.Final to be precise.

The source of my inspiration for this little project can be found at the following link:=> PlugTree.

The error I get indicated that Drools is unable to create a Field Extractor - an error one gets when they forget to create setters/getters in their Domain POJO's.

SEVERE: Unable to build KieBaseModel:rules Unable to create Field Extractor for 'amount'Field/method 'amount' not found for class 'com.sample.Sale' : [Rule name='StoreOne - Has Passed it's Sales Record'] java.lang.RuntimeException: Field/method 'amount' not found for class 'com.sample.Sale'

I've traced the problem to a 'declare' statement in the Rules File (I'll list the full listing further down):

declare Sale
    @role(event)
end

Using just this causes the error (which does NOT happen in V5 btw), however using the next "altered" declaration statement does not cause an error. It just does nothing ...

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

What it does do - is nothing. It compiles, runs but the facts just don't get inserted (or recognized).

Here is my Java Test Harness:

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

And here is my Rules File:

//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

And my kmodule.xml file:

<?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>

I do think this is also relevant - the Consult output using a listener (for the altered declaration statement (listing the attributes):

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 *** 

Since there are very few examples of CEP in V5 floating around the web, and even less in V6 KIE, I'd appreciate any thoughts on the origins of this error and how to correct it.

2

There are 2 answers

0
ProfVersaggi On

I posted this in the Google Drools Board and got a decent answer - here is the repost:

By David Sottara:

A bit of history is needed. As of 6.2 and previous versions: Unfortunately, "declare" has historically been overloaded with two very (!) different use cases. One is to define new inline classes in the DRL. The other is to redeclare preexisting classes for extension and annotation. In the former, new bytecode is generated. In the latter, Drools uses the existing bytecode and simply gathers metadata about the usage of the class.

Unfortunately, for compatibility reasons, we could not use two keywords to differentiate the use cases. The price to pay, alas, is ambiguity - reason why we're trying to deprecate the whole feature in favor of a more robust one. Furthermore, the historical ability to declare "foreign" classes in packages they don't belong to made things worse.

The (weak) strategy we use to disambiguate is essentially the following. If a declare can't be traced to any existing class in the classpath, it's considered a "define" and bytecode is generated. If there is a potential name clash, the following rules apply. If the declare does not have any fields, it is considered a "redeclare". if the declare has EXACTLY the same fields as the class in the classpath, it is considered a "redeclare". Otherwise, an error is generated and reported.

This said, a number of things may happen in your code. - if the rules are written in terms of a "Sale" class on the classpath, and that class does not have all the required getters/setters, declarations won't help you since your main java code will still instantiate that class - The no-field declare will be considered a "redeclare", so the DRL will use the class on the classpath. It may be the case that 5.5 was just looking for getters, while later versions require BOTH getter and setter. - I wonder if the declare with attributes is actually considered as a "define", resulting in a class with the same name and attributes, but which does not really match the instances you provide from the java code.

[EDIT: After checking the Plugtree site and double-checking your code]

The "Sale" class used to be in org.nicozan.examples.droolsfusion. You use it in your com.sample.cep.CEPExample class without an import, so I assume that it lives in com.sample.cep (!confirmed by the listener trace!); Your DRL, however, uses the package com.sample AND imports com.sample.Sale rather than com.sample.cep.Sale. So, your declares are always "defines": the first creates a com.sample.Sale with no attributes and the compiler complains. In your second case, rules are compiled using com.sample.Sale (which has the appropriate attributes) but you insert instances of com.sample.cep.Sale - a different class, so no rule fires.

The import self-circuits on the locally defined class, so you don't get any compile time error. This may be something we could fix. @Mario, could you help track this, based on the follow-up?

Diagnosing as I write, I hope this clarifies the current behavior of "declares" for others on the list, too Thanks for the report Davide

THE SOLUTION:

In the rules file I changed:

**import com.sample.Sale;**

to:

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

That fixed it. However, what I learned in the process was far more valuable than the simple fix. I hope to pass it along to others ....

6
laune On

A declare statement of the DRL language can be used for two rather different purposes:

  1. Add metadata (such as @role) to a POJO
  2. Declare a class (without requiring any Java code) for use within rules (and Java application code)

The distinction is derived from the presence of one or more fields in the declare statement.

When using the second form, Java code cannot create objects of the declared class in the usual way. You'll have to use the roundabout technique of locating the class from the package using (KieBase.getFactType()), create an instance (FactType.newInstance()) and add attribute values using the generic setters (FactType.set(), setFromMap()).

It's quite possible that something goes wrong if you have a POJO Sale and a declared class Sale at the same time. I haven't tried a build using kmodule.xml (I never do), but calling the KieBuilder from Java and checking for errors produces (Sale fields not commented):

[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 : ...

BTW, this example isn't using any event processing feature and the issue isn't related to cEP at all.

Later

The only difference with the insertion of a "standard fact" and an "event fact" is that the Engine may have to add the timestamp field (unless your meta data says it's already there as an attribute).

Technically, for the way you code an insert, it depends on the way the fact type is defined: either by a Java class (with the "event" flavour being added by the declare according to item 1) or by a full-blown declare, as described previously. There is no further difference w.r.t. insertion between plain fact and event fact.

Also, there's no difference in accessing (to retrieve attributes for pattern evaluation), except that events have the additional timestamp attribute (unless mapped to an explicit attribute) which is implicitly retrieved when temporal operators are applied.

There are differences in processing, most notably temporal operators being applicable to events themselves (as opposed to attributes). Also, events may be eligible for automatic retraction, and they are available for selection in windows.