Drools problem, why drools see just my first rule? - drools

I need help with drools. Problem is that when I run an application (which is otherwise done in the spring) and when I try to fire all rules,
only the first rule starts and the other rules cannot be started.
This is a my drl file with some my 3 rules.
package drools.medicine
import com.example.demo.model.Pacijent;
import com.example.demo.model.Lek;
import com.example.demo.model.SastojakLeka;
import com.example.demo.model.Pregled;
import com.example.demo.model.ValidacijaDiagnoze;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
rule "test-rule"
agenda-group "alergija"
when
$vd: ValidacijaDiagnoze()
then
System.out.println("TEEEST!!!");
end
rule "Alergic-on-medicine"
agenda-group "alergija"
when
$v: ValidacijaDiagnoze()
$pregled: Pregled($prepisaniLek: prepisanLek)
$lek: Lek() from $prepisaniLek
$pacijent: Pacijent(alergicanNaLek($lek))
then
System.out.println("Pacijent je alergican na lek: "+ $lek.getNazivLeka());
modify( $v ){
setValid(false),
setMessage("PATIENT IS ALLERGIC!")
}
end
rule "Alergic-on-ingredient"
agenda-group "alergija"
lock-on-active
when
$v: ValidacijaDiagnoze()
$pregled: Pregled($prepisaniLek: prepisanLek)
$lek: Lek($sastojci: sastojak) from $prepisaniLek
$sastojak: SastojakLeka() from $sastojci
$pacijent: Pacijent(alergicanNaSastojak($sastojak))
then
System.out.println("Pacijent je alergican na sastojak " + $sastojak.getNazivSastojka());
modify( $v ){
setValid(false),
setMessage("PATIENT IS ALLERGIC ON INGREDIENT!")
}
end
Below is where i call the kieSession to fire all rules.
// Take medicine(and ingredient) and decide throw rules are they ok or not.
#CrossOrigin(origins = "http://localhost:3000")
#PostMapping(value = "/validation")
public ResponseEntity<?> validateDiagnosis(#RequestBody PregledDto pregledDto){
//TO DO--
KieSession kieSession = kieContainer.getKieBase("MedicineBase").newKieSession();
//initial state, witch can be changed in rules if patient is allergic
ValidacijaDiagnoze vd = new ValidacijaDiagnoze();
vd.setMessage("PATIENT IS NOT ALLERGIC");
vd.setValid(true);
FactHandle fact;
fact = kieSession.insert(vd);
kieSession.getAgenda().getAgendaGroup("alergija").setFocus();
System.out.println("Number of fired rules: "+ kieSession.fireAllRules()); // 1
kieSession.dispose();
return new ResponseEntity<>(vd, HttpStatus.OK);
}
And there is my kmodule file in META-INF folder.
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<kbase name="DiagnosisBase" packages="drools.diagnosis">
<ksession name="diagnosis" />
</kbase>
<kbase name="DiagnosisBase2" packages="drools.diagnosis2">
<ksession name="diagnosis2" />
</kbase>
<kbase name="MedicineBase" packages="drools.medicine">
<ksession name="medicine" />
</kbase>
</kmodule>
Help me to solve problem I am new in using drools.
Also let me know where I am making mistakes and what are the best practices when using drools, thank you very much.

The number returned by the method kieSession.fireAllRules() is the number of rules that actually matched and were executed by the Drools engine. If a rule's left hand side (the 'when' clause) does not match the inputs, it does not match, and it will not be executed.
In the example rules given, the test rule checks only for the presence of a ValidacijaDiagnoze in working memory. The other two rules require that the ValidacijaDiagnoze includes children (a Lek instance or SastojakLeka instance) as well as an instance of Pacijent also in working memory.
However, in the code that fires the rules, only a ValidacijaDiagnoze is added to the working memory like this:
ValidacijaDiagnoze vd = new ValidacijaDiagnoze();
vd.setMessage("PATIENT IS NOT ALLERGIC");
vd.setValid(true);
Since this instance has no Lek or SastojakLeka associated with, it won't trigger the other rules. Additionally, since there is no Pacijent in the working memory, those other rules won't trigger either.
Therefore, since only the first rule can trigger, fireAllRules returns 1 because that is the number of rules that actually executed.

Related

Scala name mangling of private fields and JavaFX FXML injection

