Note: This bug is displayed in read-only format because the product is no longer active in Red Hat Bugzilla.

Bug 724450 (BRMS-395)

Summary: NullPointerException When Using Conditional Element "or" in LHS Together with a Return Value Restriction
Product: [JBoss] JBoss Enterprise BRMS Platform 5 Reporter: Alessandro Lazarotti <alazarot>
Component: BRE (Expert, Fusion)Assignee: Tihomir Surdilovic <tsurdilo>
Status: CLOSED NEXTRELEASE QA Contact:
Severity: unspecified Docs Contact:
Priority: high    
Version: 5.0.2CC: alazarot, lcarlon
Target Milestone: ---   
Target Release: BRMS 5.2.0.GA   
Hardware: Unspecified   
OS: Unspecified   
URL: http://jira.jboss.org/jira/browse/BRMS-395
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
<remark>Need info from Tihomir - Was anything changed for this one or is the resolution that we helped the customer redesign their rules? Thanks.</remark></para><para>When using the conditional 'or' element in the left handside of rules with a return value restriction the NullPointerException exception was thrown.
Story Points: ---
Clone Of: Environment:
Fedora 12, BRMS Enterprise Platform 5.0.2, Java 1.6 (sun/oracle)
Last Closed: 2011-06-02 21:43:04 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
debug1.png
none
debug2.png none

Description Alessandro Lazarotti 2010-10-19 21:21:29 UTC
Help Desk Ticket Reference: https://c.na7.visual.force.com/apex/Case_View?id=500A00000045HoV&sfdc.override=1
Steps to Reproduce: /* JAVA FILE TO LOAD THE RULE */
package com.sample;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;

public class DroolsTest {

private int arg;

public int getArg() {
return this.arg;
}

public void setArg(int arg) {
this.arg = arg;
}

public static final void main(String[] args) throws Exception {
KnowledgeBase kbase = readKnowledgeBase();
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

DroolsTest test = new DroolsTest();
test.setArg(2);
ksession.insert(test);

ksession.fireAllRules();
}

/* taken verbatim from the Hello World sample */
private static KnowledgeBase readKnowledgeBase() throws Exception {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory
.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("Sample.drl"),
ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (errors.size() > 0) {
for (KnowledgeBuilderError error : errors) {
System.err.println(error);
}
throw new IllegalArgumentException("Could not parse knowledge.");
}
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
return kbase;
}

}




/* PROBLEMATIC RULE FILE */
package com.sample
 
import com.sample.DroolsTest;

rule "test"
when
DroolsTest( arg == (1 + 1) );
// REMOVE THE COMMENTED PARTS BELOW TO GET THE EXCEPTION:
//(or eval(true);
eval(true);
//)
then
System.out.println( "it works!" );
end


When the above Java file is compiled and run in combination with the given rule file, everything works fine. However, if the marked parts of the rule are uncommented, then you get the exception.
Workaround: Workaround Exists
Workaround Description: Two choices:

1) If you use a "eval" in the first sentence, it's work:
rule "test"
when
//now the sentence below with "eval"
DroolsTest( eval(arg == (1 + 1) ));
(or eval(true); eval(true);)
then
System.out.println( "it works!" );
end

2)Creating a new rule for the second part of your conditional "OR"
securitylevel_name: Public

When using the conditional element "or" on the left hand side of a rule together with a return value restriction somewhere in the same rule, I get the following exception:


Exception in thread "main" java.lang.NullPointerException
at org.drools.rule.ReturnValueRestriction.equals(ReturnValueRestriction.java:304)
at org.drools.rule.ReturnValueConstraint.equals(ReturnValueConstraint.java:121)
at org.drools.reteoo.AlphaNode.equals(AlphaNode.java:218)
at org.drools.reteoo.SingleObjectSinkAdapter.getMatchingNode(SingleObjectSinkAdapter.java:48)
at org.drools.reteoo.builder.BuildUtils.attachNode(BuildUtils.java:130)
at org.drools.reteoo.builder.PatternBuilder.attachAlphaNodes(PatternBuilder.java:295)
at org.drools.reteoo.builder.PatternBuilder.attachPattern(PatternBuilder.java:117)
at org.drools.reteoo.builder.PatternBuilder.build(PatternBuilder.java:70)
at org.drools.reteoo.builder.GroupElementBuilder$AndBuilder.build(GroupElementBuilder.java:126)
at org.drools.reteoo.builder.GroupElementBuilder.build(GroupElementBuilder.java:73)
at org.drools.reteoo.builder.ReteooRuleBuilder.addSubRule(ReteooRuleBuilder.java:153)
at org.drools.reteoo.builder.ReteooRuleBuilder.addRule(ReteooRuleBuilder.java:126)
at org.drools.reteoo.ReteooBuilder.addRule(ReteooBuilder.java:117)
at org.drools.reteoo.ReteooRuleBase.addRule(ReteooRuleBase.java:362)
at org.drools.common.AbstractRuleBase.addRule(AbstractRuleBase.java:618)
at org.drools.common.AbstractRuleBase.addPackages(AbstractRuleBase.java:500)
at org.drools.reteoo.ReteooRuleBase.addPackages(ReteooRuleBase.java:379)
at org.drools.impl.KnowledgeBaseImpl.addKnowledgePackages(KnowledgeBaseImpl.java:121)
at com.sample.DroolsTest.readKnowledgeBase(DroolsTest.java:48)
at com.sample.DroolsTest.main(DroolsTest.java:26)



