cleanup
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / hibernate / PostMergeEntityListener.java
index f080a552633fbfc09f2894378346ebcf72b800df..34ad7519edf9d30fd9eda34fc3964eebc6150189 100644 (file)
@@ -13,6 +13,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.hibernate.Hibernate;
 import org.hibernate.HibernateException;
 import org.hibernate.Session;
@@ -20,18 +22,17 @@ import org.hibernate.event.spi.MergeEvent;
 import org.hibernate.event.spi.MergeEventListener;
 
 import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.cdm.model.description.FeatureNode;
-import eu.etaxonomy.cdm.model.description.FeatureTree;
+import eu.etaxonomy.cdm.model.common.ITreeNode;
 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
-import eu.etaxonomy.cdm.model.taxon.TaxonNode;
 
 /**
  * @author cmathew
  * @since 23 Sep 2015
- *
  */
 public class PostMergeEntityListener implements MergeEventListener {
+
     private static final long serialVersionUID = 1565797119368313987L;
+    private static final Logger logger = LogManager.getLogger();
 
     private static Map<Session, Set<CdmBase>> newEntitiesMap = new ConcurrentHashMap<>();
 
@@ -49,8 +50,9 @@ public class PostMergeEntityListener implements MergeEventListener {
 
     @Override
     public void onMerge(MergeEvent event) throws HibernateException {
-        Object entity = event.getEntity();
-
+        //Note AM: TODO is there a reason why we neglect onMerge in this case?
+        //         Shouldn't we do something like "onMerge(event, new HashMap<>());"
+//        Object entity = event.getEntity();
     }
 
     @Override
@@ -61,9 +63,10 @@ public class PostMergeEntityListener implements MergeEventListener {
                 event.getResult() != null && CdmBase.class.isAssignableFrom(event.getResult().getClass())) {
             CdmBase original = (CdmBase) event.getOriginal();
             CdmBase result = (CdmBase) event.getResult();
-            removeNullFromCollections(result);
+            handleTreeNodes(result, original, event, copiedAlready);
             if(original != null && Hibernate.isInitialized(original) && original.getId() == 0 &&
                     result != null && Hibernate.isInitialized(result) && result.getId() > 0) {
+                //see IService#merge(detachedObject, returnTransientEntity)
                 original.setId(result.getId());
                 Set<CdmBase> newEntities = newEntitiesMap.get(event.getSession());
                 if(newEntities != null) {
@@ -71,46 +74,69 @@ public class PostMergeEntityListener implements MergeEventListener {
                 }
             }
         }
-
-
     }
 
-    /**
-     * @param entity
-     */
-    private static void removeNullFromCollections(Object entity) {
-        if (entity != null){
-            Class<?> entityClazz = entity.getClass();
-
-            if (TaxonNode.class.isAssignableFrom(entityClazz)){
-                TaxonNode node = (TaxonNode)entity;
-                node.removeNullValueFromChildren();
-            } else if (PolytomousKeyNode.class.isAssignableFrom(entityClazz)){
-                PolytomousKeyNode node = (PolytomousKeyNode) entity;
-                if (node.getChildren() != null && Hibernate.isInitialized(node.getChildren()) ){
-                    node.removeNullValueFromChildren();
-
+    private static void handleTreeNodes(CdmBase result, CdmBase original, MergeEvent event, Map copiedAlready) {
+        if (original != null){
+            Class<?> entityClazz = original.getClass();
+
+            if (PolytomousKeyNode.class.isAssignableFrom(entityClazz)){
+                //For some reason the children list needs to be read once
+                //to guarantee that the sortindex starts with zero
+                PolytomousKeyNode resultNode = (PolytomousKeyNode)result;
+                resultNode.getChildren().size();
+
+                // #10101 the following code tried to handle orphanRemoval for key nodes that were
+                // really removed from the graph. Generally the removal worked but it was not possible at this
+                // place to guarantee that the node which was removed from the parent was not used elsewhere
+                // (has a new parent). For this one needs to retrieve the new state of the node (or of its new parent).
+                // But this is not difficult or impossible at this point as the node is not part of the graph to
+                // to be merged or if it is because its new parent is part of the graph also, it is not guaranteed
+                // that it has already treated so far.
+                // Maybe it can better be handled by another type of listener therefore I keep this code here.
+                // See #10101 for further information on this issue and how it was solved.
+                // The implementation was partly copied from https://stackoverflow.com/questions/812364/how-to-determine-collection-changes-in-a-hibernate-postupdateeventlistener
+
+//                EventSource session = event.getSession();
+//                PersistenceContext pc = session.getPersistenceContext();
+//                CollectionEntry childrenEntry = pc.getCollectionEntry((PersistentCollection)resultNode.getChildren());
+//                List<PolytomousKeyNode> childrenEntrySnapshot = (List<PolytomousKeyNode>)childrenEntry.getSnapshot();
+//                if (childrenEntrySnapshot != null) {
+//                    for (PolytomousKeyNode snapshotChild: childrenEntrySnapshot){
+//                        if (!resultNode.getChildren().contains(snapshotChild)) {
+//                            EntityEntry currentChild = pc.getEntry(snapshotChild);
+//                            Object parent = currentChild == null ? null :
+//                                currentChild.getLoadedValue("parent");
+//                            if (parent == null || parent == resultNode) {
+//                                session.delete(snapshotChild);
+//                            }
+//                        }
+//                   }
+//                }
+            } else if (ITreeNode.class.isAssignableFrom(entityClazz)){ //TaxonNode or TermNode
+                //See PolytomousKeyNode above
+                //Not yet tested if necessary here, too.
+
+                try {
+                    ITreeNode<?> resultNode = (ITreeNode<?>)result;
+                    resultNode.getChildNodes().size();
+                } catch (Exception e) {
+                    //#10101
+                    //preliminary catched and logged as it seems to be the cause
+                    //for failing TaxEditor tests in TaxonNameEditorTest
+                    //methods
+                    //    * addDeleteAddHomotypicSynonym,
+                    //    * addDeleteAddHomotypicSynonymWithAnnotations
+                    //    * addHeterotypicSynonym
+                    //    * testAddHomotypicSynonym
+                    //All due to failed to lazily initialize a collection of role: eu.etaxonomy.cdm.model.taxon.TaxonNode.childNodes, could not initialize proxy - no Session
+                    //We need to check if this is an issue in the test behavior or in the solution itself.
+                    //We could also try to atleast add a check if the children list is attached to a session
+                    //before initializing it.
+                    //
+                    logger.warn("Error in PostMergeEntityListener during handleTreeNodes: " + e.getMessage());
                 }
-
-            }   else if(FeatureTree.class.isAssignableFrom(entityClazz)){
-
-                FeatureTree tree = (FeatureTree)entity;
-                for (FeatureNode node:tree.getRootChildren()){
-                    node.removeNullValueFromChildren();
-                    if (node.getChildNodes() != null){
-                        for (FeatureNode childNode: node.getChildNodes()){
-                            removeNullFromCollections(childNode);
-                        }
-                    }
-
-                }
-            } else if (FeatureNode.class.isAssignableFrom(entityClazz)){
-                FeatureNode node = (FeatureNode)entity;
-                node.removeNullValueFromChildren();
             }
-
         }
-
     }
-
-}
+}
\ No newline at end of file