|
|
@@ -0,0 +1,704 @@
|
|
|
+package commons.utility;
|
|
|
+
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.ObjectInput;
|
|
|
+import java.io.ObjectInputStream;
|
|
|
+import java.io.ObjectOutput;
|
|
|
+import java.io.ObjectOutputStream;
|
|
|
+import java.io.Serializable;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.lang.reflect.Modifier;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.Comparator;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Map.Entry;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+import org.hibernate.Hibernate;
|
|
|
+import org.hibernate.HibernateException;
|
|
|
+import org.hibernate.Session;
|
|
|
+import org.hibernate.SessionFactory;
|
|
|
+import org.hibernate.collection.spi.PersistentCollection;
|
|
|
+import org.hibernate.persister.entity.EntityPersister;
|
|
|
+import org.hibernate.proxy.HibernateProxy;
|
|
|
+import org.hibernate.proxy.LazyInitializer;
|
|
|
+import org.hibernate.type.Type;
|
|
|
+
|
|
|
+import commons.exception.ZSystemException;
|
|
|
+import commons.utility.database.hibernate.HibernateDatabase;
|
|
|
+
|
|
|
+/** Funzioni di utilit? per eseguire una deep-copy di un oggetto legato
|
|
|
+ * alla sessione di Hibernate. L'oggetto risultante ? completamente
|
|
|
+ * libero da proxy!
|
|
|
+ */
|
|
|
+public final class UnproxyObject
|
|
|
+{
|
|
|
+ /** La cache da utilizzare per comodo
|
|
|
+ */
|
|
|
+ private class Cache
|
|
|
+ {
|
|
|
+ /** Cache dei metadati raggruppati per classe */
|
|
|
+ private Map<Class<?>,EntityPersister> metadataClass = new HashMap<Class<?>, EntityPersister>();
|
|
|
+ /** Lista degli oggetti replaced */
|
|
|
+ private Map<Object,Object> replacedObjects = new HashMap<Object, Object>();
|
|
|
+ /** Lista delle collezioni replaced */
|
|
|
+ private Map<Object,Object> replacedCollections = new HashMap<Object, Object>();
|
|
|
+
|
|
|
+ private Cache target;
|
|
|
+
|
|
|
+ Cache()
|
|
|
+ {
|
|
|
+ target = this;
|
|
|
+ }
|
|
|
+
|
|
|
+ Cache(final Cache target)
|
|
|
+ {
|
|
|
+ this.target = target;
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("unused")
|
|
|
+ private final Map<Class<?>, EntityPersister> getMetadataClass()
|
|
|
+ {
|
|
|
+ return target == null ? metadataClass : target.metadataClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ private final Map<Object, Object> getNakedObjects()
|
|
|
+ {
|
|
|
+ return target == null ? replacedObjects : target.replacedObjects;
|
|
|
+ }
|
|
|
+
|
|
|
+ private final Map<Object, Object> getNakedCollections()
|
|
|
+ {
|
|
|
+ return target == null ? replacedCollections : target.replacedCollections;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private SessionFactory sessionFactory;
|
|
|
+ /** La lista delle collection che devono essere ricreate DOPO il processo
|
|
|
+ * di unproxy.Da utilizzare quando le collection sono create da oggetti
|
|
|
+ * creati parzialmente.
|
|
|
+ */
|
|
|
+ private List<Object> rehashCollection;
|
|
|
+ /** Cache con gli oggetti completamente costruiti (HibernateProxy -> nakedObject)
|
|
|
+ */
|
|
|
+ private final Cache fullConstructedCache = new Cache();
|
|
|
+ /** Cache con gli oggetti parzialmente costruiti (HibernateProxy -> nakedObject)
|
|
|
+ */
|
|
|
+ private final Cache partiallyConstructedCache = new Cache();
|
|
|
+
|
|
|
+ private UnproxyObject(final SessionFactory sessionFactory)
|
|
|
+ {
|
|
|
+ this.sessionFactory = sessionFactory;
|
|
|
+ rehashCollection = new ArrayList<Object>();
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Instance a new empty object calling empty ctor
|
|
|
+ * @param <T>
|
|
|
+ * @param clazz
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final <T> T instance0(final Class<T> clazz)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return clazz.getConstructor().newInstance();
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ throw new ZSystemException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /** Set the value of a field via reflection
|
|
|
+ * @param f
|
|
|
+ * @param target
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ private final void setFieldValue(final Field f,final Object target,final Object value)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ f.set(target, value);
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ throw new ZSystemException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /** Get the value of a field via reflection
|
|
|
+ * @param f
|
|
|
+ * @param target
|
|
|
+ * @param value
|
|
|
+ */
|
|
|
+ private final Object getFieldValue(final Field f,final Object target)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return f.get(target);
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ throw new ZSystemException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /** Verifica se un campo ? modificabile o meno (ovvero non ? final oppure static)
|
|
|
+ * @param property il campo da controllare
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final boolean fieldIsModifiable(final Field property)
|
|
|
+ {
|
|
|
+ final int modifiers = property.getModifiers();
|
|
|
+ final boolean unmodifiable = Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers);
|
|
|
+ return !unmodifiable;
|
|
|
+ }
|
|
|
+ /** Estrazione di tutti i campi presenti in una classe e nelle sue superclassi
|
|
|
+ * @param clazz
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final List<Field> retrieveFields(final Class<?> clazz)
|
|
|
+ {
|
|
|
+ final List<Field> fields = new ArrayList<Field>();
|
|
|
+ Class<?> currentClass = clazz;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ fields.addAll(Arrays.asList(currentClass.getDeclaredFields()));
|
|
|
+ currentClass = currentClass.getSuperclass();
|
|
|
+ } while(currentClass != null);
|
|
|
+ Collections.sort(fields, new Comparator<Field>(){
|
|
|
+ public int compare(Field o1, Field o2)
|
|
|
+ {
|
|
|
+ return o1.getName().compareTo(o2.getName());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return fields;
|
|
|
+ }
|
|
|
+ /** Esegue la rimozione dei proxy di tutti gli oggetti di una collection
|
|
|
+ * @param collectionClass l'implementazione reale della collection
|
|
|
+ * @param collection la collection di origine
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final Collection<?> makeNakedCollection(final Class<?> collectionClass,final Collection<?> collection)
|
|
|
+ {
|
|
|
+ final Collection<Object> unproxiedCollection = (Collection<Object>) instance0(collectionClass);
|
|
|
+ for(final Object maybeProxy : collection)
|
|
|
+ unproxiedCollection.add(makeNakedObject(maybeProxy));
|
|
|
+ return unproxiedCollection;
|
|
|
+ }
|
|
|
+ /** Esegue la rimozione dei proxy di un set ed eventualmente,nel caso gli oggetti aggiunti al Set
|
|
|
+ * siano completati solo parzialmente,aggiunge la lista alle Collection da re-indicizzare
|
|
|
+ * @param collectionClass
|
|
|
+ * @param collection
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final Set<?> makeNakedSet(final Class<?> collectionClass,final Set<?> collection)
|
|
|
+ {
|
|
|
+ boolean rehashCollection = false;
|
|
|
+ // Verifica se uno degli oggetti presenti nel Set ? solo parzialmente costruito!
|
|
|
+ for(final Iterator<?> maybeProxies = collection.iterator();!rehashCollection && maybeProxies.hasNext();)
|
|
|
+ rehashCollection = rehashCollection || null != getNakedObjectFromCache(partiallyConstructedCache, maybeProxies.next());
|
|
|
+ // Ricrea la collection senza proxy
|
|
|
+ final Set<Object> unproxiedCollection = (Set<Object>) makeNakedCollection(collectionClass,collection);
|
|
|
+ // Nel caso la collection fosse costruita su oggetti parzialmente costruiti allora
|
|
|
+ // la collection ? da marcare per la re-indiicizzazione
|
|
|
+ if(rehashCollection)
|
|
|
+ addCollectionToRehasingList(unproxiedCollection);
|
|
|
+ return unproxiedCollection;
|
|
|
+ }
|
|
|
+ /** Esegue la rimozione dei proxy di tutti gli oggetti di una map
|
|
|
+ * @param collection la collection di origine
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final Map<?,?> makeNakedMap(final Map<?,?> collection)
|
|
|
+ {
|
|
|
+ boolean rehashCollection = false;
|
|
|
+ final Map<Object,Object> unproxiedMap = new HashMap<Object,Object>();
|
|
|
+ for(final Object key : collection.keySet())
|
|
|
+ {
|
|
|
+ // Verifica se la chiave ? una
|
|
|
+ rehashCollection = rehashCollection || null != getNakedObjectFromCache(partiallyConstructedCache, key);
|
|
|
+ final Object maybeProxy = collection.get(key);
|
|
|
+ unproxiedMap.put(key,makeNakedObject(maybeProxy));
|
|
|
+ }
|
|
|
+ // Nel caso la collection fosse costruita su oggetti parzialmente costruiti allora
|
|
|
+ // la collection ? da marcare per la re-indiicizzazione
|
|
|
+ if(rehashCollection)
|
|
|
+ addCollectionToRehasingList(unproxiedMap);
|
|
|
+ return unproxiedMap;
|
|
|
+ }
|
|
|
+ /** Se una classe implementa {@link HibernateProxy} viene ritornato l'oggetto castato a HibernateProxy
|
|
|
+ * @param maybeProxy
|
|
|
+ * @return null se l'oggetto ? null oppure non ? un HibernateProxy
|
|
|
+ */
|
|
|
+ private final HibernateProxy getHibernateProxy(final Object maybeProxy)
|
|
|
+ {
|
|
|
+ return maybeProxy instanceof HibernateProxy ? (HibernateProxy)maybeProxy : null;
|
|
|
+ }
|
|
|
+ /** Controlla se un oggetto ? una {@link Collection} oppure una {@link Map}
|
|
|
+ * @param maybeCollection
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final boolean isCollection(final Object maybeCollection)
|
|
|
+ {
|
|
|
+ return maybeCollection instanceof Collection || maybeCollection instanceof Map;
|
|
|
+ }
|
|
|
+ /** Data una classe ritorna i metadati di mapping relativi
|
|
|
+ * @param clazz
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final EntityPersister getClassMetadata(final Class<?> clazz)
|
|
|
+ {
|
|
|
+ EntityPersister value = null;
|
|
|
+ if(null != sessionFactory)
|
|
|
+ value = sessionFactory.unwrap(org.hibernate.engine.spi.SessionFactoryImplementor.class)
|
|
|
+ .getMappingMetamodel().getEntityDescriptor(clazz);
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ /** Estrazione del mapping di Hibernate di una property.Per i component si una la
|
|
|
+ * dot notation
|
|
|
+ * @param entityPersister
|
|
|
+ * @param propertyName
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final Type getHibernatePropertyType(final EntityPersister entityPersister,final String propertyName)
|
|
|
+ {
|
|
|
+ Type type = null;
|
|
|
+ if(null != entityPersister)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ type = entityPersister.getPropertyType(propertyName);
|
|
|
+ }
|
|
|
+ catch(HibernateException e)
|
|
|
+ {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+ /** Verifica se un oggetto richiede la rimozione del proxy
|
|
|
+ * @param maybeProxy
|
|
|
+ * @param propertyMapping
|
|
|
+ * @return true se l'oggetto rispetta {@link UnproxyObject#getHibernateProxy(Object)} oppure
|
|
|
+ * la mappatura riguarda un'associazione,un componente oppure una collection
|
|
|
+ */
|
|
|
+ private final boolean objectShouldBeNaked(final Object maybeProxy,final Type propertyMapping)
|
|
|
+ {
|
|
|
+ return getHibernateProxy(maybeProxy) != null ||
|
|
|
+ (propertyMapping != null && (propertyMapping.isAssociationType() || propertyMapping.isComponentType() || propertyMapping.isCollectionType()));
|
|
|
+ }
|
|
|
+ /** L'oggetto necessita di inizializzazione?
|
|
|
+ * @param maybeProxy
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final boolean objectSholdBeInitialized(final Object maybeProxy)
|
|
|
+ {
|
|
|
+ return !Hibernate.isInitialized(maybeProxy);
|
|
|
+ }
|
|
|
+ /** Dato un oggetto proxy viene ritornata la relativa versione
|
|
|
+ * replace
|
|
|
+ * @param La cache in cui fare la ricerca
|
|
|
+ * @param proxyObject
|
|
|
+ * @return null se non esiste nessun replace,il replace in caso contrario
|
|
|
+ */
|
|
|
+ private final Object getNakedObjectFromCache(final Cache cache,final Object proxyObject)
|
|
|
+ {
|
|
|
+ if(cache.getNakedObjects().containsKey(proxyObject))
|
|
|
+ return cache.getNakedObjects().get(proxyObject);
|
|
|
+ if(cache.getNakedCollections().containsKey(proxyObject))
|
|
|
+ return cache.getNakedCollections().get(proxyObject);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ /** Associa ad un oggetto proxy la relativa implementazione naked
|
|
|
+ * @param La cache a cui aggiungere l'oggetto
|
|
|
+ * @param proxyObject
|
|
|
+ * @param nakedObject
|
|
|
+ */
|
|
|
+ private final void addNakedObjectToCache(final Cache cache,final Object proxyObject,final Object nakedObject)
|
|
|
+ {
|
|
|
+ if(isCollection(proxyObject))
|
|
|
+ cache.getNakedCollections().put(proxyObject, nakedObject);
|
|
|
+ else
|
|
|
+ cache.getNakedObjects().put(proxyObject, nakedObject);
|
|
|
+ }
|
|
|
+ /** Rimuove un proxy da una cache
|
|
|
+ * @param cache
|
|
|
+ * @param proxyObject
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final Object removeNakedObjectFromCache(final Cache cache,final Object proxyObject)
|
|
|
+ {
|
|
|
+ if(isCollection(proxyObject))
|
|
|
+ return cache.getNakedCollections().remove(proxyObject);
|
|
|
+ else
|
|
|
+ return cache.getNakedObjects().remove(proxyObject);
|
|
|
+ }
|
|
|
+ /** Aggiunge una {@link Collection} alla lista delle collection da re-indicizzare.
|
|
|
+ * Senza duplicazioni (check tramite reference)
|
|
|
+ * @param collection
|
|
|
+ */
|
|
|
+ private void addCollectionToRehasingList(final Object collection)
|
|
|
+ {
|
|
|
+ for(final Object c : rehashCollection)
|
|
|
+ /* Utilizza i reference e non {@link Object#equals(Object)} per i controlli
|
|
|
+ * di identit? perch? gli hash degli oggetti nella collection potrebbero
|
|
|
+ * cambiare durante la costruzione dell'oggetto!
|
|
|
+ */
|
|
|
+ if(c == collection)
|
|
|
+ return;
|
|
|
+ rehashCollection.add(collection);
|
|
|
+ }
|
|
|
+ /** Elimina i proxy da un oggetto marcato come collection
|
|
|
+ * @param proxyCollection
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private final Object makeNakedCollection(final Object proxyCollection)
|
|
|
+ {
|
|
|
+ Class<?> impl = proxyCollection.getClass();
|
|
|
+ // In caso di una PersistentCollection bisogna utilizzare una impl di default
|
|
|
+ if(proxyCollection instanceof PersistentCollection)
|
|
|
+ {
|
|
|
+ if(Map.class.isAssignableFrom(impl))
|
|
|
+ impl = HashMap.class;
|
|
|
+ if(List.class.isAssignableFrom(impl))
|
|
|
+ impl = ArrayList.class;
|
|
|
+ if(Set.class.isAssignableFrom(impl))
|
|
|
+ impl = HashSet.class;
|
|
|
+ }
|
|
|
+ // Map (non deriva da Collection)
|
|
|
+ if(Map.class.isAssignableFrom(impl))
|
|
|
+ return makeNakedMap((Map<?,?>) proxyCollection);
|
|
|
+ if(Set.class.isAssignableFrom(impl))
|
|
|
+ return makeNakedSet(impl,(Set<?>) proxyCollection);
|
|
|
+ // Collection generica
|
|
|
+ if(Collection.class.isAssignableFrom(impl))
|
|
|
+ return makeNakedCollection(impl,(Collection<?>) proxyCollection);
|
|
|
+ throw new ZSystemException(new ClassCastException(proxyCollection.getClass().getName() + " must be a assignable from " + PersistentCollection.class.getName()));
|
|
|
+ }
|
|
|
+ /** Elimina i proxy da un oggetto marcato come component. I component non hanno cache perch? sono 'annegati'
|
|
|
+ * all'interno dell'oggetto ospitante
|
|
|
+ * @param componentEntity il componente da lavorare
|
|
|
+ * @param parentComponentName il nome della propriet? del componente nell'oggetto parent
|
|
|
+ * @param parentProxyObject l'entit? che contiene il component
|
|
|
+ * @param parentProxyObjectClassMetadata le info di mapping del parent
|
|
|
+ * @return
|
|
|
+ * @throws ZSystemException
|
|
|
+ */
|
|
|
+ private final Object makeNakedComponent(final Object componentEntity,final String parentComponentName,final Object parentProxyObject,final EntityPersister parentProxyObjectEntityPersister) throws ZSystemException
|
|
|
+ {
|
|
|
+ // La vera classe dell'oggetto (in caso di proxy)
|
|
|
+ final Class<?> componentEntityClass = Hibernate.getClass(componentEntity);
|
|
|
+ final Object nakedComponent = instance0(componentEntityClass);
|
|
|
+ // Il valore di una property
|
|
|
+ Object componentPropertyValue;
|
|
|
+ Object nakedPropertyValue;
|
|
|
+ Type propertyMappingType;
|
|
|
+
|
|
|
+ // Il lavoro va fatto su tutti i campi dell'entity da hidratare!
|
|
|
+ for(final Field propertyField : retrieveFields(componentEntityClass))
|
|
|
+ {
|
|
|
+ if(!fieldIsModifiable(propertyField)) continue;
|
|
|
+ final String propertyName = propertyField.getName();
|
|
|
+ // Rendilo accessibile e leggi il valore
|
|
|
+ boolean propertyFieldAccessible = propertyField.isAccessible();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ propertyField.setAccessible(true);
|
|
|
+ // Leggi il valore della property!
|
|
|
+ if(null != (nakedPropertyValue = componentPropertyValue = getFieldValue(propertyField, componentEntity)))
|
|
|
+ {
|
|
|
+ // Potrebbe essere necessaria l'inizializzazione!
|
|
|
+ if(objectSholdBeInitialized(componentPropertyValue))
|
|
|
+ Hibernate.initialize(componentPropertyValue);
|
|
|
+
|
|
|
+ // Nel caso sia un oggetto di mappato con Hibernate estraine le informazioni:per i component le informazioni utilizzano
|
|
|
+ // la dot notation per accedere ai nomi dei campi!
|
|
|
+ final String dottedComponentPropertyName = new StringBuffer(parentComponentName).append('.').append(propertyName).toString();
|
|
|
+ if(null != (propertyMappingType = getHibernatePropertyType(parentProxyObjectEntityPersister, dottedComponentPropertyName)))
|
|
|
+ {
|
|
|
+ // Solo gli oggetti rimappati vanno gestiti!
|
|
|
+ // Per esempio le collection che non sono mapped non conterranno,di
|
|
|
+ // sicuro,degli oggetti rimappati!
|
|
|
+ if(objectShouldBeNaked(nakedPropertyValue, propertyMappingType))
|
|
|
+ {
|
|
|
+ if(isCollection(componentPropertyValue))
|
|
|
+ {
|
|
|
+ nakedPropertyValue = makeNakedCollection(componentPropertyValue);
|
|
|
+ addNakedObjectToCache(fullConstructedCache, componentPropertyValue, nakedPropertyValue);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(propertyMappingType.isComponentType())
|
|
|
+ nakedPropertyValue = makeNakedComponent(componentPropertyValue, dottedComponentPropertyName, parentProxyObject, parentProxyObjectEntityPersister);
|
|
|
+ else
|
|
|
+ nakedPropertyValue = makeNakedObject(componentPropertyValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Un oggetto di cui non sono riuscito ad estrarre le info,ma che
|
|
|
+ // ? un proxy di hibernate: gli devo dare una chance!
|
|
|
+ if(getHibernateProxy(componentPropertyValue) != null)
|
|
|
+ nakedPropertyValue = makeNakedObject(componentPropertyValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Rimpiazza il valore della property nell'oggetto copia
|
|
|
+ setFieldValue(propertyField, nakedComponent, nakedPropertyValue);
|
|
|
+ }
|
|
|
+ catch(ZSystemException e)
|
|
|
+ {
|
|
|
+ final boolean isIllegalAccess = e.getCause() instanceof IllegalAccessException;
|
|
|
+ if(isIllegalAccess)
|
|
|
+ {
|
|
|
+ // I campi che non sono modificabili possono generare l'errore,? normale!
|
|
|
+ if(!fieldIsModifiable(propertyField))
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ propertyField.setAccessible(propertyFieldAccessible);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nakedComponent;
|
|
|
+ }
|
|
|
+ /** Esegue una deep-copy di un oggetto
|
|
|
+ * @param <T>
|
|
|
+ * @param proxyObject L'oggetto sorgente
|
|
|
+ * @return il parametro replaceEntity
|
|
|
+ * @throws ZSystemException
|
|
|
+ */
|
|
|
+ private final Object makeNakedObject(final Object proxyObject) throws ZSystemException
|
|
|
+ {
|
|
|
+ // Gli oggetti null tornano null!
|
|
|
+ if(null == proxyObject)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ // Per prima cosa verifica nella cache se esiste l'oggetto! (prima in quelli parziali,che sono meno...
|
|
|
+ Object nakedObject = getNakedObjectFromCache(partiallyConstructedCache,proxyObject);
|
|
|
+ if(null == nakedObject)
|
|
|
+ // ...e poi eventualmente in quelli completi!
|
|
|
+ nakedObject = getNakedObjectFromCache(fullConstructedCache,proxyObject);
|
|
|
+ if(null != nakedObject)
|
|
|
+ return nakedObject;
|
|
|
+
|
|
|
+ // Gestione dei proxy!
|
|
|
+ final HibernateProxy proxy = getHibernateProxy(proxyObject);
|
|
|
+ final LazyInitializer li = proxy != null ? proxy.getHibernateLazyInitializer() : null;
|
|
|
+ // La vera classe dell'oggetto (in caso di proxy)
|
|
|
+ final Class<?> nakedObjectClass = li != null ? li.getPersistentClass() : proxyObject.getClass();
|
|
|
+ // Il valore di una property
|
|
|
+ Object proxyPropertyValue = null;
|
|
|
+ Object nakedPropertyValue = null;
|
|
|
+ final EntityPersister proxyObjectMetadata = getClassMetadata(nakedObjectClass);
|
|
|
+ Type propertyMappingType = null;
|
|
|
+
|
|
|
+ // L'oggetto da utilizzare come rimpiazzo!
|
|
|
+ nakedObject = instance0(nakedObjectClass);
|
|
|
+ // Subito mettilo nella cache dei parziali cosi chiunque potr? accedervi mentre lo inizializzo!
|
|
|
+ addNakedObjectToCache(partiallyConstructedCache, proxyObject, nakedObject);
|
|
|
+
|
|
|
+ // Il lavoro va fatto su tutti i campi dell'entity da hidratare!
|
|
|
+ for(final Field propertyField : retrieveFields(nakedObjectClass))
|
|
|
+ {
|
|
|
+ if(!fieldIsModifiable(propertyField)) continue;
|
|
|
+ final String propertyName = propertyField.getName();
|
|
|
+ // Rendilo accessibile e leggi il valore
|
|
|
+ boolean propertyFieldAccessible = propertyField.isAccessible();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ propertyField.setAccessible(true);
|
|
|
+ // Leggi il valore della property!
|
|
|
+ if(null != (nakedPropertyValue = proxyPropertyValue = getFieldValue(propertyField, li != null ? li.getImplementation() : proxyObject)))
|
|
|
+ {
|
|
|
+ // Potrebbe essere necessaria l'inizializzazione!
|
|
|
+ if(objectSholdBeInitialized(proxyPropertyValue))
|
|
|
+ Hibernate.initialize(proxyPropertyValue);
|
|
|
+
|
|
|
+ // Nel caso sia un oggetto di mappato con Hibernate estraine le informazioni...
|
|
|
+ if(null != (propertyMappingType = getHibernatePropertyType(proxyObjectMetadata, propertyName)))
|
|
|
+ {
|
|
|
+ // Solo gli oggetti rimappati vanno gestiti!
|
|
|
+ // Per esempio le collection che non sono mapped non conterranno,di
|
|
|
+ // sicuro,degli oggetti rimappati!
|
|
|
+ if(objectShouldBeNaked(nakedPropertyValue, propertyMappingType))
|
|
|
+ {
|
|
|
+ if(isCollection(proxyPropertyValue))
|
|
|
+ {
|
|
|
+ nakedPropertyValue = makeNakedCollection(proxyPropertyValue);
|
|
|
+ addNakedObjectToCache(fullConstructedCache, proxyPropertyValue, nakedPropertyValue);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(propertyMappingType.isComponentType())
|
|
|
+ nakedPropertyValue = makeNakedComponent(proxyPropertyValue, propertyName, nakedObject, proxyObjectMetadata);
|
|
|
+ else
|
|
|
+ nakedPropertyValue = makeNakedObject(proxyPropertyValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Un oggetto di cui non sono riuscito ad estrarre le info,ma che
|
|
|
+ // ? un proxy di hibernate: gli devo dare una chance!
|
|
|
+ if(getHibernateProxy(proxyPropertyValue) != null)
|
|
|
+ nakedPropertyValue = makeNakedObject(proxyPropertyValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Rimpiazza il valore della property nell'oggetto copia
|
|
|
+ setFieldValue(propertyField, nakedObject, nakedPropertyValue);
|
|
|
+ }
|
|
|
+ catch(ZSystemException e)
|
|
|
+ {
|
|
|
+ final boolean isIllegalAccess = e.getCause() instanceof IllegalAccessException;
|
|
|
+ if(isIllegalAccess)
|
|
|
+ {
|
|
|
+ // I campi che non sono modificabili possono generare l'errore,? normale!
|
|
|
+ if(!fieldIsModifiable(propertyField))
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ propertyField.setAccessible(propertyFieldAccessible);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Sposta l'oggetto dalla cache dei parzialmente a quelli dei completamente costruiti!
|
|
|
+ addNakedObjectToCache(fullConstructedCache, proxyObject, removeNakedObjectFromCache(partiallyConstructedCache, proxyObject));
|
|
|
+ return nakedObject;
|
|
|
+ }
|
|
|
+ /** Re-indicizzazione di un {@link Set}
|
|
|
+ * @param c
|
|
|
+ */
|
|
|
+ private void rehashSet(final Set<Object> c)
|
|
|
+ {
|
|
|
+ final Object[] all = c.toArray();
|
|
|
+ c.clear();
|
|
|
+ for(final Object o : all)
|
|
|
+ c.add(o);
|
|
|
+ }
|
|
|
+ /** Re-indicizzazione di una {@link Map}
|
|
|
+ * @param c
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private void rehashMap(final Map<Object,Object> c)
|
|
|
+ {
|
|
|
+ /* E' necessario fare una copia delle chiavi,
|
|
|
+ * svuotare la collection e reinserire gli oggetti
|
|
|
+ * in modo che siano re-indicizzati completamente!
|
|
|
+ */
|
|
|
+ final Entry[] entries = c.entrySet().toArray(new Entry[c.size()]);
|
|
|
+ c.clear();
|
|
|
+ for(final Entry entry : entries)
|
|
|
+ c.put(entry.getKey(),entry.getValue());
|
|
|
+ }
|
|
|
+ /** Postprocessing sulle collection di cui effettuarne il rehash perch?
|
|
|
+ * costruite,in origine,su oggetti parzialmente costruiti!
|
|
|
+ */
|
|
|
+ private void rehashCollection()
|
|
|
+ {
|
|
|
+ for(final Object c : rehashCollection)
|
|
|
+ {
|
|
|
+ if(Set.class.isAssignableFrom(c.getClass()))
|
|
|
+ rehashSet((Set<Object>) c);
|
|
|
+ if(Map.class.isAssignableFrom(c.getClass()))
|
|
|
+ rehashMap((Map<Object,Object>) c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Esegue l'unproxy di un oggetto utilizzando degli oggetti nuovi. Esegue una deep-copy rimuovendo
|
|
|
+ * tutti i proxy presenti.
|
|
|
+ * Gli oggetti proxy che non sono presenti nella lista delle proprties da aggiornare vengono
|
|
|
+ * azzerati
|
|
|
+ * @param <T>
|
|
|
+ * @param entity
|
|
|
+ * @return
|
|
|
+ * @throws ZSystemException
|
|
|
+ */
|
|
|
+ public final static <T> T deepCopyObject(final T entity,final SessionFactory sessionFactory) throws ZSystemException
|
|
|
+ {
|
|
|
+ if(null == entity) return null;
|
|
|
+ UnproxyObject impl = new UnproxyObject(sessionFactory);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return (T) impl.makeNakedObject(entity);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ impl.rehashCollection();
|
|
|
+ impl = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Deprecated
|
|
|
+ public final static <T> T deepCopyObject(final T entity) throws ZSystemException
|
|
|
+ {
|
|
|
+ return deepCopyObject(entity, (SessionFactory)null);
|
|
|
+ }
|
|
|
+ public final static <T> T deepCopyObject(final T entity,final HibernateDatabase hibernateDatabase) throws ZSystemException
|
|
|
+ {
|
|
|
+ SessionFactory sessionFactory = null;
|
|
|
+ if(null != hibernateDatabase)
|
|
|
+ sessionFactory = hibernateDatabase.getHibernateSession().getSessionFactory();
|
|
|
+ return deepCopyObject(entity, sessionFactory);
|
|
|
+ }
|
|
|
+ public final static <T> T deepCopyObject(final T entity,final Session session) throws ZSystemException
|
|
|
+ {
|
|
|
+ SessionFactory sessionFactory = null;
|
|
|
+ if(null != session)
|
|
|
+ sessionFactory = session.getSessionFactory();
|
|
|
+ return deepCopyObject(entity, sessionFactory);
|
|
|
+ }
|
|
|
+ /** Clona un oggetto tramite serializzazione!
|
|
|
+ * @param <T>
|
|
|
+ * @param entity
|
|
|
+ * @return
|
|
|
+ * @throws ZSystemException
|
|
|
+ */
|
|
|
+ public final static <T extends Serializable> T cloneObject(final T entity) throws ZSystemException
|
|
|
+ {
|
|
|
+ ByteArrayOutputStream baos = null;
|
|
|
+ ObjectOutput oo = null;
|
|
|
+ ObjectInput oi = null;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ baos = new ByteArrayOutputStream(4096);
|
|
|
+ oo = new ObjectOutputStream(baos);
|
|
|
+ oo.writeObject(entity);
|
|
|
+ oo.close();
|
|
|
+
|
|
|
+ oi = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
|
|
|
+ return (T)oi.readObject();
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ throw new ZSystemException(e);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if(null != baos)
|
|
|
+ baos.close();
|
|
|
+ if(null != oo)
|
|
|
+ oo.close();
|
|
|
+ if(null != oi)
|
|
|
+ oi.close();
|
|
|
+ baos = null;
|
|
|
+ oo = null;
|
|
|
+ oi = null;
|
|
|
+ }
|
|
|
+ catch(IOException e)
|
|
|
+ {
|
|
|
+ throw new ZSystemException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|