It was pretty hard to convince the development team of trying aspects and I had to do it step by step. The main concern was having some code executing without being able to realize what was being executed from looking at the code. So I decided to mix aspects with annotations, and advice only parts of the code explicitly marked with a particular annotation. So if you wanted to use the advice, you just added an annotation, and by looking at the annotation you knew that an aspect was being weaved. Annotations mixed with aspects worked as a neat kind of code reuse which we could use as an alternative to inheritance and composition/delegation.
We used that mechanism to add statistics support to our messaging gateway (a gateway for MMS/SMS from and to different protocols including SMPP, HTTP, web services, MM1, MM7, RMI...) and it was quite successful. You only needed to annotate a method by specifying the type of the stat and it's namespace (the place of the stat in a browseable hierarchy of stats, which could include parameters values, return types, or properties of the annotated method class). We did something similar to check for authorization before executing some method, which also worked OK (even if, nonetheless, we needed additional layers of authorization before and after method authorization).
My next step was to try to implement synchronization using aspects. Our messaging gateway is based on the concept of independent services that share the same lifecycle (started, stopped, reloading, etc.). Most of the lifecycle methods needed to be executed with an exclusive lock (since we want to avoid start and stop from being executed concurrently, and reloading from being executed while handling a request), and the service methods (things like submitting a message) must not be executed when a lifecycle method was executing but could be executed concurrently with other service methods. To achieve this, I considered three alternatives:
- Write code before and after each lifecycle and service method to synchronize the access. Since the gateway is basically a framework for implementing new services, programmers also needed to ensure to write this code or things could go wrong. This approach required A LOT of duplicate code, and required the framework user to write code at each service method to make sure its services where well behaved.
- By using inheritance, through a template pattern, so the programmer sub-classed the template class which handled synchronization. This required a new class for each type of possible service (and there are more than a few!), and required an additional method for each template method (the template method wrapper that handled synchronization, and the template method itself).
- Use aspects.
The team reception to this approach was not exactly warm. It's currently working OK, but there are some issues with lock escalation that must be taken in account by programmers in some border cases (like calling a lifecycle method from a service method).
Main concerns are that Aspects should not be used when the adviced code NEEDS the aspect to work correctly. While I share this view somewhat, I decided to leave the synchronization aspect because it greatly reduced the code base. I still have my doubts however....
No comments:
Post a Comment