PS: It seems that the issue has been existing in previous Drools versions already, see https://jira.jboss.org/jira/browse/JBSEAM-3064 for example ("Drools 4.0.7 don't like 'action == (DocumentType.USER.extendedName())' and multiple OR's - weird!").

Comment 1 Alessandro Lazarotti 2010-10-19 21:22:18 UTC
Workaround Description: Removed: Do you have two choices:

1) If you use a "eval" in the first sentence, it's work:
rule "test"
when
//now the sentence below with "eval"
DroolsTest( eval(arg == (1 + 1) ));
(or eval(true); eval(true);)
then
System.out.println( "it works!" );
end

2)Creating a new rule for the second part of your conditional "OR" Added: Two choices:

1) If you use a "eval" in the first sentence, it's work:
rule "test"
when
//now the sentence below with "eval"
DroolsTest( eval(arg == (1 + 1) ));
(or eval(true); eval(true);)
then
System.out.println( "it works!" );
end

2)Creating a new rule for the second part of your conditional "OR"


Comment 2 Alessandro Lazarotti 2010-10-19 21:24:01 UTC
Link: Added: This issue related JBRULES-2203


Comment 3 Alessandro Lazarotti 2010-10-19 22:26:45 UTC
I talked to Porcelli about this issue, which made some tests on the parser. Apparently the Parser and TreeWalker are generating the data (descriptors) correctly.

Attached here two prints about this (thanks Porcelli).


Comment 4 Alessandro Lazarotti 2010-10-19 22:27:32 UTC
Attachment: Added: debug1.png
Attachment: Added: debug2.png


Comment 5 Tihomir Surdilovic 2010-10-27 16:03:23 UTC
Can you please show us a real-world rule that reproduces this issue (from customer) because the example rule even tho exposes a possible issue with drools is really such bad practice that  it's worth more to investigate into providing a better pattern(s) for customer first.



Comment 6 Alessandro Lazarotti 2010-11-01 18:58:11 UTC
Below a real rule, from Customer:

rule "0701 - Guar Date Invalid"
dialect "mvel"
when
   ContinueProcessing( continueProcess == ( true ) )
   // it 's work if change to ->   cp : ContinueProcessing( eval( cp.isContinueProcess() )
   ( LenderLoan( eval( isBlank(guaranteeDate) )) or LenderLoan( eval( isStringDateNotAfter(guaranteeDate, "19651107") )) )
then
   LMError fact0 = new LMError();
   fact0.setCode( "0701" );
   insert(fact0 );
   System.out.println("Fired Rule: " + drools.getRule().getName());
end


function boolean isBlank(String arg) {    
   if(arg == null)
     return true;
   else 
    return (arg.trim().length() < 1);
}


function boolean isStringDateNotAfter(String inDate, String afterDate) {
   
 if (inDate == null)
      return true;
   
    //set the format to use as a constructor argument
    SimpleDateFormat dateFormat = new   SimpleDateFormat("yyyymmdd");
    String sDate = afterDate;
    Date d; 
    Date dIn;

  try {

    d = dateFormat.parse(sDate);
      dIn = dateFormat.parse(inDate);
   }
    catch (ParseException pe) {
      return true;
    }

    if (inDate.trim().length() != dateFormat.toPattern().length())
      return true;

    dateFormat.setLenient(false);
    
    try {
      //parse the inDate parameter
      dateFormat.parse(inDate.trim());
    }
    catch (ParseException pe) {
      return true;
    }
    
    boolean dTest = dIn.after(d);
 
    return  !dTest;
}

Comment 7 Tihomir Surdilovic 2010-11-02 03:01:51 UTC
Thanks Alessandro, I have placed a comment on your support ticket on this.

Comment 8 Tihomir Surdilovic 2010-11-02 03:02:28 UTC
Here is this comment: 
Rule from BRMS-935:

rule "0701 - Guar Date Invalid"
dialect "mvel"
when
   ContinueProcessing( continueProcess == ( true ) )
   // it 's work if change to ->   cp : ContinueProcessing( eval( cp.isContinueProcess() )
   ( LenderLoan( eval( isBlank(guaranteeDate) )) or LenderLoan( eval( isStringDateNotAfter(guaranteeDate, "19651107") )) )
