<div dir="ltr">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:<br><div><br></div><div>*****</div>
<div><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">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.</span><br style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">
<br style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px"><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">My class FactWiper implements WorkingMemoryEventListener and Runnable.</span><br style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">
<ul style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px"><li>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.<br>
</li><li>A SortedMap<Date,Object> expiry2fact keeps track of pending retractions.</li><li>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.</li>
</ul><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">-W</span><br></div><div><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">*****</span></div>
<div><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px"><br></span></div><div style><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">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</span></div>
<div style><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px"><br></span></div><div style><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px">--------------------- LISTENER CLASS ------------------------</span></div>
<div style><span style="color:rgb(0,0,0);font-family:Verdana,Geneva,Helvetica,Arial,sans-serif;font-size:13px"><br></span></div><div style><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif">@Slf4j</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif">@Component</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif">class RetractionListener implements WorkingMemoryEventListener, Runnable {</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> /** allow a sorted map to track dates and objects to be retracted */</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> NavigableMap<Date, Object> retractionSchedule = new TreeMap<Date, Object>();</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> /** allow some condition to signify when we have a new retractable fact to consider */</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> Condition change</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> /** session wrapper */</font></div><div>
<font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> @Autowired</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> SessionManager sessionManager</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> /**</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> * runnable task entry point for executor</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> */</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> void run() {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> Lock lock = new ReentrantLock()</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> change = lock.newCondition()</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> lock.lock()</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> while (retractionSchedule.isEmpty()) {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> change.await()</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> try {</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> while (System.currentTimeMillis() < retractionSchedule.firstKey().time) {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.debug("issuing wait")</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> change.awaitUntil(retractionSchedule.firstKey())</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> FactHandle handle = sessionManager.factHandleMap.get(retractionSchedule.firstEntry().value)</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> sessionManager.session.retract(handle)</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> } catch (InterruptedException e) {</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.error("RetractionListener thread ${Thread.currentThread().name} interrupted!")</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> throw e</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> } finally {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> lock.unlock()</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> /**</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> * detect new facts, determine if they are retractable, and add to the tracker map if needed</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> *</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> * @param event</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> */</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> void objectInserted(ObjectInsertedEvent event) {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> if (event.object.isRetractable) {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.debug("retractable object of type ${event.object.class.simpleName} detected")</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> try {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> long duration = event.object.duration</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> if (!duration) {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> // go ahead and throw up a similar exception to missing property for a missing value</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> throw new MissingPropertyException("no value specified for retractable object's duration")</font></div><div>
<font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> } else {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> Calendar calendar = GregorianCalendar.getInstance()</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.debug("duration of object noted to be ${duration} milliseconds")</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.debug("current time: ${calendar.time}")</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> calendar.add(Calendar.MILLISECOND, duration.toInteger())</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.debug("setting schedule for ${calendar.time}")</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> retractionSchedule.put(calendar.time, event.object)</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.debug("signaling change condition")</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> change.signal()</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> } catch (MissingPropertyException e) {</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> log.error("retractable object ${event.object} missing needed property/value 'duration'")</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> throw e</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> }</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br>
</font></div><div style><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"> ...[REST OF CLASS IRRELEVANT]...</font></div><div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif">}</font></div>
<div><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div style><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif">--------------------- RULE FILE -------------------------</font></div>
<div style><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><br></font></div><div style><font color="#000000" face="Verdana, Geneva, Helvetica, Arial, sans-serif"><div>declare Fire</div><div> @role(event)</div>
<div>end</div><div><br></div><div>declare SprinklerInterval</div><div> @role(event)</div><div> fire : Fire</div><div> isRetractable : boolean</div><div> duration : long</div><div>end</div><div><br></div><div>rule "fire detected"</div>
<div>when</div><div> $f : Fire ( )</div><div> not SprinklerInterval ( fire == $f )</div><div>then</div><div> insertLogical ( new SprinklerInterval($f, true, 500) );</div><div>end</div><div><br></div><div>rule "add sprinklers running notification"</div>
<div>when</div><div> $s : SprinklerInterval ( )</div><div>then</div><div> <a href="http://log.info">log.info</a>("Sprinklers running on fire located in: " + $s.getFire().getName());</div><div>end</div></font></div>
</div></div>