package com.oreilly.ecomm.reactor; import java.lang.reflect.Field; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.SortedMap; import java.util.SortedSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Logger; import org.hibernate.LazyInitializationException; import org.hibernate.collection.PersistentBag; import org.hibernate.collection.PersistentList; import org.hibernate.collection.PersistentMap; import org.hibernate.collection.PersistentSet; import org.hibernate.collection.PersistentSortedMap; import org.hibernate.collection.PersistentSortedSet; /** * Completely removes all Hibernate's tentacles from a retreived POJO.
* This is necessary because Hibernate uses CGLib to decorate objects * with proxy implementations of certain declared fields, particularly collections. * * @author haren */ @SuppressWarnings({"unchecked", "deprecation"}) public class Dehydrator { private static final Logger logger = Logger.getLogger(Dehydrator.class); private static Map hibernateCollectionsMap = new HashMap(); static { hibernateCollectionsMap.put(PersistentBag.class, ArrayList.class); hibernateCollectionsMap.put(PersistentList.class, ArrayList.class); hibernateCollectionsMap.put(PersistentMap.class, HashMap.class); hibernateCollectionsMap.put(PersistentSet.class, HashSet.class); hibernateCollectionsMap.put(PersistentSortedMap.class, SortedMap.class); hibernateCollectionsMap.put(PersistentSortedSet.class, SortedSet.class); } public static T dehydrate(Object original) { return (T) deepCloneAndSanitize(original, new HashMap()); } private static Object deepCloneAndSanitize(Object original, Map cache) { if (original == null) return null; // if we've already encountered this original reference, retreive the copy if (cache.containsKey(original)) { return cache.get(original); } // check for immutables, no need to clone if (original instanceof String || original instanceof Character || original instanceof Class) { return original; } if (original instanceof Number) { // "Number" classes are immutable, except for Atomics... if (!(original instanceof AtomicInteger) && !(original instanceof AtomicLong)) return original; } // assume, then, that it's an object that hibernate has gotten its // paws on. try to sanitize it. if this is not the case, nothing will come back, // so move on to the next trick. Object candidate = sanitize(original, cache); if (candidate != null) return candidate; // At this point, we're confident it's a plain 'ol mutable object, so clone it // using reflection return cloneViaReflection(original, cache); } /** * @param original * @param cache * @return */ private static Object cloneViaReflection(Object original, Map cache) { Class c = original.getClass(); Field[] fields = ClassUtilities.getFields(c, false); try { Object copy = instantiate(original); // Put into cache cache.put(original, copy); for (Field f : fields) { Object object = f.get(original); object = deepCloneAndSanitize(object, cache); f.set(copy, object); } return copy; } catch (Throwable t) { logger.error("Exception during clone (returning original): " + t.getMessage()); return original; } } private static final Object sanitize(Object original, Map cache) { try { if (original instanceof java.sql.Timestamp) { Date date = new Date(); date.setDate(((Timestamp) original).getDate()); return date; } if (hibernateCollectionsMap.containsKey(original.getClass())) { Collection col = (Collection) hibernateCollectionsMap.get(original.getClass()).newInstance(); cache.put(original, col); for (Object item : ((Collection) original)) { col.add(deepCloneAndSanitize(item, cache)); } return col; } return null; } catch (LazyInitializationException le) { logger.error("LazyInitializationException during clone: returning null " + le.getMessage()); return null; } catch (Exception e) { logger.error("Exception during clone (returning original): " + e.getMessage()); return original; } } private static final Object instantiate(Object original) throws InstantiationException, IllegalAccessException { return original.getClass().newInstance(); } }