<div dir="ltr">Thanks for your reply Wolfgang! I appreciate you leaving the summation out there a while back, it's been helpful in pointing me in the right direction and proved as a sanity check in thinking that such a mechanism would benefit my application. I forgot to mention that I'm working in Groovy, thus the absence of semicolons in my code. I'll be putting your advice to good use today, thanks!<div>
<br></div><div style>Jeremy</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Thu, Jan 10, 2013 at 1:32 AM, Wolfgang Laun <span dir="ltr"><<a href="mailto:wolfgang.laun@gmail.com" target="_blank">wolfgang.laun@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">What is missing is that the thread signalling the condition must<br>
acquire the lock guarding the condition. The event handler<br>
should do:<br>
<br>
private Lock mutex = new ReentrantLock();<br>
private Condition change = mutex.newCondition();<br>
<br>
public void objectInserted( ObjectInsertedEvent event ) {<br>
Object object = event.getObject();<br>
mutex.lock();<br>
try {<br>
Long duration = ...;<br>
if( duration != null ){<br>
...add to map...<br>
change.signalAll();<br>
}<br>
} finally {<br>
mutex.unlock();<br>
}<br>
}<br>
<br>
Also, shouldn't the Runnable's "run()" method contain an infinite<br>
loop? Due to the absence of semicolons I suspect something may have<br>
happened to the text, but it seems that it signals only once.<br>
<br>
-W<br>
<div class="im"><br>
<br>
On 09/01/2013, Jeremy Ary <<a href="mailto:jeremy.ary@gmail.com">jeremy.ary@gmail.com</a>> wrote:<br>
> I've found myself in need of something similar to what Wolfgang described<br>
> in an archived post in order to handle retractions based on dynamic<br>
> properties found on the fact itself:<br>
><br>
> *****<br>
> Just in case anybody wonders about the effort for building a mechanism for<br>
> retracting facts based on fact attributes or other data, I outline the<br>
> solution I have implemented.<br>
><br>
> My class FactWiper implements WorkingMemoryEventListener and Runnable.<br>
><br>
</div>> - The implementation of objectInserted( ObjectInsertedEvent event )<br>
<div class="im">> determines whether the object needs handling and, if so, the retraction<br>
> Date, based upon whatever strategy you care to implement, e.g.,<br>
> reflection<br>
> method call, Map<Class,Long>, etc. If it is to be handled, it signals<br>
> Condition "change" to the thread.<br>
</div>> - A SortedMap<Date,Object> expiry2fact keeps track of pending<br>
> retractions.<br>
> - The thread loops into an awaitUntil( x ) on the "change" condition,<br>
<div><div class="h5">> where x is (generally) the first retraction Date. If reached, it<br>
> retracts<br>
> according to the foremost Map entry; if signalled it reasesses the<br>
> situation and reenters the awaitUntil.<br>
><br>
> -W<br>
> *****<br>
><br>
> However, I've been thus far unable to beat an IllegalMonitorStateException<br>
> I encounter when looping back in from an insertLogical. Below is my<br>
> Listener class and my example rules that demonstrate the exception. Anyone<br>
> see what I'm doing wrong with the thread conditions? Thanks for your time.<br>
> - Jeremy<br>
><br>
> --------------------- LISTENER CLASS ------------------------<br>
><br>
> @Slf4j<br>
> @Component<br>
> class RetractionListener implements WorkingMemoryEventListener, Runnable {<br>
><br>
> /** allow a sorted map to track dates and objects to be retracted */<br>
> NavigableMap<Date, Object> retractionSchedule = new TreeMap<Date,<br>
> Object>();<br>
><br>
> /** allow some condition to signify when we have a new retractable fact<br>
> to consider */<br>
> Condition change<br>
><br>
> /** session wrapper */<br>
> @Autowired<br>
> SessionManager sessionManager<br>
><br>
> /**<br>
> * runnable task entry point for executor<br>
> */<br>
> void run() {<br>
><br>
> Lock lock = new ReentrantLock()<br>
> change = lock.newCondition()<br>
> lock.lock()<br>
><br>
><br>
> while (retractionSchedule.isEmpty()) {<br>
> change.await()<br>
> }<br>
><br>
> try {<br>
> while (System.currentTimeMillis() <<br>
> retractionSchedule.firstKey().time) {<br>
> log.debug("issuing wait")<br>
> change.awaitUntil(retractionSchedule.firstKey())<br>
> }<br>
> FactHandle handle =<br>
> sessionManager.factHandleMap.get(retractionSchedule.firstEntry().value)<br>
> sessionManager.session.retract(handle)<br>
><br>
> } catch (InterruptedException e) {<br>
> log.error("RetractionListener thread<br>
> ${Thread.currentThread().name} interrupted!")<br>
> throw e<br>
> } finally {<br>
> lock.unlock()<br>
> }<br>
> }<br>
><br>
> /**<br>
> * detect new facts, determine if they are retractable, and add to the<br>
> tracker map if needed<br>
> *<br>
> * @param event<br>
> */<br>
> void objectInserted(ObjectInsertedEvent event) {<br>
><br>
> if (event.object.isRetractable) {<br>
> log.debug("retractable object of type<br>
> ${event.object.class.simpleName} detected")<br>
> try {<br>
> long duration = event.object.duration<br>
> if (!duration) {<br>
> // go ahead and throw up a similar exception to missing<br>
> property for a missing value<br>
> throw new MissingPropertyException("no value specified<br>
> for retractable object's duration")<br>
> } else {<br>
><br>
> Calendar calendar = GregorianCalendar.getInstance()<br>
> log.debug("duration of object noted to be ${duration}<br>
> milliseconds")<br>
> log.debug("current time: ${calendar.time}")<br>
><br>
> calendar.add(Calendar.MILLISECOND,<br>
> duration.toInteger())<br>
><br>
> log.debug("setting schedule for ${calendar.time}")<br>
> retractionSchedule.put(calendar.time, event.object)<br>
><br>
> log.debug("signaling change condition")<br>
><br>
> change.signal()<br>
> }<br>
> } catch (MissingPropertyException e) {<br>
> log.error("retractable object ${event.object} missing<br>
> needed property/value 'duration'")<br>
> throw e<br>
> }<br>
> }<br>
> }<br>
><br>
> ...[REST OF CLASS IRRELEVANT]...<br>
> }<br>
><br>
> --------------------- RULE FILE -------------------------<br>
><br>
> declare Fire<br>
> @role(event)<br>
> end<br>
><br>
> declare SprinklerInterval<br>
> @role(event)<br>
> fire : Fire<br>
> isRetractable : boolean<br>
> duration : long<br>
> end<br>
><br>
> rule "fire detected"<br>
> when<br>
> $f : Fire ( )<br>
> not SprinklerInterval ( fire == $f )<br>
> then<br>
> insertLogical ( new SprinklerInterval($f, true, 500) );<br>
> end<br>
><br>
> rule "add sprinklers running notification"<br>
> when<br>
> $s : SprinklerInterval ( )<br>
> then<br>
> <a href="http://log.info" target="_blank">log.info</a>("Sprinklers running on fire located in: " +<br>
> $s.getFire().getName());<br>
> end<br>
><br>
</div></div>_______________________________________________<br>
rules-users mailing list<br>
<a href="mailto:rules-users@lists.jboss.org">rules-users@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/rules-users" target="_blank">https://lists.jboss.org/mailman/listinfo/rules-users</a><br>
</blockquote></div><br></div>