Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drools fires in a loop

Tags:

drools

Simple example loops ad infinitum if executed. If I add "no-loop true" it works. But why? There is no loop hole...

package spikes;

import org.springframework.roo.addon.javabean.RooJavaBean;

@RooJavaBean
public class Applicant {
private final String string;
private final int age;
public boolean valid=true;

public Applicant(String string, int i) {
    this.string = string;
    this.age = i;
}
}

DroolsSpikeTest

package spikes;

import static org.junit.Assert.*;

import org.drools.KnowledgeBase;
import org.drools.runtime.StatelessKnowledgeSession;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:**/applicationContext-drools.xml"})
public class DroolsSpikeTest {

    @Autowired
    private KnowledgeBase kbase;

    @Test
    public void testspikeDroolRule() {
        StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
        Applicant a = new Applicant("Mr John Smith", 16);
        assertTrue(a.isValid());
        ksession.execute(a);
        assertFalse(a.isValid());
    }

}

applicationContext-drools.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--INFO: http://architects.dzone.com/articles/drools-51-expands-spring -->
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:aop="http://www.springframework.org/schema/aop" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:jee="http://www.springframework.org/schema/jee" 
xmlns:tx="http://www.springframework.org/schema/tx" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:drools="http://drools.org/schema/drools-spring" 
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://drools.org/schema/drools-spring http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-container/drools-spring/src/main/resources/org/drools/container/spring/drools-spring-1.0.0.xsd">

<drools:kbase id="kbase1">
   <drools:resources>
    <drools:resource id="licenseRule" type="DRL" source="classpath:spikes/licenseApplication.drl"/>
   </drools:resources>
   <drools:configuration>
       <drools:mbeans enabled="true" />
   </drools:configuration>
</drools:kbase>
<drools:ksession kbase="kbase1" type="stateless" id="ksessionStateless" name="stateless1" >
</drools:ksession>
</beans>

licenseApplication.drl

import spikes.Applicant
rule "Is of valid age" when
    $a : Applicant( age < 18 )
then
    modify( $a ) { valid = false };
end
like image 407
Roland Kofler Avatar asked Jan 20 '12 14:01

Roland Kofler


1 Answers

The line beginning with modify($a) tells the engine that the Applicant fact has been updated. This causes the engine to reevaluate all rule conditions. Since the Applicant's age is still under 18, this causes this rule to activate again. Rinse and repeat, forever.

Adding no-loop tells the engine that the consequence of a rule should not cause the same rule to reactivate. Please note that no-loop is not a silver bullet against infinite loops; if the consequence of Rule A causes Rule B to activate, and the consequence of Rule B causes Rule A to activate, no-loop will not prevent the infinite loop.

Personally, I think no-loop is a cheat to get around writing better conditions. The better solution to this is to write your conditions in such a way that they do not reactivate when they shouldn't. Sometimes this involves inserting "control facts" that your conditions reference, but you can often simply update the pattern to be more specific. In your case, the solution is simple:

rule "Is of valid age" when
    $a : Applicant( age < 18, valid != false )
then
    modify( $a ) { valid = false };
end

By adding valid != false to your pattern, the modification of the fact will not cause this rule to reactivate.

like image 70
mike9322 Avatar answered Sep 28 '22 21:09

mike9322