Friday, February 4, 2011

Spring AOP - Changing target of a Proxy at runtime

I had a pretty strange usecase where i wanted to change the target object of the proxy and runtime. Spring makes it so simple to use.

My usecase was as follows



ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:META-INF/spring/aop-poc-module-context.xml");
AccountDAO accountDao = (AccountDAO) ctx.getBean("accountDao");

IAccount account = accountDao.getById(123);

System.out.println("account" + account);

account.setName("sudheer");

System.out.println("account" + account);



Is a property is change on the entity , i want to change the entity object itself , that is , in the above use two sysouts will print different objects.

The use case arose because i was using a cache(infinispan) which did not allow modifying POJOs outside a transaction scope.

I did achieve the same using the follwing



public class AccountDAO {

private Advisor advisor;

//This is get methods of the accountDao

public IAccount getById(long id) {
//creating new account - to mock database behavior
IAccount a = new Account(369, "suji");
//Using spring factory
ProxyFactory pf = new ProxyFactory();
pf.setExposeProxy(true);
pf.addInterface(IAccount.class);
//Using by own Target source to change the Target
pf.setTargetSource(new SwappableTargetSource(a));
//advisor which holds the advice and pointcut
pf.addAdvisor(advisor);
return (IAccount) pf.getProxy();
}

public void setAdvisor(Advisor advisor) {
this.advisor = advisor;
}

}



My advice class



public class SwappableBeforeAdvice implements MethodBeforeAdvice {

@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("Before advice called");

if (!((IAccount) target).isWritable()) {

//Changing the target source based on specific business condition
((SwappableTargetSource) ((Advised) AopContext.currentProxy())
.getTargetSource()).swap(new Account(56, "dan"));
}

}
}



My custom target source



public class SwappableTargetSource implements TargetSource {

private Object target;

public SwappableTargetSource(Object initialTarget) {
this.target = initialTarget;
}

public synchronized Class<?> getTargetClass() {
return this.target.getClass();
}

public final boolean isStatic() {
return false;
}

public synchronized Object getTarget() {
return this.target;
}

public void releaseTarget(Object target) {
// nothing to do
}

public synchronized void swap(Object newTarget)
throws IllegalArgumentException {
this.target = newTarget;
}

@Override
public boolean equals(Object other) {
return (this == other || (other instanceof SwappableTargetSource && this.target
.equals(((SwappableTargetSource) other).target)));
}

@Override
public int hashCode() {
return SwappableTargetSource.class.hashCode();
}

@Override
public String toString() {
return "SwappableTargetSource for target: " + this.target;
}

}



These are the spring xml configuration.



<bean id="accountDao" class="com.test.dao.account.AccountDAO">
<property name="advisor" ref="settersAdvisor" />
</bean>

<bean id="swapableBeforeAdvice" class="com.test.framework.SwappableBeforeAdvice"/>



This is the advice which advise all setter methods on the bean



<bean id="settersAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="swapableBeforeAdvice" />
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
</list>
</property>
</bean>



Thanks to this post in spring forum which helped me do this.

http://forum.springsource.org/showthread.php?t=102784

No comments:

 
Free Domain Names @ .co.nr!