In between some yardwork around the homestead, I've been coding up a storm on the HiveMind front. This morning, I hit all the remaining code inside the framework to handle the new localized message strategy (more on that in a later post). I then refactored ClassFab and friends to make them easier to use outside of HiveMind. ClassFab is my attempt to "tame" Javassist, which is a powerful bytecode enhacement framework used by Tapestry and HiveMind. ClassFab streamlines the work of creating new classes at runtime ... the heavy lifting is done by Javassist, of course, but ClassFab is a prettier face.
The refactoring allowed me to create a better test suite for ClassFab; previously, the tests were a bit indirect, more like integration tests, where ClassFab was used to create a running HiveMind Registry. I've been trying to get away from that as the primary method of testing in HiveMind (and, some day, Tapestry), based largely on listening to Dave Thomas and reading Pragmatic Unit Testing. I now try to test directly, instantiating the objects inside my unit tests (assisted by EasyMock). I then do an additional integration test or two to verify that my module deployment descriptors are correct. This has a lot of advantages; it's much easier to set up exceptional cases than trying to do so using the integration approach, so this more disciplined approach yields better confidence (and code coverage!)
The main effort was this evening, where I created a pair of related services. The first is passed an interface and it creates a default (or placeholder) implementation of that interface. More Javassist, of course. The second service is a service implementation factory, allowing you to easily create placeholder services.
This is leading up to something very powerful, that builds upon the concept in HiveMind of mixing configurations and services on an equal footing: processing pipelines. Think in terms of servlets and servlet filters. Some series of filters will call each other, and the last filter will call into the servlet itself.
The end result will be a way to easily define a service in terms of a configuration point and a pair of interfaces: the service interface and the related filter interface. The configuration will define a series of filters (with all of the neat ordering power that's already built into HiveMind). HiveMind will dynamically create the necessary classes and such that will make the pipeline operate efficiently. This will be great for Tapestry, as it will break apart a number of monolithic classes and code blocks into tiny pieces, and give end users the ability to easily hook into many different places to provide application specific extensions.
2 comments:
Does your pipeline implementation allow filters that extend only a subset of the methods in the original interface? It would make the filters more single-minded with less phantom methods. I have been doing something similar in Avalon without the Bridge objects (I passed in Iterator instead).
Albert
No, the filters must implement the entire filter interface. However, because HiveMind makes it so easy to define services, you often end up with lots of little services and interfaces. The ideal filter interface has a single method.
Post a Comment