2 * Copyright (C) 2017 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
.service
;
11 import org
.apache
.log4j
.Logger
;
12 import org
.hibernate
.Session
;
13 import org
.hibernate
.engine
.spi
.SessionImplementor
;
14 import org
.springframework
.transaction
.TransactionStatus
;
16 import com
.vaadin
.ui
.Notification
;
17 import com
.vaadin
.ui
.UI
;
19 import eu
.etaxonomy
.cdm
.api
.application
.CdmRepository
;
20 import eu
.etaxonomy
.cdm
.api
.service
.DeleteResult
;
21 import eu
.etaxonomy
.cdm
.api
.service
.IService
;
22 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
23 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
24 import eu
.etaxonomy
.cdm
.vaadin
.event
.EntityChangeEvent
;
25 import eu
.etaxonomy
.cdm
.vaadin
.event
.EntityChangeEvent
.Type
;
28 * @author a.kohlbecker
31 * TODO better naming of this class, ServiceWrapper, ServiceOperator, ...?
34 public class CdmStore
<T
extends CdmBase
, S
extends IService
<T
>> {
36 private static final Logger logger
= Logger
.getLogger(CdmStore
.class);
38 private CdmRepository repo
;
42 TransactionStatus tx
= null;
44 Session session
= null;
50 * may be <code>null</code>, but delete operations will fail with
51 * a NullPointerException in this case.
53 public CdmStore(CdmRepository repo
, S service
) {
56 this.service
= service
;
63 public TransactionStatus
startTransaction() {
66 // holding the TransactionStatus as state is not good design. we
67 // should change the save operation
68 // in the EditorView so that the presenter can process the save in
71 // 1. the fieldGroup needs a open session and read transaction
72 // during the validation, otherwise
73 // LazyInitialisationExceptions occur.
74 // 2. passing the TransactionState to the view also doesn't seem
77 throw new RuntimeException("Can't process a second save operation while another one is in progress.");
79 return repo
.startTransaction(true);
83 * If the bean is contained in the session it is being updated by doing an
84 * evict and merge. The fieldGroup is updated with the merged bean.
88 * @return The bean merged to the session or original bean in case a merge
91 public T
mergedBean(T bean
) {
92 Session session
= getSession();
95 if (session
.contains(bean
)) {
96 // evict bean before merge to avoid duplicate beans in same session
97 logger
.trace(this._toString() + ".mergedBean() - evict " + bean
.toString());
101 logger
.trace(this._toString() + ".mergedBean() - doing merge of" + bean
.toString());
102 // to avoid merge problems as described in
103 // https://dev.e-taxonomy.eu/redmine/issues/6687
104 // we are set the hibernate property
105 // hibernate.event.merge.entity_copy_observer=allow
106 @SuppressWarnings("unchecked")
107 T mergedBean
= (T
) session
.merge(bean
);
108 logger
.trace(this._toString() + ".mergedBean() - bean after merge " + bean
.toString());
116 protected Session
getSession() {
117 Session session
= repo
.getSession();
118 logger
.trace(this._toString() + ".getSession() - session:" + session
.hashCode() + ", persistenceContext: "
119 + ((SessionImplementor
) session
).getPersistenceContext() + " - " + session
.toString());
123 protected String
_toString() {
124 return this.getClass().getSimpleName() + "@" + this.hashCode();
130 * @return the merged bean, this bean is <b>not reloaded</b> from the
131 * persistent storage.
133 public EntityChangeEvent
saveBean(T bean
) {
135 Type changeEventType
;
136 if(bean
.getId() > 1){
137 changeEventType
= Type
.MODIFIED
;
139 changeEventType
= Type
.CREATED
;
142 // Session session = getSession();
143 logger
.trace(this._toString() + ".onEditorSaveEvent - session: " + session
);
144 logger
.trace(this._toString() + ".onEditorSaveEvent - merging bean into session");
145 // merge the changes into the session, ...
146 T mergedBean
= mergedBean(bean
);
147 repo
.getCommonService().saveOrUpdate(mergedBean
);
149 logger
.trace(this._toString() + ".onEditorSaveEvent - session flushed");
150 repo
.commitTransaction(tx
);
152 if (session
.isOpen()) {
155 logger
.trace(this._toString() + ".onEditorSaveEvent - transaction comitted");
156 return new EntityChangeEvent(mergedBean
.getClass(), mergedBean
.getId(), changeEventType
);
162 * @return a EntityChangeEvent in case the deletion was successful otherwise <code>null</code>.
164 public final EntityChangeEvent
deleteBean(T bean
) {
165 logger
.trace(this._toString() + ".onEditorPreSaveEvent - starting transaction");
166 tx
= startTransaction();
167 logger
.trace(this._toString() + ".deleteBean - deleting" + bean
.toString());
168 DeleteResult result
= service
.delete(bean
);
170 Session session
= getSession();
172 logger
.trace(this._toString() + ".deleteBean - session flushed");
173 repo
.commitTransaction(tx
);
176 logger
.trace(this._toString() + ".deleteBean - transaction comitted");
177 return new EntityChangeEvent(bean
.getClass(), bean
.getId(), Type
.REMOVED
);
179 String notificationTitle
;
180 StringBuffer messageBody
= new StringBuffer();
181 if (result
.isAbort()) {
182 notificationTitle
= "The delete operation as abborded by the system.";
184 notificationTitle
= "An error occured during the delete operation.";
186 if (!result
.getExceptions().isEmpty()) {
187 messageBody
.append("<h3>").append("Exceptions:").append("</h3>").append("<ul>");
188 result
.getExceptions().forEach(e
-> messageBody
.append("<li>").append(e
.getMessage()).append("</li>"));
189 messageBody
.append("</ul>");
191 if (!result
.getRelatedObjects().isEmpty()) {
192 messageBody
.append("<h3>").append("Related objects exist:").append("</h3>").append("<ul>");
193 result
.getRelatedObjects().forEach(e
-> {
194 messageBody
.append("<li>");
195 if (IdentifiableEntity
.class.isAssignableFrom(e
.getClass())) {
196 messageBody
.append(((IdentifiableEntity
) e
).getTitleCache());
198 messageBody
.append(e
.toString());
200 messageBody
.append("</li>");
203 messageBody
.append("</ul>");
205 Notification notification
= new Notification(notificationTitle
, messageBody
.toString(),
206 com
.vaadin
.ui
.Notification
.Type
.ERROR_MESSAGE
, true);
207 notification
.show(UI
.getCurrent().getPage());