Stop LazyInitialization exceptions with the Hessian Hibernate Cleaner Aspect
September 24th, 2008
Here is a piece of code I hacked up this afternoon and just had to share with the world. A cure, if you like, for the Hibernate LazyInitialization exception.
I am tired of the Hibernate LazyInitialization exceptions when remoting via Hessian or doing pretty much anything when you’ve got a disconnected Hibernate session; perhaps there’s a nicer solution to what I’m gonna provide here but I don’t know what it is. I’m pretty sure it’s NOT to turn off Lazy Initialization….
This class descends recursively through the result of a method invocation which may have returned a class or collection which may contain uninitialized Hibernate Collection instances, such as PersistentBag, PersistentSet or PersistentMap.
Upon encountering such a Collection an attempt is made to initialize said Collection, if this fails the Collection is set to null.
You should put this class between a Spring service and the HessianServiceExporter. Alternatively, it could be implemented as part of a custom HessianSerializationFactory. If anyone rewrites it that way, feel free to share the implementation with the world.
MafuBryan
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.LazyInitializationException;
import org.hibernate.collection.PersistentBag;
public class HessianCleanerAspect {
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(HessianCleanerAspect.class);
public Object clean(ProceedingJoinPoint call) {
try {
if (logger.isDebugEnabled()) {
logger
.debug("clean(ProceedingJoinPoint) - from logging aspect: entering method ["
+ call.toShortString()
+ "] with params:”
+ appendArgs(call.getArgs()));
}
Object point = call.proceed();
point = descendThroughNode(point, new ArrayList<Object>());
if (logger.isDebugEnabled()) {
logger
.debug(”clean(ProceedingJoinPoint) - from logging aspect: exiting method ["
+ call.toShortString()
+ "with return as:"
+ point);
}
return point;
} catch (Throwable t) {
throw new UnsupportedOperationException(t);
}
}
/**
*
* This method is going to presume a couple of things.
* <ol>
* <li>Presume that we are not using any kind of bags or collections which
* shall contain duplicate elements.</li>
* </ol>
*
* @param node
* @param visited
* a {@link List} of node references for the nodes that have
* already been visited, this prevents getting stuck on, for
* example, circular references.
* @return
*/
@SuppressWarnings("unchecked")
private Object descendThroughNode(Object node, List visited) {
// Check to see if we have already visited this node.
if (visited.contains(node)) {
// If we have, then just return.
return node;
} else {
// Otherwise add the node to our visited list
visited.add(node);
}
try {
if (node == null) {
return null;
}
// if (node instanceof PersistentCollection) {
// return null;
// }
// If you got a list back from Hibernate
if (node instanceof List) {
return handleList(node, visited);
}
PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
PropertyDescriptor[] propertyDescriptors = propertyUtilsBean
.getPropertyDescriptors(node);
for (PropertyDescriptor p : propertyDescriptors) {
String name = p.getName();
if (logger.isDebugEnabled()) {
logger.debug(”descendThroughPoint(Object) - ” + name);
}
if (!”class”.equals(name)) {
Object property = propertyUtilsBean.getProperty(node, name);
if (property instanceof Set) {
try {
((Set) property).size();
// Ok, didn’t crash … must be possible to get the
// data from it…
Set s = new HashSet();
for (Object o : ((Set) property)) {
s.add(descendThroughNode(o, visited));
}
property = s;
propertyUtilsBean.setProperty(node, name, s);
} catch (LazyInitializationException l) {
if (logger.isDebugEnabled()) {
logger
.debug(”descendThroughPoint(Object) - clearing the Set:”
+ name);
}
propertyUtilsBean.setProperty(node, name,
new HashSet());
}
} else if (property instanceof List) {
try {
if (property instanceof PersistentBag) {
if (((PersistentBag) property).wasInitialized()) {
((List) property).size();
// Ok, didn’t crash … must be possible to
// get the
// data from it…
List s = new ArrayList();
for (Object o : ((List) property)) {
s.add(descendThroughNode(o, visited));
}
propertyUtilsBean
.setProperty(node, name, s);
} else {
propertyUtilsBean.setProperty(node, name,
new ArrayList());
}
}
} catch (Throwable l) {
if (logger.isDebugEnabled()) {
logger
.debug(”descendThroughPoint(Object) - clearing the List:”
+ name);
}
propertyUtilsBean.setProperty(node, name,
new ArrayList());
}
} else if (property instanceof Map) {
try {
((Map) property).keySet().size();
// Ok, didn’t crash … must be possible to get the
// data from it…
Map s = new HashMap();
for (Object o : ((Map) property).keySet()) {
s.put(o, ((Map) property)
.get(descendThroughNode(o, visited)));
}
propertyUtilsBean.setProperty(node, name, s);
} catch (LazyInitializationException l) {
if (logger.isDebugEnabled()) {
logger
.debug(”descendThroughPoint(Object) - clearing the Map:”
+ name);
}
propertyUtilsBean.setProperty(node, name,
new HashMap());
}
} else {
if (logger.isDebugEnabled()) {
logger
.debug(”descendThroughPoint(Object) - have not detected any collection membership .. lets recurse ….”);
}
// If we can think of any other Simple objects that we
// shouldn’t descend upon .. add them here.
if (!isIgnorable(property)
) {
// property = descendThroughNode(property, visited);
propertyUtilsBean.setProperty(node, name,
descendThroughNode(property, visited));
}
}
}
}
return node;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
/**
* Can we ignore the property? This method should be modified to just check
* if the class is annotated with the ∧ entity annotation.
*
* @param property
* @return
*/
private boolean isIgnorable(Object property) {
return (property == null) || (property instanceof String)
|| (property instanceof Double) || (property instanceof Long)
|| (property instanceof java.sql.Timestamp)
|| (property instanceof Date)
|| (property instanceof java.sql.Date);
}
@SuppressWarnings(”unchecked”)
private Object handleList(Object node, List visited) {
if (node instanceof PersistentBag) {
if (!((PersistentBag) node).wasInitialized()) {
return (List) new ArrayList();
}
}
List ret = new ArrayList();
for (Object o : (List) node) {
ret.add(descendThroughNode(o, visited));
continue;
}
return ret;
}
private String appendArgs(Object[] args) {
StringBuffer ret = new StringBuffer();
for (Object object : args) {
ret.append(”:”);
ret.append(object);
ret.append(”:”);
}
return ret.toString();
}
}
Leave a Reply