[rules-users] retracting facts based on fact properties

Wolfgang Laun wolfgang.laun at gmail.com
Thu Jan 10 02:32:34 EST 2013


What is missing is that the thread signalling the condition must
acquire the lock guarding the condition. The event handler
should do:

  private Lock mutex = new ReentrantLock();
  private Condition change = mutex.newCondition();

  public void objectInserted( ObjectInsertedEvent event ) {
    Object object = event.getObject();
    mutex.lock();
    try {
      Long duration = ...;
      if( duration != null ){
        ...add to map...
        change.signalAll();
      }
    } finally {
      mutex.unlock();
    }
  }

Also, shouldn't the Runnable's "run()" method contain an infinite
loop? Due to the absence of semicolons I suspect something may have
happened to the text, but it seems that it signals only once.

-W


On 09/01/2013, Jeremy Ary <jeremy.ary at gmail.com> wrote:
> I've found myself in need of something similar to what Wolfgang described
> in an archived post in order to handle retractions based on dynamic
> properties found on the fact itself:
>
> *****
> Just in case anybody wonders about the effort for building a mechanism for
> retracting facts based on fact attributes or other data, I outline the
> solution I have implemented.
>
> My class FactWiper implements WorkingMemoryEventListener and Runnable.
>
>    - The implementation of objectInserted( ObjectInsertedEvent event )
>    determines whether the object needs handling and, if so, the retraction
>    Date, based upon whatever strategy you care to implement, e.g.,
> reflection
>    method call, Map<Class,Long>, etc. If it is to be handled, it signals
>    Condition "change" to the thread.
>    - A SortedMap<Date,Object> expiry2fact keeps track of pending
>    retractions.
>    - The thread loops into an awaitUntil( x ) on the "change" condition,
>    where x is (generally) the first retraction Date. If reached, it
> retracts
>    according to the foremost Map entry; if signalled it reasesses the
>    situation and reenters the awaitUntil.
>
> -W
> *****
>
> However, I've been thus far unable to beat an IllegalMonitorStateException
> I encounter when looping back in from an insertLogical. Below is my
> Listener class and my example rules that demonstrate the exception. Anyone
> see what I'm doing wrong with the thread conditions? Thanks for your time.
> - Jeremy
>
> --------------------- LISTENER CLASS ------------------------
>
> @Slf4j
> @Component
> class RetractionListener implements WorkingMemoryEventListener, Runnable {
>
>     /** allow a sorted map to track dates and objects to be retracted */
>     NavigableMap<Date, Object> retractionSchedule = new TreeMap<Date,
> Object>();
>
>     /** allow some condition to signify when we have a new retractable fact
> to consider */
>     Condition change
>
>     /** session wrapper */
>     @Autowired
>     SessionManager sessionManager
>
>     /**
>      * runnable task entry point for executor
>      */
>     void run() {
>
>         Lock lock = new ReentrantLock()
>         change = lock.newCondition()
>         lock.lock()
>
>
>         while (retractionSchedule.isEmpty()) {
>             change.await()
>         }
>
>         try {
>             while (System.currentTimeMillis() <
> retractionSchedule.firstKey().time) {
>                 log.debug("issuing wait")
>                 change.awaitUntil(retractionSchedule.firstKey())
>             }
>             FactHandle handle =
> sessionManager.factHandleMap.get(retractionSchedule.firstEntry().value)
>             sessionManager.session.retract(handle)
>
>         } catch (InterruptedException e) {
>             log.error("RetractionListener thread
> ${Thread.currentThread().name} interrupted!")
>             throw e
>         } finally {
>             lock.unlock()
>         }
>     }
>
>     /**
>      * detect new facts, determine if they are retractable, and add to the
> tracker map if needed
>      *
>      * @param event
>      */
>     void objectInserted(ObjectInsertedEvent event) {
>
>         if (event.object.isRetractable) {
>             log.debug("retractable object of type
> ${event.object.class.simpleName} detected")
>             try {
>                 long duration = event.object.duration
>                 if (!duration) {
>                     // go ahead and throw up a similar exception to missing
> property for a missing value
>                     throw new MissingPropertyException("no value specified
> for retractable object's duration")
>                 } else {
>
>                     Calendar calendar = GregorianCalendar.getInstance()
>                     log.debug("duration of object noted to be ${duration}
> milliseconds")
>                     log.debug("current time: ${calendar.time}")
>
>                     calendar.add(Calendar.MILLISECOND,
> duration.toInteger())
>
>                     log.debug("setting schedule for ${calendar.time}")
>                     retractionSchedule.put(calendar.time, event.object)
>
>                     log.debug("signaling change condition")
>
>                     change.signal()
>                 }
>             } catch (MissingPropertyException e) {
>                 log.error("retractable object ${event.object} missing
> needed property/value 'duration'")
>                 throw e
>             }
>         }
>     }
>
>     ...[REST OF CLASS IRRELEVANT]...
> }
>
> --------------------- RULE FILE -------------------------
>
> declare Fire
>     @role(event)
> end
>
> declare SprinklerInterval
>     @role(event)
>     fire : Fire
>     isRetractable : boolean
>     duration : long
> end
>
> rule "fire detected"
> when
>     $f : Fire (  )
>     not SprinklerInterval ( fire == $f )
> then
>     insertLogical ( new SprinklerInterval($f, true, 500) );
> end
>
> rule "add sprinklers running notification"
> when
>     $s : SprinklerInterval ( )
> then
>     log.info("Sprinklers running on fire located in: " +
> $s.getFire().getName());
> end
>


More information about the rules-users mailing list