Gaim
Gaim

Reputation: 6844

Drools: Time restricted rule

Drools documentation mentions that rules can use attributes like date-effective and date-expires to specify an absolute rule validity period.

For example

rule "Date-restricted rule"
    date-effective "20.2.2013 8:00"      # 8 AM
    date-expires   "20.2.2013 16:00"     # 4 PM
    when
    then
end

Drools also supports periodically repeated rules with interval as timer(int:) and cron as timer(cron:) but it means that the rule is fired in such points.

Question:

I am interested if there is any option how to specify periodically available (not fired) rules with time restriction. For example let's image the business hours in some company - the operation can be performed only during official working period but not after hours.

I would like something like this, but it is not Drools valid rule

rule "Time-restricted rule"
    time-effective "8:00"      # 8 AM
    time-expires   "16:00"     # 4 PM
    when
    then
end

Would be possible to extend such rule to be active only Monday to Friday 8 AM to 4 PM?


Solution (by Esteban Aliverti):

Drools doesn't have a direct support for time-based keywords, but they provide much more powerful calendar mechanism using Quartz library. StatefulSession or WorkingMemory created by StatelessSession has methods to define these calendars which can restrict date and time when the rule can be fired.

Example: Rule definition

rule "Business hours only"
    calendars "business-hours"
    when
        SomeAttachedClass()
    then
        System.out.println("Rule is fired");
end

Calendar definition

import org.quartz.impl.calendar.DailyCalendar;

// stateless session and working memory or directly stateful session
StatefulKnowledgeSession memory = session.newWorkingMemory(); 
// interested time range is 8-16, also there is many Calendar implementation, not just Daily
DailyCalendar businessHours = new DailyCalendar( 8, 0, 0, 0, 16, 0, 0, 0 );
// by default, defined time is EXCLUDED, the inversion makes it INCLUDED and excludes the rest
businessHours.setInvertTimeRange( true );
//convert the calendar into a org.drools.time.Calendar
org.drools.time.Calendar businessHoursCalendar = QuartzHelper.quartzCalendarAdapter( businessHours );
//Register the calendar in the session with a name. You must use this name in your rules.
memory.getCalendars().set( "business-hours", businessHoursCalendar );

Upvotes: 5

Views: 8611

Answers (3)

tobias_k
tobias_k

Reputation: 82899

Here's another solution. Maybe a bit of a hack, but it works:

You can create a simple Java class encapsulating the current time and add an instance of this class to the working memory. This class, call it TimeFact, has methods like update(long time): void, getDay(): String and getTime(): String. This fact can then be used in the when part of a rule, like this:

rule "Foo"
when
    TimeFact(day in ("Monday", "Thursday"), time > "16:00:00" && < "17:00:00")
    [more conditions]
then
    [do stuff]
end

You will need to update the TimeFact (and notify the rule engine of this update) before firing your rules. Still, it has the advantage that rule activation times can be described in the rules themselves, without having to define a possibly large number of calendars, thus making this approach much more flexible.

Of course, if you have only a few such timed conditions, like "office hours", then the solution using calendars is preferable.

Upvotes: 1

Esteban Aliverti
Esteban Aliverti

Reputation: 6322

A better approach is to use calendar instead of timer(cron:). I managed to do something similar to what you are looking for following these steps:

When you create the session you have to create and configure a Quartz calendar:

//in this case I'm using a DailyCalendar but you can use whatever implementation of Calendar you want
org.quartz.impl.calendar.DailyCalendar businessHours = new org.quartz.impl.calendar.DailyCalendar("business-hours", 8, 0, 0, 0, 16, 0, 0, 0);
businessHours.setInvertTimeRange(true);

//convert the calendar into a org.drools.time.Calendar
org.drools.time.Calendar businessHoursCalendar = QuartzHelper.quartzCalendarAdapter(businessHours);

//Register the calendar in the session with a name. You must use this name in your rules.
ksession.getCalendars().set( "business-hours", businessHoursCalendar );

Then in your rule you have to write something like this:

rule "Rule X"
    calendars "business-hours"
when
    ...
then
    ...
end

Hope it helps,

Upvotes: 6

Esteban Aliverti
Esteban Aliverti

Reputation: 6322

According to the documentation, you could use cron expressions as timers. So, you could probably use something like this:

timer(cron: * 8-16 * * 1-5 *) 

Disclaimer: I didn't test this!

Hope it helps,

Upvotes: 0

Related Questions