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