As promised a couple months ago, here is the second part of my report on generating documentation for Spring application context. This installment is all about documenting Spring transaction configuration.
Spring declarative transaction management
Spring framework provides a very non-intrusive and effective declarative transaction management framework which can be configured through XML files. With some knowledge of the AOP/transaction syntax, the XML configuration is actually quite easy to understand, and for a simple configuration, it is also easy to reason about the transaction contexts being applied to the beans within the application context.
Here’s a snippet of a slightly more interesting configuration:
<aop:advisor pointcut="this(com.application.service.ICompanyService)" advice-ref="txRequiredAdvice"/> <aop:advisor pointcut="this(ca.intelliware.util.spring.IInputStreamService)" advice-ref="txRequiredAdvice"/> <aop:advisor pointcut="execution(* **.getBlobInputStream(..))" advice-ref="txRequiredAdvice"/> <aop:advisor pointcut="(within(com.application.service..*) or within(com.application.dao..*))" advice-ref="txMandatoryAdvice"/> <tx:advice id="txRequiredAdvice"> <tx:attributes> <tx:method name="*" read-only="false" propagation="REQUIRED" rollback-for="Exception"/> </tx:attributes> </tx:advice> <tx:advice id="txMandatoryAdvice"> <tx:attributes> <tx:method name="*" read-only="false" propagation="MANDATORY" rollback-for="Exception"/> </tx:attributes> </tx:advice>
As you can see, this configuration applies a REQUIRED transaction context on any method execution in the ICompanyService and IInputStreamServiceinterfaces. The same behaviour is also applied to any getBlobInputStream() method execution.
There is also a “catch-all” advisor which ensures that any other bean, within the “service” or “dao” packages not specifically exposed as an externalizable service, has a MANDATORY transaction context.
All this looks quite reasonable in itself but:
- it becomes difficult to trace which beans are affected by any particular advice if you have numerous beans, say over 50, distributed through several XML files; and
- it is also difficult to reason about what type of transaction context is being applied when more than one advice is applicable to a bean.
- AOP advices are usually specified in terms of interfaces whereas beans are specified in terms of the implementation. So to understand the transaction implications, one would also have to visit the bean classes.
This problem is further compounded by the fact that there are in effect 2 layers of “interception” at work here. Looking at the configuration more carefully, you see that the transaction configuration depends on:
- Spring AOP proxy mechanism, which is used to specify which interface methods we’re interested in for transaction management; and
- Transaction interception advice which defines the transaction attributes for methods executed on proxied beans
Ordering of the advisors also matters as it affects the sequence in which transaction context is applied.
(I won’t go into further details here as it would make for a very long post. The interested reader can explore the Spring documentation and source code for more details.)
This all adds to the subtlety of transaction configuration and sometimes could produce unexpected results.
Calculating the transaction specifications
Parsing the AOP/transaction advices is obviously not a desirable approach especially when one thinks about it, Spring as it loads an application context has to perform the following in an efficient manner:
- calculate the applicable advisors for each bean;
- construct the proxy; and
- apply the appropriate transaction interceptor(s) on the proxies.
Reading the Spring source code and a few serendipitous searches turned up a AOPUtils utility class. AOPUtils has a number of very useful methods, one of which when given a list of advisors and a bean class will calculate the applicable advisors.
Most of the work then is in calculating the transaction attribute of the applicable transaction advice (TX beans) and assigning the corresponding propagation behaviour to the beans.
The result of all this calculation is used to generate the Transaction Settings perspective.
Transaction Setting perspective
The beans are colour coded to indicate the transaction propagation behaviour and the applicable pointcuts are listed.
The colour coding is carried over to the other perspectives of the application context.
This graph shows the dependencies of a given bean. The bean in question is shaded grey.
Spring Beans perspective
The graphs here shows the dependencies with the given bean as the root node.
So what is the value of this documentation?
You might ask: Is this yet another set of “useless” documentation we generate?
Well, because of the difficulties I mentioned above, I regularly review the Transaction Settings perspective for changes to the configuration and watch for sign posts of trouble.
For example on a recent review, I found SecurityDAO bean was marked with PROPAGATION_REQUIRED. This raises some concern as it implies that SecurityDAO could be injected as a “front line” service; thus circumventing the business logic layer and the layered architecture of the application.
Further investigation points to the IInputStreamService advice (see configuration above) that was introduced as a result of a bug fix. Since IInputStreamService interface is one of many interfaces implemented by SecurityDAO, its transactional context is now changed and I suspect this may not have been the expected result.
In addition, because Spring Beans and Dependencies perspectives both reveal more of the layered structure of the beans and together with the colour coding, these perspectives point to several beans which have been marked as PROPAGATION_REQUIRED when they may not necessarily be exposed as such.
So all this prompts for discussion with people more familiar with these beans.
So what’s next?
The current incarnation of the documentation tool has a couple of shortcomings:
- it does not handle “execution” advice because it would involve a lot more calculations; and
- it is not aware of “name” attribute of a transaction advice, so the propagation behaviour reported on each bean is not complete.
Since Spring AOP/transaction mechanism is based on method execution on proxied beans, these are important issues and I see any new effort should be directed in this area. However, I have little time to devote to this tool right now so it may well have to wait until the next extended Christmas vacation.