Thursday, January 15, 2009

Programatically chaining Interceptors in Spring

I have several services configured like the following:









es.indra.dominioInversis.utilidades.invocarProcesoBatch.servicios.SrvInvocarProcesoBatchService



comisionesErroresOperacionInterceptor serviceTransactionInterceptor
hibernateInterceptor





Today i needed to create a Spring Interceptor that saves language information. (Functionality not important for what i'm about to say). And intercepts every service, like the previous, in the application

I easily implemented the Interceptor, but for some reason my Chief Architect told me that the functionality must be included in the "comisionesErroresOperacionInterceptor", so not adding another interceptor line to the config of every service. I thought this solution was not good, because it would mix totally different functionalities in the same Interceptor.
So i decided to have two diferent interceptors but using only one in each service definition. To achieve this, i did the following.

First i defined a CompositeInterceptor that contains a List of Interceptors.

public class CompositeServicesInterceptor implements MethodInterceptor {
private List interceptors;

public List getInterceptors() {
return interceptors;
}

public void setInterceptors(List interceptors) {
this.interceptors = interceptors;
}
}

Then i asigned the name "comisionesErroresOperacionInterceptor" to this interceptor, so now every service would be intercepted by this Interceptor.
I defined this interceptor like the following:










where "erroresOperacionInterceptor" makes reference to the previous "comisionesErroresOperacionInterceptor" and "languageForPetitionInterceptor" is my new interceptor.

Ok, The last step was to implement the invoke method in the new CompositeInterceptor:

The first attempt was:

public Object invoke(MethodInvocation invocation) throws Throwable {
ProxyMethodInvocation proxyMethodInvocation=(ProxyMethodInvocation)invocation;
Advised advised = (Advised)proxyMethodInvocation.getProxy();
for (MethodInterceptor interceptor : interceptors) {
advised.addAdvice(interceptor);
}
advised.removeAdvice(this);
return invocation.proceed();
}

This didn't work for the first petition, but yes for the followings.
The reason is that the first time when calling invocation.prooceed(), the call is passed to the target object from this point, and not to the proxy with the new advices.
The second and following times it worked great. Calling the others Interceptors instead of this one:

This problem was solved with the following


public Object invoke(MethodInvocation invocation) throws Throwable {
ProxyMethodInvocation proxyMethodInvocation=(ProxyMethodInvocation)invocation;
Advised advised = (Advised)proxyMethodInvocation.getProxy();
for (MethodInterceptor interceptor : interceptors) {
advised.addAdvice(interceptor);
}
advised.removeAdvice(this);
Method method=invocation.getMethod();
return method.invoke(advised, invocation.getArguments());
}

This time instead of calling proceed, i directly invoked the method on the proxy object. So getting the result i wanted.
The second and following invocations were like in the previous case.