then
   LMError fact0 = new LMError();
   fact0.setCode( "0701" );
   insert(fact0 );
   System.out.println("Fired Rule: " + drools.getRule().getName());
end

where isBlank and isStringDateNotAfter are functions defined in the drl.

Alessandro, this particular very unusual use of evals + or in the LHS pattern does expose an issue with the evaluation of the pattern in Drools. To fix the issue will require a big amount of time as it's not something easy to fix. This again is very unusual pattern, and if this was something common, it would have been exposed imo very long time ago.

The pattern written by the customer is not a good one. First of all, functions are designed to be used in the RHS of the rules. Currently, yes the only way
to call functions in the LHS is to call them inside an eval ( ... ) as customer is doing. This is something we are working on on changing in the future. In order to use "reusable functions" in the LHS you could have them as part of the fact model (or some global for example) or the better way would be to write a custom operator (see http://blog.athico.com/2010/06/creating-pluggable-oprators.html for example). Writing custom operators for isBlank and isStringDatNotAfter would be the ideal way for the customer to do, however writing these operators is not very easy, but the good thing is that we can help them write them if needed. 

Again, the use of eval in general should be avoided where possible and I think in this case it is indeed possible and should be done. Statements inside eval are not 
executed by the engine, but Java code. As such the engine cannot perform any optimizations or indexing. Using eval also in some cases leads to bad design of rules, for example to not having to properly notify the engine of changing object properties (modify blocks). 

So IMO the way to approach this customer issue is to explain to them that they have hit a very rare case where drools does fail to evaluate the LHS, however this case is so uncommon, as well as badly designed that in their best interest is not for us to give them a patch which makes their rule work (even though this is the long-term goal and I'll work on it), however for us to help them redesign their pattern given the info above and help them write the custom operators in the case they cannot figure it out right away.



Comment 9 Alessandro Lazarotti 2010-11-03 12:13:50 UTC
Understood! Thanks Tiho

Comment 10 Tihomir Surdilovic 2011-06-02 21:43:04 UTC
Release Notes Text: Added: resolved in 5.2


Comment 11 lcarlon 2011-08-24 04:06:45 UTC
Hi Tihomir,

For the release notes, how was this resolved?

Thanks

Comment 12 lcarlon 2011-08-31 04:44:40 UTC
    Technical note added. If any revisions are required, please edit the "Technical Notes" field
    accordingly. All revisions will be proofread by the Engineering Content Services team.
    
    New Contents:
https://bugzilla.redhat.com/show_bug.cgi?id=724450

When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception is thrown.

Comment 13 lcarlon 2011-09-15 04:43:41 UTC
    Technical note updated. If any revisions are required, please edit the "Technical Notes" field
    accordingly. All revisions will be proofread by the Engineering Content Services team.
    
    Diffed Contents:
@@ -1,3 +1 @@
-https://bugzilla.redhat.com/show_bug.cgi?id=724450
+When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception is thrown. This issue has been resolved.-
-When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception is thrown.

Comment 14 lcarlon 2011-09-21 04:31:24 UTC
    Technical note updated. If any revisions are required, please edit the "Technical Notes" field
    accordingly. All revisions will be proofread by the Engineering Content Services team.
    
    Diffed Contents:
@@ -1 +1 @@
-When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception is thrown. This issue has been resolved.+<remark>Need info from Tihomir - How was this issue resolved?</remark></para><para>When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception is thrown.

Comment 15 lcarlon 2011-09-23 03:40:14 UTC
    Technical note updated. If any revisions are required, please edit the "Technical Notes" field
    accordingly. All revisions will be proofread by the Engineering Content Services team.
    
    Diffed Contents:
@@ -1 +1 @@
-<remark>Need info from Tihomir - How was this issue resolved?</remark></para><para>When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception is thrown.+<remark>Need info from Tihomir - Was anything changed for this one or is the resolution that we helped the customer redesign their rules? Thanks.</remark></para><para>When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception was thrown.

Comment 16 lcarlon 2011-10-10 03:21:48 UTC
    Technical note updated. If any revisions are required, please edit the "Technical Notes" field
    accordingly. All revisions will be proofread by the Engineering Content Services team.
    
    Diffed Contents:
@@ -1 +1 @@
-<remark>Need info from Tihomir - Was anything changed for this one or is the resolution that we helped the customer redesign their rules? Thanks.</remark></para><para>When using the conditional 'or' element in the left handside of rule with a return value restriction the NullPointerException exception was thrown.+<remark>Need info from Tihomir - Was anything changed for this one or is the resolution that we helped the customer redesign their rules? Thanks.</remark></para><para>When using the conditional 'or' element in the left handside of rules with a return value restriction the NullPointerException exception was thrown.