2
votes

I am trying to write a rule to detect if a given event has occurred for 'n' number times in last 'm' duration of time. I am using drools version 5.4.Final. I have also tried 5.5.Final with no effect.

I have found that there are a couple of Conditional Elements, as Drools call it, accumulate and collect. I have used collect in my sample rule below

rule "check-login-attack-rule-1"
dialect "java"
when
    $logMessage: LogMessage()
    $logMessages : ArrayList ( size >= 3 ) 
                    from collect(LogMessage(getAction().equals(Action.Login) 
                    && isProcessed() == false) 
                    over window:time(10s))
then
    LogManager.debug(Poc.class, "!!!!! Login Attack detected. Generating alert.!!!"+$logMessages.size());
    LogManager.debug(Poc.class, "Current Log Message: "+$logMessage.getEventName()+":"+(new Date($logMessage.getTime())));
    int size = $logMessages.size();
    for(int i = 0 ; i < size; i++) {
        Object msgObj = $logMessages.get(i);
        LogMessage msg = (LogMessage) msgObj;
        LogManager.debug(Poc.class, "LogMessage: "+msg.getEventName()+":"+(new Date(msg.getTime())));
        msg.setProcessed(true);
        update(msgObj); // Does not work. Rule execution does not proceed beyond this point.
        // retract(msgObj) // Does not work. Rule execution does not proceed beyond this point.
    }
    // Completed processing the logs over a given window. Now removing the processed logs.
    //retract($logMessages) // Does not work. Rule execution does not proceed beyond this point.

end

The code to inject logs is as below. The code injects logs at every 3 secs and fires rules.

        final StatefulKnowledgeSession kSession = kBase.newStatefulKnowledgeSession();
        long msgId = 0;
        while(true) {
            // Generate Log messages every 3 Secs.
            // Every alternate log message will satisfy a rule condition
            LogMessage log = null;
            log = new LogMessage();
            log.setEventName("msg:"+msgId);
            log.setAction(LogMessage.Action.Login);
            LogManager.debug(Poc.class, "PUSHING LOG: "+log.getEventName()+":"+log.getTime());
            kSession.insert(log);
            kSession.fireAllRules();
            LogManager.debug(Poc.class, "PUSHED LOG: "+log.getEventName()+":"+(new Date(log.getTime())));
            // Sleep for 3 secs
            try {
                sleep(3*1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            msgId++;
        }

With this, what I could achieve is checking for existence of the above said LogMessage in last 10 secs. I could also find out the exact set of LogMessages which occurred in last 10 secs triggering the rule.

The problem is, once these messages are processed, they should not take part in next cycle of evaluation. This is something which I've not be able to achieve. I'll explain this with example.

Consider a timeline below, The timeline shows insertion of log messages and the state of alert generation which should happen.

Expected Result

Secs -- Log -- Alert

0 -- LogMessage1 -- No Alert

3 -- LogMessage2 -- No Alert

6 -- LogMessage3 -- Alert1 (LogMessage1, LogMessage2, LogMessage3)

9 -- LogMessage4 -- No Alert

12 -- LogMessage5 -- No Alert

15 -- LogMessage6 -- Alert2 (LogMessage4, LogMessage5, LogMessage6)

But whats happening with current code is

Actual Result

Secs -- Log -- Alert

0 -- LogMessage1 -- No Alert

3 -- LogMessage2 -- No Alert

6 -- LogMessage3 -- Alert1 (LogMessage1, LogMessage2, LogMessage3)

9 -- LogMessage4 -- Alert2 (LogMessage2, LogMessage3, LogMessage4)

12 -- LogMessage5 -- Alert3 (LogMessage3, LogMessage4, LogMessage5)

15 -- LogMessage6 -- Alert4 (LogMessage4, LogMessage5, LogMessage6)

Essentially, I am not able to discard the messages which are already processed and have taken part in an alert generation. I tried to use retract to remove the processed facts from its working memory. But when I added retract in the then part of the rule, the rules stopped firing at all. I have not been able to figure out why the rules stop firing after adding the retract.

Kindly let me know where am I going wrong.

1
Similar discussion hereMike

1 Answers

0
votes

You seem to be forgetting to set as processed the other 3 facts in the list. You would need a helper class as a global to do so because it should be done in a for loop. Otherwise, these groups of messages can trigger the rule as well:

1 no triggering 1,2 no triggerning 1,2,3 triggers 2,3,4 triggers because a new fact is added and 2 and 3 were in the list 3,4,5 triggers because a new fact is added and 3 and 4 were in the list

and so on

hope this helps