Setting and getting a Global variable in Drools - drools

I have something like this in drl file:
import java.lang.String
global String result;
rule ''Rule 1'' when some condition
then
result = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("Result", result); // I got an "Unexpected global" exception.
System.out.println("result = "+ Result);
Also, I don't know how to access this global variable from my MyService.java class.

I was trying to set a global variable from the drl file not my java class like Service class.
All I had to do was the following and it worked successfully
import java.lang.String
global String result;
rule ''Rule 1''
when
some condition
then
String grade = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("result", grade);
end
Also, the global variable name should match what I pass on the setGlobal("result",...).
And then get the global variable using the session I have in the Service class. like:
session.getGlobal("result");

Your rule should not be touching the 'kcontext'. What in the world are you trying to do? result = "PASS" is sufficient for setting the value of the global.
global String result
rule "Rule 1"
when
// some condition
then
result = "PASS";
end
Of course it's not going to work like you want it to because you need to change the value of the existing object; you can't overwrite it like that. Some options might be a "ResultsHolder" sort of class with a boolean variable you can set; or maybe even an AtomicBoolean that you can call set on.
To fire rules with a global, you need to add the global objects to the KieBase before invoking your rules:
var value = ...; // some OBJECT which you are going to pass in as a global
KieSession session = ruleBase.newStatefulSession();
session.insert(...); // insert data
session.setGlobal( "myGlobalFoo", value ); // sets the global; note the name must match the rule file!
session.fireAllRules();
After the rules are fired, you'll have your reference to value that you can use. This is also why you can't pass strings as globals and expect them to capture changes -- Java is pass-by-value, not pass-by-reference.
Here's an example for passing results out of the rules. This toy app will check the student's score on a test and then decide if they passed or failed.
Classes:
class Student {
private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
class Exam {
private String name;
private Double score;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public Double getScore() { return this.score; }
public void setScore(String score) { this.score = score; }
}
class ExamResults {
private List<String> results = new ArrayList<>();
public void logResults( String name, Double score, boolean passed ) {
this.results.add(name + " scored " + score + "%, which is a " + (passed ? "passing": "failing") + " grade.");
}
public List<String> getResults() { return this.results; }
}
Rule:
global ExamResults results;
rule "Evaluate exam"
when
Student( $name: name )
Exam ( $score: score, name == $name )
then
boolean passed = $score > 60.0;
results.logResults( $name, $score, passed );
end
Invocation:
List<Student> students = ...;
List<Exam> exams = ... ;
ExamResults results = new ExamResults();
KieSession session = ruleBase.newStatefulSession();
students.forEach( student -> session.insert(students) );
exams.forEach( exam -> session.insert(exam) );
session.setGlobal( "results", results);
session.fireAllRules();
// Print the results:
results.getResults().forEach(System.out::println);
If all you're trying to do is to get some data out of your rules (eg whether certain conditions match), I've written up answers about how to do that previously here and here. If you just want to know what rules triggered, you should write a listener which logs rule hits ("afterMatchFired").

Related

Drool Rules: Arithmetic Operations

I'm trying to write a simple rule. To perform a set of calculations. I understand that the calculations can be handled via functions. But, The business logic involves calculations.
package com.sample.rules
import com.sample.rules.Test
rule "Calculate"
when
Test(calculate == true)
then
finalValue = Test(InitialValue) + 10;
Test.setFinalValue(finalValue)
System.out.println("FinalValue=" + Test.getFinalValue();
end
The Class file with variable
public class Test {
private int finalValue;
private int initialValue;
private boolean calculate;
public int getFinalValue() {
return FinalValue;
}
public void setFinalValue(int FinalValue) {
this.FinalValue = FinalValue;
}
public int getInitialValue() {
return InitialValue;
}
public void setInitialValue(int InitialValue) {
this.InitialValue = InitialValue;
}
public boolean isCalculate() {
return calculate;
}
public void setCalculate(boolean calculate) {
this.calculate = calculate;
}
}
The App file is as shown below
public class CalculationApp {
public static void main(String[] args) {
System.out.println( "Bootstrapping the Rule Engine ..." );
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("ksession-rules");
Test test = new Test();
test.setInitialValue(20);
test.setCalculate(true);
kSession.insert(test);
int fired = kSession.fireAllRules();
System.out.println( "Number of Rules executed = " + fired );
}
}
The rule file throws an error :
Rule Compilation error finalValue cannot be resolved to a variable
Cannot make a static reference to the non-static method getInitialValue() from the type Test
finalValue cannot be resolved to a variable
Cannot make a static reference to the non-static method getFinalValue() from the type Test
While I found the answer by Trial and Error: I'm trying to understand the logic behind it. I mean, Assigning a variable (I understand variable has a different meaning) /assignment in the when condition is understandable, but the same the how does the Then part work. I mean, the item.setFinalValue(...) part.
Your rule has several problems, as indicated by the error.
Rule Compilation error finalValue cannot be resolved to a variable
finalValue cannot be resolved to a variable
The first problem is that you never declare finalValue, just attempt to assign to it on the right hand side. To fix this, simply declare finalValue. Assuming it's an integer:
int finalValue = ...
This is identical to how it works in Java.
However given what the original rule looks like, I think you're trying to pull the initial value from the Test object. To do that, you would do something like this:
rule "Calculate"
when
Test( calculate == true,
$initialValue: initialValue ) // assign the $initialValue variable
then
int finalValue = $initialValue + 10; // set finalValue by adding 10 to $initialValue
Cannot make a static reference to the non-static method getInitialValue() from the type Test
Cannot make a static reference to the non-static method getFinalValue() from the type Test
Both of these errors indicate that you're trying to call instance methods without an object instance. This is once again the same as in Java, where if you have an instance method you must call it against an instance.
Assuming these are methods on the Test class, you would change your rule to be:
package com.sample.rules
import com.sample.rules.Test
rule "Calculate"
when
$test : Test( calculate == true,
$initialValue: initialValue )
then
int finalValue = $initialValue + 10;
$test.setFinalValue(finalValue); // call against $test instance
System.out.println("FinalValue=" + $test.getFinalValue()); // you were also missing a parenthesis
end
What worked was :
rule "Calculate"
when
item: Test(calculate == true)
then
item.setFinalValue(item.getInitialValue() + 10)
System.out.println("FinalValue=" + item.getFinalValue();
end

How to return an object or struct from a method in MQL?

struct Person {
string FirstName;
string LastName;
};
class Builder {
public:
Person Builder::Build() {
Person person;
person.FirstName = "FirstName";
person.LastName = "LastName";
return person;
};
};
When I compile this it gives me the below error:
'return' - structure have objects and cannot be copied.
I just need to create a struct or class object and return it, I don't want to do any copying.
I tried using & and * combinations but didn't work. I tried with a class instead of struct and it didn't work either.
I also tried with class as shown below:
class Person {
public:
string FirstName;
string LastName;
};
class Builder {
public:
Person* Build() {
Person person;
person.FirstName = "FirstName";
person.LastName = "LastName";
return &person;
};
};
int OnInit()
{
Builder builder;
Person* person = builder.Build();
string firstName = person.FirstName;
return(INIT_SUCCEEDED);
}
And it gives me invalid pointer access when accessing person.FirstName in the OnInit() method at runtime.
Found the answer but how to avoid memory leak? how to destruct the object and its pointer after use?
class cPerson {
public:
string FirstName;
string LastName;
};
class cBuilder {
public:
cPerson* Build() {
cPerson* person = new cPerson();
person.FirstName = "firstname";
return person;
};
};
cBuilder builder;
cPerson* person = builder.Build();
string age = person.FirstName;
You can delete the object by delete(person); and if you are unsure the object is not a null, it is better to check if(CheckPointer(object)==POINTER_DYNAMIC)delete(object);
Overall you should have all such objects as variables with corresponding variable names, or keep them all in a collection and destroy the whole collection at end. You may also create global variable of object (before OnInit, not inside any function) and it is to be deleted at end of program.
Regarding the initial question - you cannot have string inside a struct, only primitives
you should NOT delete static objects - you will get "delete invalid pointer"-message from compiler in print area... therefore the check should be done as Daniel Kniaz answered... but as so as you are creating the object inside the wrapper - you'd better have a check for deletion & delete it in the Destructor of its wrapper (though I doubt, that you really should use here another class for CPerson creation - yuo can create its object in its - CPerson's - Constructor)
First you need a default constructor and copy constructor in your code. second you need to initialize the struct to default value, the struct only allocate space in the memory and not initialize it, so you can end up with cases where a long variable has some weird value like -1823834393939, so always set your struct to default values.
then when you return a class or struct from a function. the copy constructor will be called that copy the values. so if you don't want to return the exact object you've created in your class you don't need to return a reference
struct Person
{
string FirstName;
string LastName;
Person()
{
FirstName = "";
LastName = "";
}
Person(Person &that)
{
FirstName = that.FirstName;
LastName = that.LastName;
}
};
class Builder
{
public:
Person Builder::Build(string argFirstname, string argLastName)
{
Person person;
person.FirstName = argFirstname;
person.LastName = argLastName;
return person;
};
};
void OnStart()
{
Builder b;
Person p = b.Build("Mohammad Hossein", "amri");
Print(p.FirstName + " " + p.LastName);
}
the output will be

How to compare all properties values from a two equal classes

I've a class defined as follows:
Public Class DeviceConfig
Private _maxNumCodesGlobal As Integer
Private _maxNumCodesDataMatrix As Integer
Private _maxNumCodesQR As Integer
Private _maxNumCodesBarcode As Integer
Private _partialResults As String
Private _allowIdenticalSymbols As String
Private _datamatrixValidation As Integer
Private _datamatrixValidationType
'AND MUCH MORE PROPERTIES
'GETTERS & SETTERS
End Class
as you can see it's a long list of properties in this class.
I need to compare the values of the properties from an instance with the values of the properties of another instance.
Is there a way to iterate through all of them, or even better, just comparing both classes and get true/false if they have the same properties values or not?
if instance1=instance2 then true
Thank you
I encountered the same problem and created this method. Hopefully it will help you.
It uses reflections to iterate through the public fields, ignoring those with the JsonIgnore annotation.
This method is not considering fields as List, Set, etc.
You can change it to work for properties instead of fields.
protected <T> boolean equals(T object1, T object2) {
Field[] fields = object1.getClass().getFields();
for (Field field : fields) {
if (field.getAnnotation(JsonIgnore.class)!= null) continue; //do not check the fields with JsonIgnore
Object value1;
Object value2;
try {
value1 = field.get(object1);
value2 = field.get(object2);
} catch (Exception e) {
logger.error("Error comparing objects. Exception: " + e.getMessage());
return false;
}
//comparing
if (value1 == null) {
if (value2 != null)
return false;
} else if (!value1.equals(value2))
return false;
}
return true;
}

Get and Set attribute values of a class using aspectJ

I am using aspectj to add some field to a existing class and annotate it also.
I am using load time weaving .
Example :- I have a Class customer in which i am adding 3 string attributes. But my issues is that I have to set some values and get it also before my business call.
I am trying the below approach.
In my aj file i have added the below, my problem is in the Around pointcut , how do i get the attribute and set the attribute.
public String net.customers.PersonCustomer.getOfflineRiskCategory() {
return OfflineRiskCategory;
}
public void net.customers.PersonCustomer.setOfflineRiskCategory(String offlineRiskCategory) {
OfflineRiskCategory = offlineRiskCategory;
}
public String net.customers.PersonCustomer.getOnlineRiskCategory() {
return OnlineRiskCategory;
}
public void net.customers.PersonCustomer.setOnlineRiskCategory(String onlineRiskCategory) {
OnlineRiskCategory = onlineRiskCategory;
}
public String net.customers.PersonCustomer.getPersonCommercialStatus() {
return PersonCommercialStatus;
}
public void net.customers.PersonCustomer.setPersonCommercialStatus(String personCommercialStatus) {
PersonCommercialStatus = personCommercialStatus;
}
#Around("execution(* net.xxx.xxx.xxx.DataMigration.populateMap(..))")
public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
Object arguments[] = joinPoint.getArgs();
if (arguments != null) {
HashMap<String, String> hMap = (HashMap) arguments[0];
PersonCustomer cus = (PersonCustomer) arguments[1];
return joinPoint.proceed();
}
If anyone has ideas please let me know.
regards,
FT
First suggestion, I would avoid mixing code-style aspectj with annotation-style. Ie- instead of #Around, use around.
Second, instead of getting the arguments from the joinPoint, you should bind them in the pointcut:
Object around(Map map, PersonCustomer cust) :
execution(* net.xxx.xxx.xxx.DataMigration.populateMap(Map, PersonCustomer) && args(map, cust) {
...
return proceed(map, cust);
}
Now, to answer your question: you also need to use intertype declarations to add new fields to your class, so do something like this:
private String net.customers.PersonCustomer.OfflineRiskCategory;
private String net.customers.PersonCustomer.OnlineRiskCategory;
private String net.customers.PersonCustomer.PersonCommercialStatus;
Note that the private keyword here means private to the aspect, not to the class that you declare it on.

Writing methods and constructors

OK, i need someone to explain to me where to start on this project.
First I need to overload the constructor by adding a default (no-args) constructor to Person that defines an object to have the name "N/A" and an id of -1.
Then i need to add a setter method named reset that can be used to reset the two private instance variables of this class to two values passed in as parameters.
Then I need to add a getter method named getName and getId that can be used to retrieve these two private variables
Here is the code:
public class Person
{
private String name;
private int id;
private static int personCount = 0;
// constructor
public Person(String pname)
{
name = pname;
personCount++;
id = 100 + personCount;
}
public String toString()
{
return "name: " + name + " id: " + id
+ " (Person count: " + personCount + ")";
}
// static/class method
public static int getCount()
{
return personCount;
}
////////////////////////////////////////////////
public class StaticTest
{
public static void main(String args[])
{
Person tom = new Person("Tom Jones");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(tom);
System.out.println();
Person sue = new Person("Susan Top");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(sue);
System.out.println("sue.getCount(): " + sue.getCount());
System.out.println();
Person fred = new Person("Fred Shoe");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(fred);
System.out.println();
System.out.println("tom.getCount(): " + tom.getCount());
System.out.println("sue.getCount(): " + sue.getCount());
System.out.println("fred.getCount(): " + fred.getCount());
}
}
I'm not exactly sure where to start and I don't want just the answer. I'm looking for someone to explain this clearly.
First I need to overload the constructor by adding a default (no-args) constructor to Person that defines an object to have the name "N/A" and an id of -1.
Read about constructors here.
The Person class already contains a ctor that takes 1 argument. What you need to do is create a "default ctor" which is typically a ctor w/out any parameters.
Example:
class x
{
// ctor w/ parameter
//
x(int a)
{
// logic here
}
// default ctor (contains no parameter)
//
x()
{
// logic here
}
}
Then i need to add a setter method named reset that can be used to reset the two private instance variables of this class to two values passed in as parameters.
Setter methods are used to "encapsulate" member variables by "setting" their value via public function. See here.
Example:
class x
{
private int _number;
// Setter, used to set the value of '_number'
//
public void setNumber(int value)
{
_number = value;
}
}
Then I need to add a getter method named getName and getId that can be used to retrieve these two private variables
Getters do the opposite. Instead of "setting" the value of a private member variable, they are used to "get" the value from the member variable.
Example:
class x
{
private int _number;
// Getter, used to return the value of _number
//
public int getNumber()
{
return _number;
}
}
Hope this helps
I highly recommend consulting the Java Tutorials, which should be very helpful here. For example, there is a section on constructors which details how they work, even giving an example of a no-argument form:
Although Bicycle only has one constructor, it could have others, including a no-argument constructor:
public Bicycle() {
gear = 1;
cadence = 10;
speed = 0;
}
Bicycle yourBike = new Bicycle(); invokes the no-argument constructor to create a new Bicycle object called yourBike.
Similarly, there are sections dedicated to defining methods and passing information to them. There's even a section on returning values from your method.
Read the above and you should be able to complete your homework :-)