I am using arrayList in .drl rule file.
In one rule i am checking list is null or not and setting setFocus(2nd rule) .
in 2nd rule i am getting the element from list, bt in this rule i am getting list is null error.
I want to check list is null or not and getting particular element from that arraylist in one rule.
rule "Rule chesks client had already received Notifications or Not"
salience 10
no-loop true
when
event : Event($listOfClientNotifications : clientNotifications)
eval($listOfClientNotifications < 1)
then
event.setMessage("list is null");
end
2nd rule:
rule "Rule chesks "
salience 05
no-loop true
when
event : Event($listOfClientNotifications : clientNotifications)
value : ClientNotifications() from $listOfClientNotifications; // <<< !!!
then
event.setMessage("Value "+**value.getMessage()**);
end
This <<< !!! is where the null error occurs.
Testing a list (correctly!) against null in one rule doesn't avoid running into a NPE in another rule. Rule evaluations are not, repeat not, synchronized with rule executions, salience and activations groups notwithstanding.
This is a correct check:
rule "check list is null"
when
event : Event(clientNotifications == null)
then
event.setMessage("list is null");
end
Guard against running into NPE
rule "use notifications from list"
when
event : Event($listOfClientNotifications : clientNotifications != null)
value : ClientNotifications() from $listOfClientNotifications;
then
event.setMessage("Value "+ value.getMessage()); // strange, but maybe?
end
Related
I'm working on writing a rule set in Drools and the following situation causes an infinite loop by retriggering the rule:
rule "My Rule"
when
a1:ObjectA()
b1:ObjectB(field1 > 0 || a1.field1 in (1,2,3))
ObjectB(field2 > 10)
then
modify( b1 ) { setField3(5) };
end
The following rule change doesn't result in an infinite loop, i.e., when a1 is no longer referenced within ObjectB:
rule "My Rule"
when
b1:ObjectB(field1 > 0 || field4 in (1,2,3))
ObjectB(field2 > 10)
then
modify( b1 ) { setField3(5) };
end
Another scenario which doesn't cause an infinite loop is when I change the || to an && in the second when line:
rule "My Rule"
when
a1:ObjectA()
b1:ObjectB(field1 > 0 && a1.field1 in (1,2,3))
ObjectB(field2 > 10)
then
modify( b1 ) { setField3(5) };
end
From the Drools docs I understand that calling modify(){} will "trigger a revaluation of all patterns of the matching object type in the knowledge base," but since the field I'm modifying, field3, isn't used in the LHS conditions, I didn't think it should reevaluate. However, I am faily certain it has to do with referencing a1.field1 within ObjectB, but I can't find a concrete reason why. Thanks in advance!
It matters at the object level, not the field level. Since you modify a1, the rule is re-evaluated because it relies on the ObjectA objects in working memory. Note that the docs indicate it will "trigger a reevaluation of all patterns of the matching object type in the knowledge base." Not the parameter values.
One way you could avoid this would be to add a constraint like field3 != 5 on the left hand side. That is:
rule "My Rule"
when
a1: ObjectA( field3 != 5 ) // <-- if field3 is already 5, don't fire the rule
b1: ObjectB(field1 > 0 || a1.field1 in (1,2,3))
exists(ObjectA(field2 > 10))
then
modify( a1 ) { setField3(5) };
end
Basically you need to make the rule no longer eligible for re-fire.
Alternatively, depending on your structure you could try to use no-loop, but that only keeps the rule from refiring when immediately triggered by the right hand side. If you have multiple modifications/updates/etc going on, you could get into a "broader" looping scenario where multiple rules cause reevaluation. (Example: Rule A does a re-evaluate, no-loop keeps A from firing again; then B causes re-eval, so A can trigger because no-loop does not apply.)
I declared String variables in my grammar, vAttributeName and vClassName, and I assign them with the text values of the tokens : className and attributeName. When I use one of the variables that should normally contain a value in an error alternative so in another rule, it returns a null in the message ... Why do not my variables keep the values? How can I fix that?
grammar TestExpression;
#parser::members {
String vAttributeName;
String vClassName;
}
/* SYNTAX RULES */
textInput : classifierContext ;
classifierContext : 'context' c=className attributeContext {vClassName = $c.text;};
attributeContext : '::' a=attributeName ':' dataType initDefinition {vAttributeName = $a.text;};
initDefinition : 'init' ':' initExpression ;
initExpression : boolExpression
| decimalExpression
| dateTimeExpression
| .+? {notifyErrorListeners("Corriger - "l'attribut "+vAttributeName+" de l'entité "+vClassName+" ne correspond pas");}
I'm trying to parse an expression who describe a class with an attribute that has a false value.
And the message i expected was : "Corriger - l'attribut seat de l'entité Car ne correspond pas".
But the actual message was : "Corriger - l'attribut null de l'entité null ne correspond pas".
If you have a rule like a: b c {action}; and an input that matches that rule, events will happen in the following order:
The rule b is applied and its action is executed if it has one.
The rule c is applied and its action is executed if it has one.
The action action is executed and is able to access the results of the b and c rules (including anything set by their actions, which is why it's important that those have run first).
If you have a: b {action} c; instead, then the action will be executed after b, but before c (and will consequently be unable to access c's result).
So for your code that means that the action of initExpression is run before those of classifierContext and attributeContext and that's why the variables aren't set. You can fix the problem by moving the action, so that it happens directly after the needed part (i.e. className/attributeName) has been parsed.
You could also get rid of the variables altogether and instead get the desired information by getting it from your grandparent contexts.
I am trying to write a drools rule that check if two events happens from the same stream. I have a compliance rules class which contains logic (in the working memory) to be compared with events coming from entry point. all I need is to detect the occurrence of two events, for example I want to detect that event A occurred and after that B occurred. I wrote this role in drools syntax
$comrule : Comprules ( pattern == "response" , isBefore == false)
Event (task == $comrule.antecedent) from entry-point StoreOne
Event (task == $comrule.consequent) from entry-point StoreOne
the problem is this technique doesn't work. the only one working is when I wrote this
Event (task == $comrule.antecedent) from entry-point StoreOne
not Event (task == $comrule.consequent) from entry-point StoreOne
I read the drools documentation but I couldn't find any solve to this problem
any help will be appreciated
The typical pattern for checking that two Events occur in the right order is this:
Comprules( pattern == "response", !isBefore, $a: antecedent, $b: consequent )
$one: Event( task == $a ) from entry-point StoreOne
$two: Event( task == $b, this after $one ) from entry-point StoreOne
Using not tests for the absence of a fact, which would be the situation after $one has arrived while $two still is absent.
We are trying to map data using Talend DI tool. In that, we have to capture transformation which is related to Conditional operator.(Because of limitation of tool it won't allow if-then-else syntax instead it does support conditional operator.
Sample Data :
I am trying to write this expression into talend tmap component . How to write this expression into tmap component expression builder using ternary operator. Additional, i have to check for null values.
case when [TCode]='(00) PRE-PAID' then '00'when[TCode]='(01) C.O.D.' then '01'when[TCode]='(02) EOM' then '02'when[TCode]='10' then '(10) NET 10 DAYS'when[TCode]='15' then '(15) NET 15 DAYS'when[TCode]='21' then '(21) 2 % 30 NET 31'when[TCode]='23' then '(23) 2% NET 30 DAYS'when[TCode]='3' then '(3) CHECK'when[TCode]='30' then '(30) NET 30 DAYS' else [TCode]end as TCode
Tried this conditional operator :
"(00) PRE-PAID".equals(row.tCode) ?"00" :
"(01) C.O.D".equals(row.tCode) ?"01" :
"(02) EOM".equals(row.tCode) ? "02" :
"Unknown"
Getting error when tried above conditional operator :
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
XML_API_tXMLMap_1 cannot be resolved to a type
XML_API_tXMLMap_1 cannot be resolved to a type
Syntax error on token ""(00) PRE-PAID"", delete this token
Thanks in advance !
Analysis
According to the link in your comment, you try to execute the following:
row1.tcode==null?null:row1.tcode.length()==0?null:row1.tcode.toUpperCase()
"(00) PRE-PAID".equals(row.tCode) ?"00" :
"(01) C.O.D".equals(row.tCode) ?"01" :
"(02) EOM".equals(row.tCode) ? "02" :
"Unknown"
and you are getting the following error:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
XML_API_tXMLMap_1 cannot be resolved to a type
XML_API_tXMLMap_1 cannot be resolved to a type
Syntax error on token ""(00) PRE-PAID"", delete this token
NOTE: Java is a case sensitive language. So either tcode or tCode is correct, not both.
Explanation
You provided two separate rows of code and Java doesn't know how to interpret this.
Your first line of code is (usually ended with a ;):
row1.tcode==null?null:row1.tcode.length()==0?null:row1.tcode.toUpperCase()
and the second line (albeit it has more lines in itself it is seen as "one line") of code is:
"(00) PRE-PAID".equals(row.tCode) ?"00" :
"(01) C.O.D".equals(row.tCode) ?"01" :
"(02) EOM".equals(row.tCode) ? "02" :
"Unknown"
We need to put those two instructions together.
Solution
(row1.tCode != null && !row1.tCode.equals("")) ? (
"(00) PRE-PAID".equals(row.tCode.toUpperCase()) ? "00" :
"(01) C.O.D".equals(row.tCode.toUpperCase()) ? "01" :
"(02) EOM".equals(row.tCode.toUpperCase()) ? "02" :
"Unknown") : "Unknown"
Alternatively, to shorten the first row, you could set a Default value for tCode if that makes any sense. The default value could be "" and you don't have to check for null anymore. Again, this depends on your use case.
Using the rule below, I am attempting to match an Account using a rule with an OR logical condition. In this situation, I have a table of Accounts and a table of Insurance records for those accounts. Not all accounts have insurance records. The Hibernate DAO objects exist and there is an association from Account to Insurance. I am not observing expected behavior from this rule. In this situation, I would expect accounts 1, 2, 3, and 4 to match this rule, as the rule should match any Account with status "Inactive" or any account with an Insurance CURRENT_IND value of 'N'. However, only accounts 2 and 4 match the rule. An account will match the rule only if it has an Insurance record. I want all Account records with status = 'Inactive', regardless of their Insurance record's existence.
I am currently testing this with Drools 5.6.10.FINAL and Hibernate 3.6.0.
Is it possible to create such a rule using Hibernate with Drools? What is causing the rule to filter on Insurance records' existence?
package com.app.Testing
import com.app.abilities.RuleBuilder.EvalObject;
import com.app.dao.Insurance;
import com.app.dao.Account;
rule "Null Test"
when
$V1 : Account( )
$V2 : Insurance( ) from $V1.getInsurance()
$V3 : EvalObject(
$V2.getCurrentInd == "N" ||
$V1.getStatus == "Inactive"
)
then
reply.getReplyList().add("Null Test");
end
Example data:
Account:
ACCT_ID STATUS
1 Inactive
2 Inactive
3 Inactive
4 Active
5 Active
Insurance:
ID CURRENT_IND
2 N
4 N
5 Y
Accessing a readily available field of some fact using "from" is completeley unnecessary - you can access via a bound variable or a getter. This is what causes the problem: a null value can't be "frommed" into a pattern and so any Account with insurance == null will block further evaluation.
Moreover, I find the use of a completely fact (EvalObject) for writing a boolean rather baroque. A simple eval should do nicely, even if you have to use the oh-so-terrible Java syntax.
Altogether, the following rule fires on all four of your Accounts:
rule "Better Test"
when
$V1: Account( $sta: status, $ins: insurance )
eval( $sta.equals( "Inactive" ) ||
$ins != null && $ins.getCurrentInd().equals( "N" ) )
then
System.out.println("Better Test " + $V1.getId() );
end
Logic is, of course, a many splendoured thing and it will follow the tug of the leash if applied nicely. Thus, you could also use a couple of rules:
rule "Third Test A"
when
$V1: Account( $sta: status == "Inactive" )
then
System.out.println("Third Test A " + $V1.getId() );
end
rule "Third Test B"
when
$V1: Account( $sta: status == "Active" )
Insurance( currentInd == "N" ) from $V1.getInsurance()
then
System.out.println("Third Test B " + $V1.getId() );
end
"Ahh", I hear you say, "he's making me use two rules for what can be done with one? Duplicate the RHS code? Phooey!"
No, not me: this was just the bridge to the final. How about this:
rule "Fourth Test"
when
$V1: Account( $sta: status == "Inactive" )
or
($V1: Account( $sta: status == "Active" )
and
Insurance( currentInd == "N" ) from $V1.getInsurance()
)
then
System.out.println("Fourth Test " + $V1.getId() );
end
Edit If an account may be associated with any number of Insurance facts (i.e., getInsurance returns a Collection<Insurance>) the last three rules work just as fine. However, they will fire for each associated Insurance where currentInd == "N". To reduce this to a single activation, prefix the Insurance pattern with exists (or not for a somewhat different effect).
It is always worthwhile to consider entering dependant objects (Insurance) as facts as well. Also, a link from the child to the parent (Account) may improve matters.