2 * Copyright (C) 2015 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.persistence
.hibernate
;
11 import java
.util
.HashSet
;
14 import java
.util
.concurrent
.ConcurrentHashMap
;
16 import org
.apache
.logging
.log4j
.LogManager
;
17 import org
.apache
.logging
.log4j
.Logger
;
18 import org
.hibernate
.Hibernate
;
19 import org
.hibernate
.HibernateException
;
20 import org
.hibernate
.Session
;
21 import org
.hibernate
.event
.spi
.MergeEvent
;
22 import org
.hibernate
.event
.spi
.MergeEventListener
;
24 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
25 import eu
.etaxonomy
.cdm
.model
.common
.ITreeNode
;
26 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
32 public class PostMergeEntityListener
implements MergeEventListener
{
34 private static final long serialVersionUID
= 1565797119368313987L;
35 private static final Logger logger
= LogManager
.getLogger();
37 private static Map
<Session
, Set
<CdmBase
>> newEntitiesMap
= new ConcurrentHashMap
<>();
39 public static void addSession(Session session
) {
40 newEntitiesMap
.put(session
, new HashSet
<>());
43 public static void removeSession(Session session
) {
44 newEntitiesMap
.remove(session
);
47 public static Set
<CdmBase
> getNewEntities(Session session
) {
48 return newEntitiesMap
.get(session
);
52 public void onMerge(MergeEvent event
) throws HibernateException
{
53 //Note AM: TODO is there a reason why we neglect onMerge in this case?
54 // Shouldn't we do something like "onMerge(event, new HashMap<>());"
55 // Object entity = event.getEntity();
59 public void onMerge(MergeEvent event
, Map copiedAlready
) throws HibernateException
{
60 // any new entities are added to a map which is retrieved at the end of the
61 // CdmEntityDaoBase.merge(T transientObject, boolean returnTransientEntity) call
62 if(event
.getOriginal() != null && CdmBase
.class.isAssignableFrom(event
.getOriginal().getClass()) &&
63 event
.getResult() != null && CdmBase
.class.isAssignableFrom(event
.getResult().getClass())) {
64 CdmBase original
= (CdmBase
) event
.getOriginal();
65 CdmBase result
= (CdmBase
) event
.getResult();
66 handleTreeNodes(result
, original
, event
, copiedAlready
);
67 if(original
!= null && Hibernate
.isInitialized(original
) && original
.getId() == 0 &&
68 result
!= null && Hibernate
.isInitialized(result
) && result
.getId() > 0) {
69 //see IService#merge(detachedObject, returnTransientEntity)
70 original
.setId(result
.getId());
71 Set
<CdmBase
> newEntities
= newEntitiesMap
.get(event
.getSession());
72 if(newEntities
!= null) {
73 newEntities
.add(result
);
79 private static void handleTreeNodes(CdmBase result
, CdmBase original
, MergeEvent event
, Map copiedAlready
) {
80 if (original
!= null){
81 Class
<?
> entityClazz
= original
.getClass();
83 if (PolytomousKeyNode
.class.isAssignableFrom(entityClazz
)){
84 //For some reason the children list needs to be read once
85 //to guarantee that the sortindex starts with zero
86 PolytomousKeyNode resultNode
= (PolytomousKeyNode
)result
;
87 resultNode
.getChildren().size();
89 // #10101 the following code tried to handle orphanRemoval for key nodes that were
90 // really removed from the graph. Generally the removal worked but it was not possible at this
91 // place to guarantee that the node which was removed from the parent was not used elsewhere
92 // (has a new parent). For this one needs to retrieve the new state of the node (or of its new parent).
93 // But this is not difficult or impossible at this point as the node is not part of the graph to
94 // to be merged or if it is because its new parent is part of the graph also, it is not guaranteed
95 // that it has already treated so far.
96 // Maybe it can better be handled by another type of listener therefore I keep this code here.
97 // See #10101 for further information on this issue and how it was solved.
98 // The implementation was partly copied from https://stackoverflow.com/questions/812364/how-to-determine-collection-changes-in-a-hibernate-postupdateeventlistener
100 // EventSource session = event.getSession();
101 // PersistenceContext pc = session.getPersistenceContext();
102 // CollectionEntry childrenEntry = pc.getCollectionEntry((PersistentCollection)resultNode.getChildren());
103 // List<PolytomousKeyNode> childrenEntrySnapshot = (List<PolytomousKeyNode>)childrenEntry.getSnapshot();
104 // if (childrenEntrySnapshot != null) {
105 // for (PolytomousKeyNode snapshotChild: childrenEntrySnapshot){
106 // if (!resultNode.getChildren().contains(snapshotChild)) {
107 // EntityEntry currentChild = pc.getEntry(snapshotChild);
108 // Object parent = currentChild == null ? null :
109 // currentChild.getLoadedValue("parent");
110 // if (parent == null || parent == resultNode) {
111 // session.delete(snapshotChild);
116 } else if (ITreeNode
.class.isAssignableFrom(entityClazz
)){ //TaxonNode or TermNode
117 //See PolytomousKeyNode above
118 //Not yet tested if necessary here, too.
121 ITreeNode
<?
> resultNode
= (ITreeNode
<?
>)result
;
122 resultNode
.getChildNodes().size();
123 } catch (Exception e
) {
125 //preliminary catched and logged as it seems to be the cause
126 //for failing TaxEditor tests in TaxonNameEditorTest
128 // * addDeleteAddHomotypicSynonym,
129 // * addDeleteAddHomotypicSynonymWithAnnotations
130 // * addHeterotypicSynonym
131 // * testAddHomotypicSynonym
132 //All due to failed to lazily initialize a collection of role: eu.etaxonomy.cdm.model.taxon.TaxonNode.childNodes, could not initialize proxy - no Session
133 //We need to check if this is an issue in the test behavior or in the solution itself.
134 //We could also try to atleast add a check if the children list is attached to a session
135 //before initializing it.
137 logger
.warn("Error in PostMergeEntityListener during handleTreeNodes: " + e
.getMessage());