03 June 05
ObjectAsserted/Retracted/ModifiedEvent (tag 'memelet-aspectj-day2a')
After a some help from the newsgroup and a new compiler from Andy Clement I'm off again.
I had to restructure the eclipse projects a bit. I am introducting methods into WorkingMemory (eg, addListener, removeListener). However, from within the drools-core project, a regular java test class cannot see these methods unless you open the file with the aspectj editor. Since our users will probably not want to do that, I needed to write tests that simulate the general usage environment. To achieve this, I created the project drools-core-aspectjtest. This project does not depend directly on drools-core, but rather includes its output path as a dependency, thus simulating the use of drools-core.jar. Now tests in drools-core-aspectjtest can invoke introduced methods without any knowledge of aspectj. (Other than including aspectrt.jar in the classpath, that is.)
I have green bars that cover WorkingMemory addListener, removeListener, assertObject, retractObject, and modifyObject. I needed to change the signatures of WorkingMemory assertObject, retractObject, and modifyObject to return FactHandle, Object, Object, respectively. Without these return values, after advice would not have all the arguments to send the events. Orignally, I was using advice around the calls to RuleBase so I could get these extra values, but that was very smelly.
So here's some snippets from the aspect. First we introduce into WorkingMemory the WorkingMemoryEventSupport instance variable and the addListener/removeListener methods:
Then we define the pointcuts:
Finally, we send the events via after advice:
To see the test, checkout WorkingMemoryEventSupportTest.java.
ConditionTestedEvent (tag 'memelet-aspectj-day2b')
Next I'll tackle ConditionTestedEvent. The class that sent this event, org.drools.reteoo.ConditionNode is concrete and package private. In order to add not-conditions, we are going to need an interface for condition-nodes, so I created org.drools.spi.IConditionNode. The first problem I hit is that ConditionNode dependes on concrete reteoo classes (eg, ReteTuple, WorkignMemoryImpl). ReteTuple I change to Tuple, and WorkingMemoryImpl I change to WorkingMemory, but there is also a dependency on reteoo.TupleKey. This class does not seem to depend on anything in reteoo, so I move it spi.
After moving TupleKey I see that ReteTuple internally defines variables of type FactHandleImpl and tries to assign them from TupleKey.get* methods. So I change the fields to be of the type FactHandle. But this fails, because ReteTuple is depending on methods of FactHandleImpl. Specifically, "recency" values for use by RecencyConflictResolver. This is a smell, since conflict strategies are pluggable and should not require other classes to carry data for them. So, out all the recency stuff comes. I'll add this to the list of things to aspectize.
At this point pretty all hell is breaking loose. The package structure of drools-core seems way off the main sequence. Also many interfaces and classes depend on concrete classes. Few of the interfaces that do exist are proper behavioral abstractions, so when I refactor code to use the interfaces, they rarely export the required methods. Many of the problems seem to stem from violating Law-of-Demeter (ie, foo.getBar().getBla().contains(thng)), or at least that's what I'm seeing right now.
I am making all kinds of changes to accomadate this simple refactoring. To see all the changes you will have to compare between the cvs tags 'memelet-aspectj-day2a' and 'memelet-aspectj-day2b'.
Whew. I had to roll everything back. There were too many required changes to keep the tests green. So I'm going to take another approach (for now at least). I'll use a control-flow pointcut. Gotta do some reading...
