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.
Related
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 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,
I'm having an object as below:
class License{
private field1;
private field2;
private boolean active;
private String activeMessage;
private boolean processed = false;
//Getter and setter methods
}
What I'm trying to do is, based on the values of field1, and field2, I need to set the isActive flag and a corresponding message. However, if either the rule for field1 or field2 is fired, I need to stop the rules processing. That is, I need to execute only 1 successful rule.
I read on a post that doing ksession.fireAllRules(1) will solve this. But the fireAllRules() method is not available in Drools 6. I also tried putting a return; statement at the end of each rule. That didn't help me either.
Finally, I ended up adding an additional field to my object called processed. So whenever I execute any rule, I set the processed flag to true. And if the flag is already set, then I do not execute any rule. This is my rules file:
rule "Check field1"
when
$obj : License(getField1() == "abc" && isProcessed() == false)
then
System.out.println("isProcessed >>>>>> "+$obj.isProcessed());
$obj.setActive(true);
$order.setActiveMessage("...");
$order.setProcessed(true);
end
rule "Check field2"
when
$obj : License(getField2() == "def" && isProcessed() == false)
then
System.out.println("isProcessed >>>>>> "+$obj.isProcessed());
$obj.setActive(true);
$order.setActiveMessage("...");
$order.setProcessed(true);
end
However, I see that even now both my rules are being fired. When I try to print the value of isProcessed(), it says true, even though I enter the rule only if isProcessed() is false.
This is how I'm calling the drools engine:
kieService = KieServices.Factory.get();
kContainer = kieService.getKieClasspathContainer();
kSession = kContainer.newStatelessKieSession();
kSession.execute(licenseObj);
It is not just 2 rules, I have a lot of rules, so controlling the rules execution by changing the order of the rules in the drl file is not an option. What is happening here? How can I solve this problem? I am sort of new to Drools, so I might be missing something here.
Thanks.
Your question contains a number of errors.
It is definitely not true that fireAllRules has disappeared in Drools 6. You might have looked at the javadoc index, to find four (4!) overloaded versions of this method in package org.kie.api.runtime.rule in the interface StatefulRuleSession.
You might easily avoid the problem of firing just one out of two rules by combining the triggering constraint:
rule "Check field1 and field2"
when
$lic: License(getField1() == "abc" || getField2() == "def" )
//...
then
$lic.setXxx(...);
end
You complain that both of your rules fire in spite of setting the processed flag in the fact. Here you are missing a fundamental point (which is covered in the Drools reference manual), i.e., the necessity of notifying the Engine whenever you change fact data. You should have used modify on the right hand side of your rules.
But even that would not have been good enough. Whenever an update is made due to some properties, a constraint should be added to avoid running the update over and over again. You might have written:
rule "Check field1 and field2"
when
$lic: License(getField1() == "abc" || getField2() == "def",
! active )
//...
then
modify( $lic ){ setActive( true ) }
end
You might even write this in two distinct rules, one for each field, and only one of these rules will fire...
I am currently using ReSharper V8.1. I've only recently began using ReSharper and have found some interest in their LiveTemplate Macros. I've conjured up a solution to return a list of HotspotItems from a constant, similar to ReSharper's predefined macro "Comma-delimited list of values". In the method I take the constant variable of the template parameter and do a split string on them to provide a collection of HotSpotItems. Unfortunately it doesn't work if I use the macro more than one time within a template. Below is an extreme hack job showing my implementation of the method HotspotItems of IMacroImplementation.
I am hoping that someone out there may have done some work in this area and could possibly provide an example of how they've implemented IMacroImplementation which provides a list of items from a constant and also allows for multiple uses within a single template.
Thank you.
public override HotspotItems GetLookupItems(IHotspotContext context)
{
HotspotItems hotSpotItems = null;
foreach (var hotspot in context.HotspotSession.Hotspots)
{
if (hotspot.Expression != null && ((MacroCallExpressionNew)hotspot.Expression).Definition is Macros.DisplayMultipleItems)
{
//hotspot.CurrentValue
var multiItems = ((MacroCallExpressionNew) hotspot.Expression).Definition as DisplayMultipleItems;
if (!multiItems.ItemSet)
{
var expression = hotspot.Expression as MacroCallExpressionNew;
IMacroParameterValueNew baseValue = expression.Parameters[0].GetValue(context.SessionContext.Solution.GetLifetime(), context.HotspotSession);
string templateValue = baseValue.GetValue();
multiItems.ItemSet = true;
if (!string.IsNullOrEmpty(templateValue) && templateValue.Split(',').Any())
{
var lookupItems = templateValue.Split(',').Select(param => new TextLookupItem(param)).Cast<ILookupItem>().ToList();
if (hotSpotItems == null)
hotSpotItems = new HotspotItems(lookupItems);
else
{
foreach (var item in lookupItems)
{
hotSpotItems.Items.Add(item);
}
}
}
}
}
}
return hotSpotItems;
}
You should fire up dotPeek and point it to the ReSharper bin directory and take a look at ListMacroDef and ListMacroImpl, which is the implementation for the comma-delimited list macro.
The definition derives from SimpleMacroDefinition. It gets given the parameters in the call to GetPlaceholder, looks at the first and splits it by comma, returning the first item as the placeholder.
ListMacroImpl is just as simple. Its constructor has an [Optional] parameter of type MacroParameterValueCollection. This is the list of parameter values specified in the hotspot editor. You'll want to check for null and take the first parameter, which will be your delimited list. It then overrides GetLookupItems and returns HotspotItems.Empty if the parameter value is null, or parses the value and returns a list of TextLookupItem.
You don't need to look at the session and list of hotspots - that will get you all hotspots in the session, when you're only interested in the current hotspot, and ReSharper will create a new IMacroImplementation for each hotspot and give you those values in your constructor.
I use protobuf-net and ProtoGen.exe to parse following .proto file (given by another project)
enum RGBFlags { FLAG_RED = 1; FLAG_GREEN = 2; FLAG_BLUE = 4; }
message SomeMessage {
// Values from RGBFlags only allowed
optional int32 flags = 2;
}
My fellow programmers in C++ don't care about type safety and treat flags field as a plain integer. I wanted to be more strict and try to avoid such code:
SomeMessage foo = new SomeMessage();
foo.flags = (int)RGBFlags.FLAG_BLUE | (int)RGBFlags.FLAG_GREEN;
I thought that I could use protbuf custom options to amend proto code and modify XSLT transform of ProtoGet to generate necessary `[Flags]' annotations.
extend google.protobuf.EnumOptions {
optional bool generate_bit_field = 60000;
}
enum RGBFlags {
option (generate_bit_field) = true;
FLAG_RED = 1; FLAG_GREEN = 2; FLAG_BLUE = 4;
}
message SomeMessage {
// Values from RGBFlags only allowed
optional int32 flags = 2;
}
Problem is that all custom options appear as uninterpreted_option in the temporary file in ProtoGen.
Any idea what I could do to get [Flags] annotations in my code?
Re flags; the raw protobuf spec doesn't really have support for composite enum values, so in some ways I understand why they are doing it that way. And sadly there is no such this as partial enum, so you can't add the [Flags] in a separate code file.
Re custom options; it is an excellent question, and support for custom options has been raised before. It is definitely something I'd like to add, but relative to other features it simply isn't a massively demanded item, so (due to limited resource) it has not (yet) been investigated fully.
Therefore, I don't have a great answer for you; that feature isn't really there much right now. You could hard-code that one scenario in your xslt (for your specific types). Or wait until I get around to it (I don't have a specific timescale). Or take a peek yourself.