Matt Wizeman and Jeff Kunkle of NearInfinity presented a practitioner report describing how they used AspectJ to build a product for enterprise compliance auditing. They note that most projects add auditing as an afterthought and it shows in the poor quality results. One key benefit of separating out the auditing implementation is it allows consistent policy implementation (it takes audit decisions out of the developers' hands).
NearInfinity uses AspectWerkz aspects to collect data, which then gets fed into a pipeline:
collect -> transport -> filter -> transform -> alert -> store
The data is collected into an in-memory database, and the architecture allows filtering data in an XML form at several places.
The system uses load-time weaving with AspectWerkz. They were initially using AspectJ with the Weblogic Aspect System, but converted when the latter wasn't updated for AspectJ 1.2. The AspectJ 1.1 version took 4 minutes on system start up. The AspectWerkz 2.0 version took 50-60 seconds. They are planning to use AspectJ 5 when it picks up comparable LTW support. This lets them weave into several interesting places:
- generated code (e.g., entity beans)
- 3rd party libraries
They also have thin aspects, moving as much code as possible into helper classes. One key thing they emphasized is the importance of aspect robustness: e.g., they hit a bug when beta testing because they were trying to log a null argument to a JNDI lookup in a customer system. The customer system was swallowing the resulting NPE so it was hard to track down. They key lesson is to be paranoid.
As a side note, aspects can also help with this problem. You can write after throwing advice to catch any exceptions thrown out of advice you write, to prevent it from poisioning the underlying system (e.g., log it and swallow it). This is very appropriate for auxiliary aspects, or any part of the system that isn't essential for core operation. Note that you can't quite make this work for around advice: if there's an exception in around advice you don't know if it preempted the proceed call... This is a great candidate for a reusable aspect too.
One key part of their system is tracking SQL data: they have gone through 4 different approaches:
- wrapper pattern (on construction)
- per instance aspects (but the Weblogic EJB deployer choked on this)
- storing a TheadLocal HashMap of statement to state (I believe Nick pointed out that this is probably better done with an InheritableThreadLocal, as Wes has suggested in past).
- generating a dynamic proxy to wrap a statement on creation
I had previously discussed with them how one might weave into the JDBC driver code to address this problem, while still allowing per-application data (e.g., tracking information by application). I am optimistic about this approach for situations where it's reasonable to have your aspect affect all the applications in a container. In many cases, it's quite reasonable to deploy some applications to different containers, too.
Another interesting implementation issue is how to audit requests that don't reach user servlets and how to map to the right application (e.g., 404 errors or static resources). This is very important for auditing (e.g., to monitor sql injection attempts).
So they use a Servlet filter, which has the right hooks defined portably. Sam Pullara suggested advising an empty filter. It's an interesting question if one can portably add a filter at runtime, or achieve the equivalent affect with just advice (i.e., avoiding the need to configure applications' web.xml files).
In future, they plan to expand coverage to include system commands, socket I/O, file manipulation, exception handling and 3rd party search engine APIs, Web frameworks and persistence frameworks.