In my company they came up with a rather strange way to"customize/extend" the product. There is a sort of framework upon whichcustomizations are built. The way to customize a certain piece of logicis by using a variation of the chain of responsibility pattern. Pieces of logic are put into command objects and these objects are chained using Apache Commons-Chain. The reasons why this done like this is a topic for another post.
Becausethe application is spring based, it was awkward to have these commandobject instantiated outside spring. By doing this we were unable toleverage the dependency injection facility in spring and therefore, hadto come up with a very wacky way of doing this which basically wasputting collaborators into the command context.
Recently this wasfixed. We are now able to declare command objects as spring beans andinject anything we want into them. The same goes with chains. This isindeed, a great improvement over what we had before.
Now, to thepoint of this post. In my team, we thought it would be nice to have aframework to validate the command contexts before the actual executemethod is called. By having this, we'll be saving our selves theclutter of having validation code inside the commands; instead, thecommands would assume everything expected to be in the context wasthere, and focus only on getting the job done.
This is the kind ofuse cases aspects are very well suited for. It is a cross cuttingconcern because validation has to be performed across a large set ofobjects.
So, we came up with a before advice for the execute methodon the command objects and also wrote a factory that is able to returna validator object for a particular type of command. This factory onlyhave to be declared as a bean in the spring context and wheninitialized it will search the application context for Validator beansand put them in a map keyed by the type of comand they know how tovalidate.
If the factory returns nullfor a particular type of command then the advice will let theinvocation continue without further actions. At the most, it logs awarning about the missing validator.
This is how the advice looks in real life:
public class CommandContextValidationAdvice implements MethodBeforeAdvice {
private CommandValidatorFactory validatorFactory = null;
/**
* Lookup a CommandContextValidator via the factory. And if not null then execute the validate method.
*/
public void before(Method method, Object[] params, Object command) throws Throwable {
CommandContextValidator validator=validatorFactory.getCommandValidator(command.getClass());
if(validator != null) {
validator.validate((Context) params[0]);
}
}
/**
* @param validatorFactory The validatorFactory to set.
*/
public void setValidatorFactory(CommandValidatorFactory validatorFactory) {
this.validatorFactory = validatorFactory;
}
}
The second problem we faced was applying the aspect to the commands. We didnot wanted to change every single command bean definition as this woulddefeat the purpose of making the validation framework orthogonal to theactual command hierarchy.
In order to solve this, we used the autoproxy facility in spring. The bean definitions in the applicationcontext then looks like this:
<BEAN class=org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator id=jdkBeanNameProxyCreator>
<property name="beanNames">
<VALUE>*Command</VALUE>
</property>
<property name="interceptorNames">
<LIST>
<VALUE>commandContextValidationAdvisor</VALUE>
</LIST>
</property>
</BEAN>
<BEAN class=com.mycompany.myclientcompany.command.CommandContextValidationAdvice id=commandContextValidationAdvice>
<property name="validatorFactory">
<REF bean="commandValidatorFactory"/>
</property>
</BEAN>
<BEAN class=org.springframework.aop.support.RegexpMethodPointcutAdvisor id=commandContextValidationAdvisor>
<property name="advice">
<REF local="commandContextValidationAdvice"/>
</property>
<property name="pattern">
<VALUE>org.apache.commons.chain.Command.execute</VALUE>
</property>
</BEAN>
The first bean definition is the auto proxy creator. It will proxy everybean in the application context whose name end with the word Command. In our case this was not much of a problem because all the commands were following this convention even before we thought about the validation framework. The rest of the bean definitions are the Advice and the Advisor respectively.
No comments:
Post a Comment