I have a drools decision table in excel spreadsheet with two rules. (This example has been greatly simplified, the one I am working with has alot more rules.)
The first rule checks if amount is more than or equal to 500. If it is, then it sets status to 400.
The second rule checks if status is 400. If it is, then it sets the message variable.
The problem is, I am unable to get the second rule to fire, even though sequential is set. I also have to use no-loop and lock-on-active to prevent infinite looping.
My goal is to get the rules to fire top down, and the rules that come after might depend on changes made to the fact/object by earlier rules.
Is there a solution to this problem?
Any help would be appreciated, thanks!
package com.example;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
public class SalaryTest {
public static final void main(String[] args) {
try {
// load up the knowledge base
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("ksession-dtables");
Salary a = new Salary();
a.setAmount(600);
kSession.insert(a);
kSession.fireAllRules();
} catch (Throwable t) {
t.printStackTrace();
}
}
public static class Salary{
private String message;
private int amount;
private int status;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
}
The attribute lock-on-active countermands any firings after the first from the group of rules with the same agenda group. Remove this column.
Don't plan to have rules firing in a certain order. Write logic that describes exactly the state of a fact as it should trigger the rule. Possibly you'll have to write
rule "set status"
when
$s: Salary( amount >= 500.0 && < 600.0, status == 0 )
then
modify( $s ){ setStatus( 400 ) }
end
to avoid more than one status setting to happen or just the right setting to happen. But you'll find that your rules may be more outspoken and easier to read.
Think of rule attributes are a last resort.
Please replace the action in the column H in the following way:
Current solution:
a.setStatus($param);update(a);
New solution:
modify(a) {
setStatus($param)
}
Related
I have a simple Solr query component as follows:
public class QueryPreprocessingComponent extends QueryComponent implements PluginInfoInitialized {
private static final Logger LOG = LoggerFactory.getLogger( QueryPreprocessingComponent.class );
private ExactMatchQueryProcessor exactMatchQueryProcessor;
public void init( PluginInfo info ) {
initializeProcessors(info);
}
private void initializeProcessors(PluginInfo info) {
List<PluginInfo> queryPreProcessors = info.getChildren("queryPreProcessors")
.get(0).getChildren("queryPreProcessor");
for (PluginInfo queryProcessor : queryPreProcessors) {
initializeProcessor(queryProcessor);
}
}
private void initializeProcessor(PluginInfo queryProcessor) {
QueryProcessorParam processorName = QueryProcessorParam.valueOf(queryProcessor.name);
switch(processorName) {
case ExactMatchQueryProcessor:
exactMatchQueryProcessor = new ExactMatchQueryProcessor(queryProcessor.initArgs);
LOG.info("ExactMatchQueryProcessor initialized...");
break;
default: throw new AssertionError();
}
}
#Override
public void prepare( ResponseBuilder rb ) throws IOException
{
if (exactMatchQueryProcessor != null) {
exactMatchQueryProcessor.modifyForExactMatch(rb);
}
}
#Override
public void process(ResponseBuilder rb) throws IOException
{
// do nothing - needed so we don't execute the query here.
return;
}
}
This works as expected functionally except when I use this in a distributed request, it has an issue with facets counts returned. It doubles the facet counts.
Note that I am not doing anything related to faceting in plugin. exactMatchQueryProcessor.modifyForExactMatch(rb); does a very minimal processing if the query is quoted otherwise it does nothing. Even if the incoming query is not quoted, facet count issue is there. Even if I comment everything inside prepare function, issue persists.
Note that this component is declared in as first-components in solrconfig.xml.
I resolved this issue by extending the class to SearchComponent instead of QueryComponent. It seems that SearchComponent sits at higher level of abstraction than QueryComponent and is useful when you want to work on a layer above shards.
Please have a look at the two code settings below.
Setting 1:
public class DroolsAnotherTest {
private static KieSession kSession;
private static Building building;
private static FactHandle buildingFact;
public static final void main(String[] args) {
try {
// load up the knowledge base
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
kSession = kContainer.newKieSession("ksession-rules");
building = new Building();
building.setRooms(2);
buildingFact = kSession.insert(building);
kSession.fireAllRules();
} catch (Throwable t) {
t.printStackTrace();
}
}
public static void fireAgain(){
System.out.println("inside fireAgain method");
kSession.fireAllRules(); // firing the rules second time
}
public static class Building {
public int rooms;
public int getRooms() {
return this.rooms;
}
public void setRooms(int rooms) {
this.rooms = rooms;
}
}
}
And the .drl file
package com.sample
import com.sample.DroolsAnotherTest.Building;
import com.sample.DroolsAnotherTest;
rule "Building"
when
Building( rooms <3 )
then
System.out.println( "Building rule fired" );
DroolsAnotherTest.firingAgain();
end
Upon running, the output is :
Building rule fired
inside fireAgain method
Setting 2:
Here I just changed the fireAgain() method to:
public static void fireAgain(){
System.out.println("inside fireAgain method");
kSession.delete(buildingFact);
building.setRooms(4);
kSession.insert(building);
kSession.fireAllRules(); // firing the rule second time
}
everything else is same.
Upon running, the output is same again :
Building rule fired
inside fireAgain method
As per my understanding,
in Setting 1, the rule did not get fired second time because the fact has not changed in the working memory.
in Setting 2, the rule did not get fired second time because now the fact has updated and it does not match with the rule condition.
My question is, does Drools generate any event for the setting 2 ( the rule fired once but now is not fired again because fact has updated ?) In that way I can distinguish between : a rule did not get fired because the fact are unchanged and it did not get fired because now the fact does not match with rule condition ?
There is no way you can determine why a rule isn't put on the agenda.
You know when you update a fact, so if this interesting for some reason, register it in the fact or elsewhere.
And DO NOT, repeat: DO NOT, call fireAllRules while another such call on the same session is still executing. There may be all kind of unexpected effects.
I need help with understanding of the win_ext window in Esper (CEP). I'm wondering why older (first 2) events still popup on the update-method even though they have been "expired"
public class MyCepTest {
public static void main(String...args) throws Exception{
System.out.println("starting");
MyCepTest ceptest = new MyCepTest();
ceptest.execute();
System.out.println("end");
}
public void execute() throws Exception{
Configuration config = new Configuration();
config.addEventType(MyPojo.class);
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);
EPAdministrator admin = epService.getEPAdministrator();
EPStatement x1 = admin.createEPL(win);
EPStatement x2 = admin.createEPL(win2);
x1.setSubscriber(this);
x2.setSubscriber(this);
EPRuntime runtime = epService.getEPRuntime();
ArrayList<MyPojo> staffToSendToCep = new ArrayList<MyPojo>();
staffToSendToCep.add(new MyPojo(1, new Date(1490615719497L)));
staffToSendToCep.add(new MyPojo(2, new Date(1490615929497L)));
for(MyPojo pojo : staffToSendToCep){
runtime.sendEvent(pojo);
}
Thread.sleep(500);
System.out.println("round 2...");//why two first Pojos are still found? Shouldn't ext_timed(pojoTime.time, 300 seconds) rule them out?
staffToSendToCep.add(new MyPojo(3, new Date(1490616949497L)));
for(MyPojo pojo : staffToSendToCep){
runtime.sendEvent(pojo);
}
}
public void update(Map<String,Object> map){
System.out.println(map);
}
public static String win = "create window fiveMinuteStuff.win:ext_timed(pojoTime.time, 300 seconds)(pojoId int, pojoTime java.util.Date)";
public static String win2 = "insert into fiveMinuteStuff select pojoId,pojoTime from MyPojo";
}
class MyPojo{
int pojoId;
Date pojoTime;
MyPojo(int pojoId, Date date){
this.pojoId = pojoId;
this.pojoTime = date;
}
public int getPojoId(){
return pojoId;
}
public Date getPojoTime(){
return pojoTime;
}
public String toString(){
return pojoId+"#"+pojoTime;
}
}
I've been puzzled with this for a while and help would be greatly appreciated
See the processing model in docs. http://espertech.com/esper/release-6.0.1/esper-reference/html/processingmodel.html
All incoming insert-stream events are delivered to listeners and subscribers. regardless of your window. A window, if one is in the query at all, defines the subsets of events to consider and therefore defines what gets aggregated, pattern-matched or is available for iteration. Try "select * from MyPojo" for reference. My advice to read up on external time, see http://espertech.com/esper/release-6.0.1/esper-reference/html/api.html#api-controlling-time
Usually when you want "external time window" you want event time to drive engine time.
I am new to the drools. I was trying to understand the difference between Stateless and Stateful sessions provided by Drools.
As per my initial understanding,
In case of Stateless session, if fact is modified during action execution of any rule then it will not be re-submitted to inference engine to find out the new rules which matches the modified fact.
In case of Stateful session, if fact is modified during action execution of any rule then it will be re-submitted to inference engine to find out the new rules which matches the modified fact and then their corresponding action will be executed.
So when I tried to verify this behavior by writing a sample rule, I found that behavior is exactly same in both the cases. So now I am really confused regarding the difference between Stateful and Stateless sessions.
I would like to request everyone to help me in understand the correct behavior of Stateful and Stateless sessions.
For your reference I am pasting my sample code for Stateful and Stateless session along with their output and sample rule.
licenseApplication.drl (Rule file)
package com.idal.droolsapp
rule "Is of valid age"
no-loop
when
$a : Applicant( age < 18 )
then
System.out.println( "Not eligible for license" );
System.out.println( "Setting Valid to false" );
modify( $a ) { setValid( false ) };
end
rule "Is of valid false"
salience 100
when
$a : Applicant( valid == false )
then
System.out.println( "Second rule fired" );
end
Input object (Fact) Applicant.java
package com.idal.droolsapp;
public class Applicant {
private String name;
private int age;
private boolean valid = true;
public Applicant(String name, int age) {
setName(name);
setAge(age);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setValid(boolean valid) {
this.valid = valid;
}
public boolean isValid() {
return valid;
}
#Override
public String toString() {
return "Applicant [name=" + name + ", age=" + age + ", valid=" + valid
+ "]";
}
}
StatelessSessionExample.java (Stateless Session test code)
package com.idal.droolsapp;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatelessKnowledgeSession;
public class StatelessSessionExample {
/**
* #param args
*/
public static void main(String[] args) {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource(
"licenseApplication.drl", StatelessSessionExample.class),
ResourceType.DRL);
if (kbuilder.hasErrors()) {
System.err.println(kbuilder.getErrors().toString());
}
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
Applicant applicant = new Applicant( "Mr John Smith", 16 );
ksession.execute( applicant );
System.out.println("Updated Applicant = " + applicant);
}
}
Output of Stateless session test code:
Not eligible for license
Setting Valid to false
Second rule fired
Updated Applicant = Applicant [name=Mr John Smith, age=16, valid=false]
StatefulSessionExample.java (Stateless Session test code)
package com.idal.droolsapp;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
public class StatefulSessionExample {
/**
* #param args
*/
public static void main(String[] args) {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource(
"licenseApplication.drl", StatefulSessionExample.class),
ResourceType.DRL);
if (kbuilder.hasErrors()) {
System.err.println(kbuilder.getErrors().toString());
}
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
Applicant applicant = new Applicant( "Mr John Smith", 16 );
ksession.insert( applicant );
ksession.fireAllRules();
ksession.dispose();
System.out.println("Updated Applicant = " + applicant);
}
}
Output of Stateful session test code:
Not eligible for license
Setting Valid to false
Second rule fired
Updated Applicant = Applicant [name=Mr John Smith, age=16, valid=false]
Once again requesting everyone to help me in understanding the correct behavior of Stateful and Stateless sessions.
Thanks in advance,
Manish Gandhi
As I understand it when you fire the rules in a stateless session, changes won't trigger new rules. But that doesn't mean that rules won't be fired because of changes. The first rule changes the value of valid, which means that by the time you check the second rule's condition valid is already false and the rules fires.
If the change affected the "when" part of the previous rule, in a stateless session the first rule wouldn't be triggered a second time, where in the first one it would.
I am using Spring batch for processing a file with a header, detail and footer records.
The footer contains the total number of records in the file.
If the detail record count dosent match the count in the footer, the file should not be processed.
I am using a Custom Line Tokenizer that processes the header, detail and footer record. When the footer record is encountered, if the count dosent match the detail record count, I am throwing an exception.
But the problem I am facing is if the chunk size is set to small numbers like 10 and the file has 20 records, the first 10 detail records are being persisted into the DB, even though the footer count dosent match the total number of records.
Is there a way to validate the footer count with the number of records in the file before the call to the Writer?
Thanks.
What you need is a reader with a footer callback handler defined. I had faced a similar problem and this link helped me a lot!
See the last post by Atefeh Zareh. He has also included the xml configuration.
And regarding the first ten being persisted, you can have another validation step before the main processing step which will just check the header and trailer counts. Do not write any persisting logic in the writer. If the count fails, stop the job so that it does not go into the processing step.
By writing our own Item Reader as well as Item classes to handle Header,Footer,Data records and finding the counts of Header,Footer,Data records
ItemReader Class
public class AggregateItemReader<T> implements ItemStreamReader<ResultHolder> {
private ItemStreamReader<AggregateItem<T>> itemReader;
#Override
public ResultHolder read() throws Exception {
ResultHolder holder = new ResultHolder();
while (process(itemReader.read(), holder)) {
continue;
}
if (!holder.isExhausted()) {
return holder;
}
else {
return null;
}
}
private boolean process(AggregateItem<T> value, ResultHolder holder) {
// finish processing if we hit the end of file
if (value == null) {
LOG.debug("Exhausted ItemReader");
holder.setExhausted(true);
return false;
}
// start a new collection
if (value.isHeader()) {
LOG.debug("Header Record detected");
holder.addHeaderRecordCount();
return true;
}
// mark we are finished with current collection
if (value.isFooter()) {
LOG.debug("Tailer Record detected");
holder.addTailerRecordCount();
holder.setFiledRecordCount(value.getFieldSet().readInt(3));
System.out.println("###########################################"+holder.getDataRecordCount()+"############################################");
return false;
}
// add a simple record to the current collection
holder.addDataRecordCount();
return true;
}
And Item Class is
public class AggregateItem<T> {
#SuppressWarnings("unchecked")
public static <T> AggregateItem<T> getData(FieldSet fs) {
return new AggregateItem(fs, false, false, true);
}
#SuppressWarnings("unchecked")
public static <T> AggregateItem<T> getFooter(FieldSet fs) {
return new AggregateItem(fs, false, true, false);
}
#SuppressWarnings("unchecked")
public static <T> AggregateItem<T> getHeader(FieldSet fs) {
return new AggregateItem(fs, true, false, false);
}
private boolean data = false;
private FieldSet fieldSet;
private boolean footer = false;
private boolean header = false;
private T item;
public AggregateItem(FieldSet fs, boolean header, boolean footer, boolean data) {
this(null);
this.header = header;
this.footer = footer;
this.data = data;
this.fieldSet = fs;
}
public AggregateItem(T item) {
super();
this.item = item;
}
public FieldSet getFieldSet() {
return fieldSet;
}
public T getItem() {
return item;
}
public boolean isData() {
return data;
}
public boolean isFooter() {
return footer;
}
public boolean isHeader() {
return header;
}
}
And ResultHolder class is
public class ResultHolder implements {
private Integer headerRecordCount = 0;
private Integer dataRecordCount = 0;
private Integer tailerRecordCount = 0;
private Integer filedRecordCount;//this is to save record count given in source File
private boolean exhausted = false;//setters & getters
}
If any doubts feel free to mail at sk.baji6#gmail.com