how to insert facts in drools at runtime to share between rules? - java

I have a simple that checks whether a user id is present in db
rule "check if user is already present"
agenda-group "dbcheck"
when
$blackListUserDto : BlackListUserDto( )
eval( BlackListServiceImpl.isUserBlacklisted($blackListUserDto) )
then
System.out.println("to be executed first");
System.out.println($blackListUserDto.isUserAlreadyBlacklisted());
end
The method isUserBlacklisted is as follows
public static boolean isUserBlacklisted(BlackListUserDto blackListUserDto)
{
try {
BlackListEntity blackListEntity = blackListRepository.findByUserId(blackListUserDto.getUserId());
if(blackListEntity!=null)
{
blackListUserDto.setUserAlreadyBlacklisted(true);
}
else
//do something else
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
As it can be seen that I am modifying the fact(dto) blackListUserDto by setUserAlreadyBlacklisted(true).
But in the "then" part of rule when I am printing the value
System.out.println($blackListUserDto.isUserAlreadyBlacklisted()); The
output is still false.
also I need to share this data in another rule which is as follows
rule "blacklist user"
agenda-group "blacklist"
when
(BlackListUserDto( userAlreadyBlacklisted == false ))
then
//do something else
end
so far my understanding is that when I edit facts then do we need to re insert them again? if yes then how do I insert it in the same session as there is another method in which I am creating this session as follows :-
public void blacklistUser(String userId) throws IOException
{
BlackListUserDto blackListUserDto=new BlackListUserDto();
blackListUserDto.setUserId(userId);
KieSession kieSession = kContainer.newKieSession();
Agenda agenda = kieSession.getAgenda();
agenda.getAgendaGroup( "blacklist" ).setFocus();
agenda.getAgendaGroup( "dbcheck" ).setFocus();
kieSession.insert(blackListUserDto);
kieSession.insert(queryTypeDto);
kieSession.fireAllRules();
kieSession.dispose();
}
what all changes to be done to make sure that the fact gets updated and the updated value gets reflected in the next rule.

I found a solution to the above and I am sharing the rule that solved the above use case
rule "check if user is already blacklisted 1"
agenda-group "dbcheck"
when
(QueryTypeDto( queryType == "blacklist" ))
$blackListUser : BlackListUserDto( )
not ControlFact( blackListUserDto == $blackListUser )
$blackListUserDto : BlackListUserDto( )
eval( BlackListServiceImpl.isUserBlacklisted($blackListUser) == false )
$queryTypeDto : QueryTypeDto()
then
System.out.println("to be executed first");
System.out.println($blackListUser.isBlackListFraudUser());
modify($blackListUser){
setBlackListFraudUser(true)
}
insert (new ControlFact($blackListUser));
//$queryTypeDto.setUserBlackListed(false);
end
This blog will help more in understanding the use of modify in drools : https://ilesteban.wordpress.com/2012/11/16/about-drools-and-infinite-execution-loops/

Related

Drools : How to write a rule that hits when data is not available in entry point

I am new to drools. I am expecting a sensor data that will send data from a tracking device (like a tag device). I am using Drools entry point to track the sensor data. I need to do some alerts on some events based on this sensor data.
DRL file is as below
import com.sample.AlertRuleModel;
declare AlertRuleModel
#role( event )
#timestamp( timespamp )
end
rule "No signals are coming from any entry-point for more than 10s"
when
$f : AlertRuleModel() from entry-point "AlertRuleStream"
not(AlertRuleModel(this != $f, this after[0s, 10s] $f) from entry-point "AlertRuleStream")
then
$f.setRuleId(1);
<Do alert here>
end
rule "Rule on Tag1 has not been in zone1 for more than 1 minutes"
when
$f : AlertRuleModel( tagId == 1, zoneId == 1 ) from entry-point "AlertRuleStream"
not(AlertRuleModel(this != $f, tagId == 1, zoneId != 1, this after[0s, 1m] $f) from entry-point "AlertRuleStream")
then
$f.setRuleId(2);
<Do alert here>
end
Java code
kSession = RuleExecutionService.getKieSession(packetProcessorData.getAlertRuleDrlPath());
ruleStream = kSession.getEntryPoint("AlertRuleStream");
kSession.addEventListener(new DefaultAgendaEventListener() {
public void afterMatchFired(AfterMatchFiredEvent event) {
super.afterMatchFired(event);
onPostExecution(event, RuleTypeEnum.ALERT_RULE.getName());
}
});
new Thread() {
#Override
public void run() {
kSession.fireUntilHalt();
}
}.start();
Stream data insertion part
private BlockingQueue<AlertRuleModel> alertFactQueue;
.
.
AlertRuleModel alertRuleModel = null;
while (true) {
alertRuleModel = alertFactQueue.poll(1, TimeUnit.SECONDS);
if (alertRuleModel != null) {
//LOGGER.debug("Inserting alertRuleModel into \"AlertRuleStream\"");
ruleStream.insert(alertRuleModel);
continue;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LOGGER.error("Exception while sleeping thread during alertFactQueue polling..", e);
}
}
But when i run application,
First rule "No signals are coming from any entry-point for more than 10s" is not hitting at all. I dont know why, please tell me if i am doing anything wrong or any syntax error is for first rule.
In case of second rule "Tag1 has not been in zone1 for more than 1 minutes", it alway hit immediately when i pass fact with tagId == 1 and zoneId == 1. I tried with different time gaps like after[0s, 10m]. But still it hits immediately after passing fact with above values.
Please tell me where i am making mistakes..?

how to correctly fire Drools rules on multiple objects?

i'm getting my hands on Drools ( with java ) for the first time and i'm quite confused about it's sessions and ability to work with collections of objects.
this is the case:
i'm building a web-application made of rest-services.
i have a class called Log with two fields ( eventType and RiskLevelId ).
Mycode retrieves from a db several objects of this kind in a defined time frame.
If this collection of objects happens to contain one Log with eventType == 2 and RiskLevelId == 1 and another Log with eventType == 3 and RiskLevelId == 1, the rule should be executed.
Via Drools interfaces I correctly retrieve KieServices, KieBuilder, KieContaier, KieBase and KieSession.
try {
// load up the knowledge base
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kfs = kieServices.newKieFileSystem();
FileInputStream fis = f;
kfs.write( "src/main/resources/simple.drl",
kieServices.getResources().newInputStreamResource( fis ) );
KieBuilder kieBuilder = kieServices.newKieBuilder( kfs ).buildAll();
Results results = kieBuilder.getResults();
if( results.hasMessages( Message.Level.ERROR ) ){
System.out.println( results.getMessages() );
throw new IllegalStateException( "### errors ###" );
}
KieContainer kieContainer = kieServices.newKieContainer( kieServices.getRepository().getDefaultReleaseId() );
KieBase kieBase = kieContainer.getKieBase();
kieSession = kieContainer.newKieSession();
}catch (Throwable t) {
t.printStackTrace();
}
i then retrieve each single Log istance in a for loop. staying in the loop i also add the object to the KieSession and fire the rule:
#Autowired
KieSessionFactory kieSessionFactory;
#Override
public void run() {
KieSession kieS = kieSessionFactory.getKieSessionCheckSavedLog();
try {
List<Log> logs = logRepo.getAllInGivenTimeSec(10);
for(Log l : logs) {
kieS.insert(l);
kieS.fireAllRules();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Here comes the rule i've written:
package com.sample
import it.protodrools.beans.Log;
dialect "java"
rule "log2"
when
$l1 : Log( eventType == 2 && riskLevelId == 1);
$l2 : Log( this != $l1 && eventType == 3 && riskLevelId == 1 );
then
System.out.println( "deadly threat !" );
end
My question is: will this rule take in account the whole list of objects that i'm passing ( though not via List, as i've read this is not a good practice ) and thus consider whether there's a condition-matching pair of object among those i'v passed ?
woukd you suggest some different workaround ?
thanks in advance
No, it will not.
for(Log l : logs) {
kieS.insert(l);
kieS.fireAllRules();
}
According to your loop you will insert an object and after each insert immediately afterwards you fire all rules. I am not sure how Drools will react to your loop, but what you probably want to do is insert all Logs in the working memory and then fire the rules:
for(Log l : logs) {
kieS.insert(l);
}
kieS.fireAllRules();
Designing a JUnit test class would show you this immediately though.

else statement still runs even when else if condtions pass

I'm having issues getting my if else statement to work correctly, here I have a login in form that uses values from a database. The statement for the Employee role works fine but even if the else if statement passes the else statement still runs.
If it helps the dialog box appears twice if the Customer statement passes and three time if the else runs by itself. I apologize if my code format is off I'm new at posting code here.
private void jBtnLoginActionPerformed(java.awt.event.ActionEvent evt) {
// action performed when the login button is pressed
// variables that will contain the row entries to the login data base (user name)
String userNameDb = "";
roleDb = rs.getString("role");
//database connection code
try
{
Class.forName("org.sqlite.JDBC");
con = DriverManager.getConnection("//database directory");
st=con.createStatement();
//selects entries from the userName password and role row from the user table
rs=st.executeQuery("Select userName, role From tblUser ;");
//loops through the table entires
while(rs.next())
{
//assigns database entry to variables
userNameDb = rs.getString("userName");
roleDb = rs.getString("role");
if (jTxtUserName.getText().equals(userNameDb) && roleDb.equals("Customer"))
{
//switch forms
break;
}
//if the users input and role match the data base for an customer send them to the selection form
else if (jTxtUserName.getText().equals(userNameDb) && roleDb.equals("Customer"))
{
//switch forms
break;
}
else
{
JOptionPane.showMessageDialog(null, "Login failed");
}
}
}
catch(Exception ex)
{
System.out.println("" + ex);
}
}
}
The problem is that your while loop is coded wrong as your "Login failed" JOptionPane else block shouldn't be within the while loop. Instead declare a boolean value before the loop, set it to false, check if the username/password are found within the that loop, and if so, set the boolean to true. Then after the loop check the boolean value, and if false, show the error message.
To see why, use a debugger to run through the code to see why it's behaving the way it's behaving. More importantly, learn the "rubber duck" debugging technique where you walk through your code mentally or on paper, telling the duck what each line of code should be doing.
To illustrate, your code is behaving something like the code below where a boolean array is mimicking your password username check. Of course, you'd be using a while loop, not a for loop, but this was used here to make the example simpler:
private someActionPerformedMethod() {
// boolean representing when the username/password test is OK
boolean[] loopItems = { false, false, false, true, false };
for (boolean loopItem : loopItems) {
if (loopItem) {
break;
} else {
JOptionPane.showMessageDialog(null, "Login failed");
}
}
}
Assume that the password/username only matches on the 4th try (forth item is true), then for each failed check, the JOptionPane will show a failed login. What you want instead is something like:
private someActionPerformedMethod() {
// boolean representing when the username/password test is OK
boolean[] loopItems = { false, false, false, true, false };
boolean userFound = false;
// you'll of course be using a while loop here
for (boolean loopItem : loopItems) {
if (loopItem) {
userFound = true;
// do something with user data
break;
}
}
if (!userFound) {
JOptionPane.showMessageDialog(null, "Login failed");
}
}

Comparing identical strings using String.equals is found getting skipped

Below is my code
for(int j=0;j<6;j++){
//checking each deal
dealname=driver.findElement(By.id("MainContent_dtlstAllDeals_lblDealTitle_"+j)).getText();
// System.out.println(dealname);
String result[]=dealname.split("\\.");
String resultTitle=result[0];
//System.out.println(resultTitle);
String splitDealname=DealTitle.substring(0,resultTitle.length());
if(splitDealname.equals(resultTitle)){
System.out.println("***whoooooo you got it********"+j+"position"+"in"+i+"page");
//click on view deal button
driver.findElement(By.id("MainContent_dtlstAllDeals_lbtnView_"+j)).click();
Thread.sleep(5000);
//System.out.println(driver.findElement(By.id("MainContent_lblDealTitle")).getText());
String name=driver.findElement(By.id("MainContent_lblDealTitle")).getText();
//verify selected deal is correct
System.out.println(name);
//Thread.sleep(5000);
try {
if (name.equals(DealTitle)) {
System.out.println("whoos...verified");
}
/*
String statuss=veifyTitle("");
if(statuss.equals("success")){
{
System.out.println("whoos...verified");
//do buying process
}
}
else{}*/
} catch (Exception e) {
// TODO: handle exception
System.out.println(e);
}
}
Even though variables DealTitle and name contain same long string as below , that above code is not working.I have put the code in for loop , but when the 'if' condition is run it goes to next iteration .I found it while debugging
Detailed Interior Cleaning + Exterior Car Wash (External Foam Wash, Shampooing, Conditioning, Engine Room Wash, Tyre Polishing) Using AUTOGLYM Brand Products for Just Rs. 399 from Hasten Auto, Vennala (76% OFF)-pramod
Pls help.
I got issue solved by using replace all method
String excelTitle= DealTitle.replaceAll("[^\\w\\s\\-_]", "");
String pageTitle=name.replaceAll("[^\\w\\s\\-_]", "");
if(excelTitle.compareTo(pageTitle)==0){
System.out.println("ok strings are same");
}
Or you can use name.replaceAll("[^a-zA-Z]", "")

Getting response from servlet

I'm using the following code to get the response from a servlet. It will check whether the given name in the variable "get" is in a particular table, and print 1 if it exists.
Portion of servlet code:
get = request.getParameter("nam");// such as get="kannan"
try {
// Connection code
ResultSet rs = stmt
.executeQuery("select * from newfarmer where rname='" + get
+ "'");
while (rs.next()) {
username = rs.getString("rname");
if (get.equals(username)) {
out.println(1);
}
}
} catch (Exception e) {
out.println(e.toString());
}
In my android application, I check this response as follows:
response = CustomHttpClient.executeHttpPost(
"http://moberp.svsugar.com:8080/androidservlt/Modify",
postParameters);
String res = response.toString();
res = res.trim();
if (res.equals("1")) {
flag = 1;
Toast.makeText(getBaseContext(), "Correct", Toast.LENGTH_LONG)
.show();
} else {
flag = 2;
Toast.makeText(getBaseContext(), "please enter correct Ryot name",
Toast.LENGTH_LONG).show();
}
It works very well for single record. I mean, in the table "newfarmer", If "rname" consists of more than one same name only the else part is executed.
Example:
If "kannan" is presented 2 times in the table Servlet output is as
1 1
Now in android application, clearly the else part is executed because response is not 1.
This is only case of two same names. The table may contains more than 10 same names.
If 10 same names, then servlet output is as
1 1 1 1 1 1 1 1 1 1
So I need to check all.
So I need to make changes in my if condition, but I don't know what to do. Someone please give answer.
Thanks in advance
instead of while loop Use
if(rs.next())
Now it will print only one time.
No change in android
in servlet do this
if(rs.next())
{
username=rs.getString("rname");
if(get.equals(username))
{
out.println(1);
}
}
}
this loop will run only 1 time now if the record is present.
I don't understand why would get.equals(username) will evaluate to false when you are having a where clause in your SQL query?
So just try this.
if(rs.next())
{
// The above condition will make the code inside if executed
// only if any matching record is found and
//hence it will print `1` only once
//if any matching record is found.
username=rs.getString("rname");
if(get.equals(username))
{
out.println(1);
}
}
Also you are using stmt.executeQuery("select * from newfarmer where rname='"+get+"'");
which is susceptible to SQL injection.
So better use prepared statement instead.
try if(rs.next()),
this will surely help you

Categories