I am using Drools 6.3.0 Final. Assuming I have a rule like this
rule "Child of Person over 18"
when
$person : Person(age > 18)
$child : from $person.children
then
end
Let us further assume I build my KieSession with this rule, add some facts and now I want to know the identifiers used in all rules / in all rules that matched my facts.
So what I want to get here is $person and $child.
I know I can get the rules, which fired using an AgendaEventListener and from the event I can get the name of the rule, as well as the objects for $person and $child. But I fail to find a way to get the identifiers $person and $child from my match. Using a debugger I can see the information is there... actually the Rule I get from the event, is a RuleImpl, which has a lhsRoot, in which I can find that information... but this sounds much more complicated than it should be and not the intended way.
So I was wondering if there is not a better way for this.
Your requirement can be easily achieved by using Drools' public API. You were looking at the right place (AgendaEventListener) but Match.getObjects() is not what you need. What you need is a combination of Match.getDeclarationIds() (to get the list of identifiers) and then Match.getDeclarationValue(String id) (to get the value for each identifier). As an example, this is how an AgendaEventListener that logs this information in the console will look like:
import org.kie.api.event.rule.BeforeMatchFiredEvent;
import org.kie.api.event.rule.DefaultAgendaEventListener;
...
ksession.addEventListener(new DefaultAgendaEventListener() {
#Override
public void beforeMatchFired(BeforeMatchFiredEvent event) {
String ruleName = event.getMatch().getRule().getName();
List<String> declarationIds = event.getMatch().getDeclarationIds();
System.out.println("\n\n\tRule: "+ruleName);
for (String declarationId : declarationIds) {
Object declarationValue = event.getMatch().getDeclarationValue(declarationId);
System.out.println("\t\tId: "+declarationId);
System.out.println("\t\tValue: "+declarationValue);
}
System.out.println("\n\n");
}
});
As #laune mentioned,you can also get an instance of the match that activated a rule in the RHS of the rules themselves. In this case, the Match object is accessible through drools.getMatch().
Hope it helps,
Related
I am using Drools version 6.5.0.Final and I have a simple rule like:
rule "Test Rule"
when
$obj : MyObject(testValue == null || testValue != "NEW")
then
$obj.testValue = "NEW";
update($obj);
end
So I also implemented a RuleRuntimeEventListener class and it gets invoked when this rule is executed. The problem is the passed in ObjectUpdatedEvent instance's getObject() and getOldObject() calls return the same exact object - which is the object after the update. So for this the getOldObject() call returns an object that has a value of "NEW" for the testValue property. I am expecting getOldObject to return me the value of testValue that isn't "NEW" which is after it was changed by the rule.
This seems like a bug to me and was wondering if anybody had any similar issues with this or if there is any workaround? I want to be notified when any property of an object is changed by a rule and what the old and new values were.
I see that there is support for using the java bean's PropertyChangeListener, but that looks like I have to create the fact objects with a addPropertyChangeListener and removePropertyChangeListener methods and also in all my fact's setter methods I have to use PropertyChangeSupport and fire a property change event via firePropertyChange method. I really don't want to have to do that.
Thanks
This seems to be a bug in Drools, the issue is also present in 7.16.0.FINAL. By doing the following
System.out.println(getOldObject() == getObject());
You will get an output of "true" indicating that both objects are of the same reference.
I just raised it as a bug on the Drools site
https://issues.jboss.org/browse/DROOLS-4305
public class Differ implements AgendaEventListener {
private List<Object> oldFacts;
public void beforeMatchFired(BeforeMatchFiredEvent event){
List<Object> facts = event.getMatch().getObjects();
// copy (!) facts into oldFacts
}
public void afterMatchFired(AfterMatchFiredEvent event){
List<Object> facts = event.getMatch().getObjects();
// compare facts with oldFacts
}
//...
}
You may want to be more selective in copying and comparing. Also, a generic way of comparing using the stringified object contents might be considered.
BeforeMatchFiredEvent
I have two Facts named OptionalCover and OptionalPremiumComponent and OptionalCover has a reference of OptionalPremiumComponent in it. So this is what I'm inserting into working memory.
private static OptionalCover getOptionalCover(Double sumAssured, Double premiumRate) {
OptionalPremiumComponent premiumComponent = new OptionalPremiumComponent();
premiumComponent.setSumAssured(sumAssured);
premiumComponent.setPremiumRate(premiumRate);
OptionalCover optionalCover = new OptionalCover();
optionalCover.setPremiumComponent(premiumComponent);
return optionalCover;
}
kieSession.insert(getOptionalCover(1000000.0, 0.02));
I have created the following rule in drools
import java.lang.Number;
rule "OptionalCoverCalculation"
dialect "java"
when
opc : OptionalPremiumComponent( sumAssured > 1I && sumAssured != null && premiumRate != null && premiumRate > 0.0 )
then
opc.setPremium( opc.getSumAssured() * 0.001 * opc.getPremiumRate() );
System.out.println("Here");
end
The problem is, the above rule is not being fired when I insert the parent object. Do I have to do anything else to enable this behaviour? Is it supported?
Thank you.
The Drools engine has no way of telling that your Cover contains a Component. (Well, it has, as it might use reflection - but where should it stop?)
So, you'll have to insert the OptionalPremiumComponent as well.
To reduce the amount of clutter in your code you might write a sorts of clever methods so that you can insert Cover and Component with a single call.
For instance, if you have many similar "contains" relationships and if you want to reason freely, you might implement s.th. like
interface FactContainer {
List<Object> factChildren(); -- return all contained fact objects
-- OR --
void insertAll( KieSession ks );
}
where factChildren would return a List with the premiumComponent or an empty List, or, alternatively, one method insertAll that handles everything internally.
I am still trying to wrap my head around some basics of drools.
One thing I don't understand is to what level I have to explicitly add facts into a session vs. declaring something as a fact or (how is it different?) as a variable within a rule.
Question: Do I always have to explicitly add a fact or does drools see a declared variable somehow as a fact as well?
Say I have this simplified hiearchy:
public class Container {
private Collection<Element> elements;
// other stuff
}
public class Element {
private SubElement subElement;
// other stuff
}
public class SubElement {
private code;
// other stuff
}
Now I want to find/match a container that contains Elements with a SubElement of a given code.
This is a rule I created:
global Collection qualifyingCodes;
rule "container rule"
when
// find a container
$container : Container( $elements : elements )
// where there is an element within its elements list
$element : Element(this memberOf $elements)
// that has a sub element
$subElement : SubElement(this == $element.subElement)
// for which the code is in the (global) list of codes
eval(qualifyingCodes.contains($subElement.code))
then
....
Now it seems to work fine, but only if I add the container, the elements and subelements separately as facts into my session.
I was hoping to be able to just add the container object as a fact and due to the "declared" $elements/$element/$subElement drools would understand those as facts as well.
Is there a way to do this (or do I always have to flatten out my data structures and add those as separate facts)?
Thanks for any advice!
POJOs inserted as facts can be matched with a pattern without the need for establishing a context (as your rule illustrates very well).
It is, however, possible to create a context where a limited set of POJOs that may or may not have been inserted as facts can be matched by a pattern: via the from phrase. Here's essentially the same rule, but without Element and SubElement being facts:
rule "from rule"
when
// find a container
$container : Container( $elements : elements )
// where there is an element within its elements list
$element : Element($subEl: subElement ) from $elements
// that has a sub element
$subElement : SubElement(this == $element.subElement, data == "DEF")
from $subEl;
then
System.out.println( "found Container " + $container +
" for " + $subElement.getData());
end
But, besides being visible in a general context, insertion as a fact has another consequence as well: facts can be modified with the engine being aware of that modification. In the "from rule", a modify of $subElement isn't possible, and so any modification setting that SubElement's data to "DEF" will not make this rule fire (not even if you insert the SubElement POJO).
For completeness sake: reasoning with a hierarchy of objects (composition) is also simplified by adding the references to the parent object, e.g.
rule "linkage rule"
when
// a sub element GHI
$subElement : SubElement( data == "GHI", $element: element)
then
System.out.println( "found Container " + $element.getContainer() +
" for " + $subElement.getData());
end
I want to create specific Object according to the type argument.
Pseudo code looks like this.
sub new {
my $type = shift;
if($type eq "S1") {$interface = X->new(); }
if($type eq "S2") {$interface = Y->new(); }
etc...
return $interface;
}
Options might be:
Substitute "package" name with $type argument. Requires package name coordination with $type.
Use Hash{S1 => X} in the Master constructor to select Value according to $type passed. Requires Hash maintenance when adding new
Object types.
I don't like any of above. Looking trully polimorphic way to accomplish that.
Thank You,
k
Your best option would likely be to use a factory pattern. A factory method takes the parameters for creating an instance of your class, then decides which object to instantiate and return from that. This can also make dependency injection easier for testing.
You'd probably be looking at something like this (in Java-esque code), with an employee object:
public class EmployeeFactory
{
public static create(String type)
{
switch (type) {
case type1:
return new EmployeeTypeOne();
case type2:
return new EmployeeTypeTwo();
default:
throw new Exception("Unrecognized type");
}
}
}
Your employees would inherit from a common interface or abstract class. You can use the factory to handle constructor parameters as well if you prefer, just try to keep things fairly reasonable (don't pass a million parameters - the factory should internally handle complex objects)
See http://refactoring.com/catalog/replaceConstructorWithFactoryMethod.html for more information.
You might like Module::PluginFinder for that. Create all your specific types in a specific namespace and give them each some identifying (constant? sub?) that the main dispatcher will then use to identify which class handles a given type.
As stated in title I'd like to change conflict resolver in my Drools project. I found following snippet on this site
ConflictResolver[] conflictResolvers =
new ConflictResolver[] { SalienceConflictResolver.getInstance( ),
FifoConflictResolver.getInstance( ) };
RuleBase ruleBase = java.io.RuleBaseLoader( url, CompositeConflitResolver( conflictResolver));
However it lacks of information where to put it and what sholud be url parameter.
Thank you in advance for any help.
As the documentation says:
Drools 4.0 supported custom conflict resolution strategies; while this
capability still exists in Drools it has not yet been exposed to the
end user via knowledge-api in Drools 5.0.
http://docs.jboss.org/drools/release/5.2.0.Final/drools-expert-docs/html/ch04.html
So if you are using Drools 5+ you will not be able to change conflict resolver unless you do some reflection magic. The Conflict Resolver is settled inside the Agenda object of the StatefulKnowledgeSession object. You can see this by using debugger (it's the content of Agenda object):
To replace ConflictResolver, first you need instance of StatefulKnowledgeSession (which will be ksession in the following snippet). Then you need to extract some nested private fields and after that you can replace field value with instance of i.e RandomConflictResolver. Full code:
Agenda agenda = ksession.getAgenda();
Field agendaField = agenda.getClass().getDeclaredField("agenda");
agendaField.setAccessible(true);
Object agendaObject = agendaField.get(agenda);
Field mainField = agendaObject.getClass().getDeclaredField("main");
mainField.setAccessible(true);
Object mainObject = mainField.get(agendaObject);
Field queueField = mainObject.getClass().getDeclaredField("queue");
queueField.setAccessible(true);
Object queueObject = queueField.get(mainObject);
Field comparatorField = queueObject.getClass().getDeclaredField("comparator");
comparatorField.setAccessible(true);
Object comparator = comparatorField.get(queueObject);
ConflictResolver randomResolver = org.drools.conflict.RandomConflictResolver.getInstance();
comparatorField.set(queueObject, randomResolver);
Based on : documentation and debugger session.
To demonstrate that the order of firings does not affect the overall outcome I'd use an AgendaFilter. I'm sketching the outline using 6.x, but this API hasn't changed since 5.x.
KieBase kieBase = ...;
Collection<KiePackage> packages = kieBase.getKiePackages();
List<Rule> rules = new ArrayList<>();
for( KiePackage p: packages ){
rules.addAll( p.getRules() );
}
Now you have all the rules. Build this simple filter that accepts a single rule:
class Selector implements AgendaFilter {
List<Rule> rules;
int ruleIndex;
Selector( List<Rule> rules ){
this.rules = rules;
}
void setRuleIndex( int value ){ this.ruleIndex = value; }
int getRulesSize(){ return rules.size(); }
boolean accept(Match match){
return match.getRule().equals( rules.get( ruleIndex ) );
}
}
Instantiate:
Selector selector = newSelector( rules );
You can now execute all rules that are activated (but see below):
for( int i = 0; i < selector.getRulesSize(); ++i ){
int fired = kieSession.fireAllRules( selector, i );
}
Any other permutation of 0..size-1 may produce another sequence of firings. You may do this systematically for a small number of rules, or you may use some random permutations.
A more efficient test would keep track of the Match data passed to the filter in the first run and use only these for successive executions.
Caution The approach outlined so far does not consider changes in Working Memory. It would be possibe for rule n to become activated when some rule n+k is fired. If you do have changes of Working Memory, you will have to
do {
int sumf = 0;
for( int i : somePermutation ){
int fired = kieSession.fireAllRules( selector, i );
sumf += fired;
}
} while( sumf > 0 );
I have never done a test like this. It seems that getting the correct result by depending on the innate order of rule firings is extremely rare, in contrast to getting all sorts of wrong results from this order.
Note Other permutations of the rule firing order are possible by changing the order of rules in the DRL (or in their packages?) or by changing the order of fact insertions into working memory. For certain scenarios, even this may provide enough test cases for showing what you intend.