The following example and explanations are quite long, so here is the gist of my question: how to deal with scalac's name-mangling of private fields when using a framework which insists on performing field injection (on fields which really should stay private)?
I am writing an application in Scala, using ScalaFX/JavaFX and FXML. When you use FXML to define your views in JavaFX, objects defined in FXML (such as buttons and text fields) are injected into the controller by :
adding an fx:id property to the FXML elements
adding (usually private) fields to the controller, with the #FXML annotation and with field names matching the values of the fx:id properties defined in the FXML
when the FXMLoader instantiates the controller, it automatically injects the fx:id annotated elements into the matching #FXML annotated fields of the controller through reflexion
I'm not a big fan of field injection, but that's how FXML works. However, I've run into unexpected complications in Scala, due to field name mangling performed by the compiler in some circumstances...
Here is an example application :
test/TestApp.scala (nothing interesting, just needed to run the example)
package test
import javafx.application.Application
import javafx.fxml.FXMLLoader
import javafx.scene.{Scene, Parent}
import javafx.stage.Stage
object TestApp {
def main(args: Array[String]) {
Application.launch(classOf[TestApp], args: _*)
}
}
class TestApp extends Application {
override def start(primaryStage: Stage): Unit = {
val root: Parent = FXMLLoader.load(getClass.getResource("/test.fxml"))
val scene: Scene = new Scene(root, 200, 200)
primaryStage.setTitle("Test")
primaryStage.setScene(scene)
primaryStage.show()
}
}
test.fxml (the view)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="test.TestController">
<children>
<CheckBox fx:id="testCheckBox" mnemonicParsing="false" text="CheckBox"/>
<Button fx:id="testButton" mnemonicParsing="false" text="Button"/>
</children>
</VBox>
test/TestController.scala (the controller for the test.fxml view)
package test
import javafx.fxml.FXML
import javafx.scene.{control => jfxsc}
import scalafx.Includes._
class TestController {
#FXML private var testCheckBox: jfxsc.CheckBox = _
#FXML private var testButton: jfxsc.Button = _
def initialize(): Unit = {
println(s"testCheckBox=$testCheckBox")
println(s"testButton=$testButton")
testCheckBox.selected.onChange {
testButton.text = "changed"
}
}
}
When running the application, the println statements show that testCheckBox gets injected properly, but testButton is null. If I click on the checkbox, there is, as expected, a NullPointerException when calling testButton.text_=.
The reason is quite obvious when looking at the compiled classes :
There is a TestController$$anonfun$initialize$1 class, for the anonymous function passed to testCheckBox.selected.onChange() in the initialize() method
In the TestController class, there are two private fields : testCheckBox (as expected) and test$TestController$$testButton (rather than just testButton), and the accessor/mutator methods. Of those, only the accessor method for test$TestController$$testButton is public.
Clearly, the Scala compiler mangled the name of the testButton field because it had to make its accessor method public (to access it from TestController$$anonfun$initialize$1) and because the field and the accesor/mutator methods should keep the same name.
Now, finally, here is my question: is there a reasonable solution to deal with this situation? Right now, what I have done is make the fields public: since the compiler doesn't need to change their visibility, it won't mangle their name. However, those fields really have no business being public.
Note: Another solution would be to use the scala-fxml library, which completely hides the field injection, but I'd rather use bog-standard FXML loading for other reasons.
This is how I declare my injected fields:
#FXML
var popoutButton: Button = _
IOW, leave off the private and things work fine. Feels a bit dirty if you're coming from the Java world, but workarounds take a lot more code.
Controllers implied by fx:include also work fine:
FXML:
<fx:include fx:id="paramTable" source="ParameterTable.fxml" />
Scala:
#FXML
var paramTable: TableView[(ModelParameter, ParameterValue)] = _
#FXML
var paramTableController: ParameterTableController = _
You can annotate your fields with #BeanProperty to stop Scala from rewriting the field name. The only drawback seems to be that the field now has to be at least protected, or the Scala compiler complains.

Loading specific rules file based on some condition

