ALBERLAU blog

Blog about my personal experience in software development.

Friday, March 20, 2009

 

AnnotationsBasedValidator for JPA entities in SpringFramework

Despite there is Hibernate Validator, I wrote my own. It is adopted to use with SpringFramework WEB MVC framework. Why I not used Hibernate Validator? In Hibernate we have @NotNull, @Length annotations and in @Column we have simmilar annotations. I think that violates DRY principle. Result of Hibernate Validator is array of InvalidValue. For Spring it is better to have messages in ResultBinding.

Usage example:




protected IAnnotationsBasedValidator annotationsBasedValidator;

@Autowired
public void setAnnotationsBasedValidator(IAnnotationsBasedValidator annotationsBasedValidator) {
this.annotationsBasedValidator = annotationsBasedValidator;
}




then somewhere in your code:




annotationsBasedValidator.validate(payment, bindingResult);




As a result bindingResult is filled with validation messages(if there is violated constraints for annotations).

This validator fully utilizes JPA @Column length, nullable, precission, scale, @JoinColumn nullable, @OneToOne , @Embedded. Nested objects for @JoinColumn , @OneToOne , @Embedded is also validated traversing full object tree, except those who is not !propertyInitializationInfo.isInitializaed(cs) (lazy properties).

Additionally it supports annotations: RegexChecker, RangeChecker, BooleanResultChecker to call static method to validate value.

Validator source

Tuesday, March 10, 2009

 

Decorating list objects dynamically to display calculated fields

Imagine there is Account class that holds IBAN, currency, and balance. Customer wants to display combo of accounts that displays options in format LT025010200020000141 LTL(1000.23) . Most quick solution probably is to place :

getDisplayInFormat1() { return iban+" "+currency+"("+balance+")";}

method inside of Acount class. Of course Account class will soon be overwhelmed with getDisplayInFormat2, getDisplayInFormat3 and so on methods with view related information, possibly with style:red to indicate insufficient balance.

Next solution is to create some DropDownAccountDecorator. Usage is below:




List<Account> acc = cpFacade.loadAccounts(user);
List decoratedAcc = ListDecorator.getInstance(new DropDownAccountDecorator(), acc);




DropDownAccountDecorator source :



import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class DropDownAccountDecorator implements IListItemDecorator,Serializable {
private boolean hideBalance;

@Override
public Object decorate(final Object o) {
return Enhancer.create(o.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method mth, Object[] args, MethodProxy mpx) throws Throwable {
Object res = mth.invoke(o, args);
String mname = mth.getName();
if (!mname.equals("getDisplay")) {
return res;
}
Account acc = (Account) obj;
String display;
if (AccStates.BLOCKED.equals(acc.getAccState())) {
display = String.format("%s (%1.2f %s blocked)", acc.getName(), acc.getTotalBalance(), acc.getCurrencyDetail().get(0).getCurrency().getCode());
} else {
if(hideBalance) {
display =  String.format("%s %s", acc.getName(), acc.getCurrencyDetail().get(0).getCurrency().getCode());
} else {
display = String.format("%s (%1.2f %s)", acc.getName(), acc.getTotalBalance(), acc.getCurrencyDetail().get(0).getCurrency().getCode());
}
}
return display;
}
});
}

public void setHideBalance(boolean b) {
this.hideBalance=b;
}
}




Code uses ListProxy infrastructure from my previous post.
 

Workaround for SpringFramework NullValueInNestedPath

I faced springframework famous NullValueInNestedPath problem. I found one out of the box solution to workaround this problem. The solution is to create decorator who wraps list and creates null objects lazily jus for display. There is LegacyTransaction object who owns one payer or one payee or both of class Participant. In most cases there is null payer or payee. Of course we can create somewhere those objects. But in case of hibernate they will be persisted. Create fake objects, just to display correct data is not right solution. Example usage is as follows.





List<LegacyTransaction> tr = bnkPmtFacade.loadLegacyTransactions(tf, user, br);
List trDeco=ListDecorator.getInstance(new NullSafeParticipantDecorator(), tr);
mm.addAttribute(BnkPmtSessionKeys.transactions, trDeco);






Actual decorator to create fake objects NullSafeParticipantDecorator.
Source is below :




import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;






public class NullSafeParticipantDecorator implements IListItemDecorator,Serializable{

@Override
public Object decorate(final Object o) {
return Enhancer.create(o.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method mth, Object[] args, MethodProxy mpx) throws Throwable {
Object res = mth.invoke(o, args);
String mname = mth.getName();
if (!mname.equals("getPayer") && !mname.equals("getPayee")) {
return res;
}
LegacyTransaction lt=(LegacyTransaction)o;
if(mname.equals("getPayer")) {
if(lt.getPayer()!=null) {
return lt.getPayer();
} else {
return new Participant();
}
}
if(mname.equals("getPayee")) {
if(lt.getPayee()!=null) {
return lt.getPayee();
} else {
return new Participant();
}
}
throw new RuntimeException("Should never get this!");
}
});
}
}




Below is classes that makes easer to create such kind of decorators
ListDecorator source:



import java.lang.reflect.Proxy;
import java.util.List;

public class ListDecorator {
public static List getInstance(IListItemDecorator deco,List list) {
return (List)Proxy.newProxyInstance(List.class.getClassLoader(), new Class[]{List.class}, new ListProxy(list,deco));
}
}




IListItemDecorator source:



public interface IListItemDecorator {
Object decorate(Object o);
}




ListProxy source:



import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Iterator;
import java.util.List;

public class ListProxy implements InvocationHandler {

private IListItemDecorator deco;
private List list;

public ListProxy(List list,IListItemDecorator deco) {
this.list=list;
this.deco = deco;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res;
if (method.getName().equals("iterator")) {
res = Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Iterator.class}, new IteratorProxy(list.iterator(), deco));
} else if(method.getName().equals("get")) {
res=deco.decorate(list.get((Integer)args[0]));
} else {
if(list==null) {
res=null;
} else {
res=method.invoke(list, args);
}
}
return res;
}
}




IteratorProxy source:



import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Iterator;

public class IteratorProxy implements InvocationHandler{
private IListItemDecorator deco;
private Iterator iterator;

public IteratorProxy(Iterator iterator, IListItemDecorator deco) {
this.iterator=iterator;
this.deco=deco;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res;
if(method.getName().equals("next")) {
res=deco.decorate(iterator.next());
} else {
res = method.invoke(iterator, args);
}
return res;
}
}



Archives

January 2008   February 2008   March 2009  

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]