I am new to Drools, so I apologize in advance if my question is very basic. Usually, if I have to load the rules file, I do something like this:
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
kSession = kContainer.newStatelessKieSession();`
But how can I change the rules file based on some condition? Say I have 3 classes (A, B, and C) which implement an interface (P), and I also have a function which takes an object of type P (myFunction(P p){...}).
Now, based on the type of the object passed to myFunction(), I need to load different .drl file. For instance, a.drl if object is instance of A, and b.drl if instance of B. How can I do this in drools 6?
The simplest approach I can think of would be to create multiple knowledge bases:
<kbase name="Kbase1" packages="rules.objectone">
<ksession name="Kbase1Session" />
</kbase>
<kbase name="Kbase2" packages="rules.objecttwo">
<ksession name="Kbase2Session" />
</kbase>
Then you can just write methods such as:
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
public void process(Object1 obj) {
kSession = kContainer.newStatelessKieSession("Kbase1Session");
kSession.insert(obj);
kSession.fireAllRules();
}
public void process(Object2 obj) {
kSession = kContainer.newStatelessKieSession("Kbase2Session");
kSession.insert(obj);
kSession.fireAllRules();
}
i.e. Use a different session for each object type.

Mule ESB: how to filter emails based on subject or sender?

I am new to Mule 3.3 and I am trying to use it to retrieve emails from a POP3 server and download the CSV attachments if the sender field and subject field contain certain keywords. I have used the example provided on Mulesoft website and I have successfully managed to scan my inbox for new emails and only download CSV attachments. However, I am now stuck because I can't figure out how to filter emails by subject and sender fields.
Doing some research I have come across a message-property-filter pattern tag that can be applied to an endpoint, but I am not sure exactly to which endpoint to apply it, incoming or outgoing. Neither approach seems to work and I can't find a decent example showing how to use this tag. The basic algorithm I want to implement is as follows:
if email is from "Bob"
if attachment of type "CSV"
then download CSV attachment
if email subject field contains "keyword"
if attachment of type CSV
then download CSV attachment
Here's the Mule xml I have so far:
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:file="http://www.mulesoft.org/schema/mule/file" xmlns:pop3s="http://www.mulesoft.org/schema/mule/pop3s" xmlns:pop3="http://www.mulesoft.org/schema/mule/pop3"
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:spring="http://www.springframework.org/schema/beans" version="CE-3.3.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/pop3s http://www.mulesoft.org/schema/mule/pop3s/current/mule-pop3s.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/pop3 http://www.mulesoft.org/schema/mule/pop3/current/mule-pop3.xsd ">
<expression-transformer expression="#[attachments-list:*.csv]"
name="returnAttachments" doc:name="Expression">
</expression-transformer>
<pop3s:connector name="POP3Connector"
checkFrequency="5000"
deleteReadMessages="false"
defaultProcessMessageAction="RECENT"
doc:name="POP3"
validateConnections="true">
</pop3s:connector>
<file:connector name="fileName" doc:name="File">
<file:expression-filename-parser />
</file:connector>
<flow name="incoming-orders" doc:name="incoming-orders">
<pop3s:inbound-endpoint user="my_username"
password="my_password"
host="pop.gmail.com"
port="995"
transformer-refs="returnAttachments"
doc:name="GetMail"
connector-ref="POP3Connector"
responseTimeout="10000"/>
<collection-splitter doc:name="Collection Splitter"/>
<echo-component doc:name="Echo"/>
<file:outbound-endpoint path="/attachments"
outputPattern="#[function:datestamp].csv"
doc:name="File" responseTimeout="10000">
<expression-transformer expression="payload.inputStream"/>
<message-property-filter pattern="from=(.*)(bob#email.com)(.*)" caseSensitive="false"/>
</file:outbound-endpoint>
</flow>
What is the best way to tackle this problem?
Thanks in advance.
To help you, here are two configuration bits:
The following filter accepts only messages where fromAddress is 'Bob' and where subject contains 'keyword':
<expression-filter
expression="#[message.inboundProperties.fromAddress == 'Bob' || message.inboundProperties.subject contains 'keyword']" />
The following transformer extracts all the attachments whose names end with '.csv':
<expression-transformer
expression="#[($.value in message.inboundAttachments.entrySet() if $.key ~= '.*\\.csv')]" />
Welcome to Mule! A few month ago I implemented a similar proejct for a customer. I take a look at your flow, let´s start refactoring.
Remove the transformer-refs="returnAttachments" from inbound-endpoint
Add the following elements to your flow
<pop3:inbound-endpoint ... />
<custom-filter class="com.benasmussen.mail.filter.RecipientFilter">
<spring:property name="regex" value=".*bob.bent#.*" />
</custom-filter>
<expression-transformer>
<return-argument expression="*.csv" evaluator="attachments-list" />
</expression-transformer>
<collection-splitter doc:name="Collection Splitter" />
Add my RecipientFilter as java class to your project. All messages will be discard if they don't match to the regex pattern.
package com.benasmussen.mail.filter;
import java.util.Collection;
import java.util.Set;
import java.util.regex.Pattern;
import org.mule.api.MuleMessage;
import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.routing.filter.Filter;
import org.mule.config.i18n.CoreMessages;
import org.mule.transport.email.MailProperties;
public class RecipientFilter implements Filter, Initialisable
{
private String regex;
private Pattern pattern;
public boolean accept(MuleMessage message)
{
String from = message.findPropertyInAnyScope(MailProperties.FROM_ADDRESS_PROPERTY, null);
return isMatch(from);
}
public void initialise() throws InitialisationException
{
if (regex == null)
{
throw new InitialisationException(CoreMessages.createStaticMessage("Property regex is not set"), this);
}
pattern = Pattern.compile(regex);
}
public boolean isMatch(String from)
{
return pattern.matcher(from).matches();
}
public void setRegex(String regex)
{
this.regex = regex;
}
}
The mule expression framework is powerful, but in some use cases I prefer my own business logic.
Improvment
Use application properties (mule-app.properties) > mule documentation
Documentation
MailProperties shows you all available message properties (EMail)
Take a look at the mule schema doc to see all available elements
Incoming payload (mails, etc) are transported by an DefaultMuleMessage (Payload, Properties, Attachments)

Drools Planner has trouble creating a configurer: how to debug

I tried to create a solver from XML configuration. But the entire process returns a cryptic error message that makes no sense.
How do I fix this? And how can I make sense of this to actually solve similar problems like this?
jesvin#Jesvin-Technovia:~/dev/drools/sudoku$ java App
Exception in thread "main" java.lang.NullPointerException
at org.drools.planner.core.domain.solution.SolutionDescriptor.processPropertyAnnotations(SolutionDescriptor.java:69)
at org.drools.planner.core.domain.solution.SolutionDescriptor.<init>(SolutionDescriptor.java:61)
at org.drools.planner.config.solver.SolverConfig.buildSolutionDescriptor(SolverConfig.java:197)
at org.drools.planner.config.solver.SolverConfig.buildSolver(SolverConfig.java:167)
at org.drools.planner.config.XmlSolverConfigurer.buildSolver(XmlSolverConfigurer.java:103)
at App.createSolver(App.java:62)
at App.main(App.java:40)
The function that throws it is listed here. The line is of course return configurer.buildSolver();.
private static Solver createSolver(){
XmlSolverConfigurer configurer = new XmlSolverConfigurer();
File file = new File("solver.xml");
FileInputStream fin = null;
try{
fin = new FileInputStream(file);
}
catch(IOException e){
System.out.println("Unable to read drl");
}
configurer.configure(fin);
//configurer.configure("/home/jesvin/dev/drools/sudoku/solver.xml");
return configurer.buildSolver();
}
The content of the XML:
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<environmentMode>DEBUG</environmentMode>
<solutionClass>domain.Sudoku</solutionClass>
<planningEntityClass>domain.Digit</planningEntityClass>
<scoreDrl>score.drl</scoreDrl>
<scoreDefinition>
<scoreDefinitionType>SIMPLE</scoreDefinitionType>
</scoreDefinition>
<termination>
<scoreAttained>0</scoreAttained>
</termination>
<!--
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
<constructionHeuristicPickEarlyType>FIRST_LAST_STEP_SCORE_EQUAL_OR_IMPROVING</constructionHeuristicPickEarlyType>
</constructionHeuristic> -->
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT</constructionHeuristicType>
<moveFactoryClass>solution.RowChangeMoveFactory</moveFactoryClass>
</selector>
<acceptor>
<completeSolutionTabuSize>1000</completeSolutionTabuSize>
</acceptor>
<forager>
<!-- Real world problems require to use of <minimalAcceptedSelection> -->
</forager>
</localSearch>
</solver>
OP's addition:
The issue was related to an inadvertent write-only property. There was setBlockList and getBlocklist (small 'l' in the getter), which was a typo. Drools complained because it detected two properties, one of which was write-only.
The other mismatch was isFixed and setFixed. It works for boolean built-in, but not for Boolean object.
I solved the issue in a mailing list post.
Cryptic error message is fixed in 5.4.0.Beta1 (already released): https://issues.jboss.org/browse/JBRULES-3247

Firing selective rules from drool file

Is it possible to fire rules in drool file by rule names ?
My requirement is, my rule file will contain list of all rules (S). But I've a separate config which contains list of rule names to be fired (A). Note (A) is a subset of (S). At run time, I want to fire only rules with names (A) from (S).
Thanks.
You can use AgendaFilters for this.
Here is how you set it:
StatelessSession session = ruleBase.newStatelessSesssion();
session.setAgendaFilter( new RuleNameMatches("<regexp to your rule name here>") );
This will allow only one rule with the specified name to fire.
In your case you will need to write your own AgendaFilter:
public class CustomAgendaFilter extends AgendaFilter{
private final Set<String> ruleNamesThatAreAllowedToFire;
public CustomAgendaFilter(Set<String> ruleNamesThatAreAllowedToFire){
this.ruleNamesThatAreAllowedToFire=ruleNamesThatAreAllowedToFire;
}
boolean accept(Activation activation){
return ruleNamesThatAreAllowedToFire.contains(activation.getRule().getName());
}
}