Project

General

Profile

Download (9.6 KB) Statistics
| Branch: | Tag: | Revision:
1 262c713c Andreas Kohlbecker
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
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.
8
*/
9
package eu.etaxonomy.cdm.service;
10
11
import org.apache.log4j.Logger;
12
import org.hibernate.Session;
13
import org.hibernate.engine.spi.SessionImplementor;
14
import org.springframework.transaction.TransactionStatus;
15 fe785c1e Andreas Kohlbecker
import org.springframework.transaction.support.DefaultTransactionDefinition;
16 262c713c Andreas Kohlbecker
17
import com.vaadin.ui.Notification;
18
import com.vaadin.ui.UI;
19
20
import eu.etaxonomy.cdm.api.application.CdmRepository;
21
import eu.etaxonomy.cdm.api.service.DeleteResult;
22
import eu.etaxonomy.cdm.api.service.IService;
23
import eu.etaxonomy.cdm.model.common.CdmBase;
24
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
25
import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
26
import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent.Type;
27
28
/**
29
 * @author a.kohlbecker
30
 * @since Jun 26, 2017
31
 *
32
 * TODO better naming of this class, ServiceWrapper, ServiceOperator, ...?
33
 *
34
 */
35
public class CdmStore<T extends CdmBase, S extends IService<T>> {
36
37
    private static final Logger logger = Logger.getLogger(CdmStore.class);
38
39
    private CdmRepository repo;
40
41
    private S service;
42
43 2f02081c Andreas Kohlbecker
    TransactionStatus txStatus = null;
44 262c713c Andreas Kohlbecker
45 2f02081c Andreas Kohlbecker
//    ConversationHolder conversationHolder = null;
46
//
47
//    /**
48
//     * @return the conversationHolder
49
//     */
50
//    public ConversationHolder getConversationHolder() {
51
//        return conversationHolder;
52
//    }
53 fe785c1e Andreas Kohlbecker
54
    protected DefaultTransactionDefinition txDefinition = null;
55
56 262c713c Andreas Kohlbecker
    /**
57
     *
58
     * @param repo
59
     * @param service
60
     *            may be <code>null</code>, but delete operations will fail with
61
     *            a NullPointerException in this case.
62
     */
63
    public CdmStore(CdmRepository repo, S service) {
64
65
        this.repo = repo;
66
        this.service = service;
67 3436c944 Andreas Kohlbecker
68
    }
69
70 2f02081c Andreas Kohlbecker
//    /**
71
//     * constructor which takes a ConversationHolder. The supplying class of the conversationHolder needs
72
//     * to care for <code>bind()</code>, <code>unbind()</code> and <code>close()</code> since the store is
73
//     * only responsible for starting and committing of transactions.
74
//     *
75
//     * @param repo
76
//     * @param service
77
//     * @param conversationHolder
78
//     */
79
//    public CdmStore(CdmRepository repo, S service, ConversationHolder conversationHolder) {
80
//
81
//        this.repo = repo;
82
//        this.service = service;
83
//        this.conversationHolder = conversationHolder;
84
//
85
//    }
86 fe785c1e Andreas Kohlbecker
87 262c713c Andreas Kohlbecker
    /**
88
     * @return
89
     *
90
     */
91 fe785c1e Andreas Kohlbecker
    public TransactionStatus startTransaction() {
92 2f02081c Andreas Kohlbecker
//        if(conversationHolder != null && !conversationHolder.isTransactionActive()){
93
//            //conversationHolder.setDefinition(getTransactionDefinition());
94
//            return conversationHolder.startTransaction();
95
//        } else {
96 fe785c1e Andreas Kohlbecker
            checkExistingTransaction();
97 2f02081c Andreas Kohlbecker
            txStatus = repo.startTransaction();
98
            return txStatus;
99
//        }
100 3436c944 Andreas Kohlbecker
    }
101
102
    /**
103
     *
104
     */
105
    protected void checkExistingTransaction() {
106 2f02081c Andreas Kohlbecker
        if (txStatus != null) {
107 262c713c Andreas Kohlbecker
            // @formatter:off
108
            // holding the TransactionStatus as state is not good design. we
109
            // should change the save operation
110
            // in the EditorView so that the presenter can process the save in
111
            // one method call.
112
            // Problems:
113
            // 1. the fieldGroup needs a open session and read transaction
114
            // during the validation, otherwise
115
            // LazyInitialisationExceptions occur.
116
            // 2. passing the TransactionState to the view also doesn't seem
117
            // like a good idea.
118
            // @formatter:on
119 fe785c1e Andreas Kohlbecker
            throw new RuntimeException("Opening a second transaction in the same" + this.getClass().getSimpleName() + " is not supported");
120 262c713c Andreas Kohlbecker
        }
121
    }
122
123
    /**
124
     * If the bean is contained in the session it is being updated by doing an
125
     * evict and merge. The fieldGroup is updated with the merged bean.
126
     *
127
     *
128
     * @param bean
129
     * @return The bean merged to the session or original bean in case a merge
130
     *         was not necessary.
131
     */
132
    public T mergedBean(T bean) {
133 3436c944 Andreas Kohlbecker
134 262c713c Andreas Kohlbecker
        Session session = getSession();
135
136
        // session.clear();
137
        if (session.contains(bean)) {
138
            // evict bean before merge to avoid duplicate beans in same session
139
            logger.trace(this._toString() + ".mergedBean() - evict " + bean.toString());
140
            session.evict(bean);
141
        }
142
143
        logger.trace(this._toString() + ".mergedBean() - doing merge of" + bean.toString());
144
        // to avoid merge problems as described in
145
        // https://dev.e-taxonomy.eu/redmine/issues/6687
146
        // we are set the hibernate property
147
        // hibernate.event.merge.entity_copy_observer=allow
148
        @SuppressWarnings("unchecked")
149
        T mergedBean = (T) session.merge(bean);
150
        logger.trace(this._toString() + ".mergedBean() - bean after merge " + bean.toString());
151
        return mergedBean;
152
153
    }
154
155
    /**
156
     * @return
157
     */
158 1af6dc9f Andreas Kohlbecker
    private Session getSession() {
159 3436c944 Andreas Kohlbecker
160 fe785c1e Andreas Kohlbecker
        Session session;
161 2f02081c Andreas Kohlbecker
//        if(conversationHolder != null){
162
//            session = conversationHolder.getSession();
163
//        } else {
164 fe785c1e Andreas Kohlbecker
            session = repo.getSession();
165 2f02081c Andreas Kohlbecker
//        }
166 262c713c Andreas Kohlbecker
        logger.trace(this._toString() + ".getSession() - session:" + session.hashCode() + ", persistenceContext: "
167
                + ((SessionImplementor) session).getPersistenceContext() + " - " + session.toString());
168 3436c944 Andreas Kohlbecker
169 262c713c Andreas Kohlbecker
        return session;
170
    }
171
172
    protected String _toString() {
173
        return this.getClass().getSimpleName() + "@" + this.hashCode();
174
    }
175
176
    /**
177
     *
178
     * @param bean
179 02ec8d6b Andreas Kohlbecker
     * 
180 262c713c Andreas Kohlbecker
     * @return the merged bean, this bean is <b>not reloaded</b> from the
181
     *         persistent storage.
182
     */
183
    public EntityChangeEvent saveBean(T bean) {
184
185
        Type changeEventType;
186
        if(bean.getId() > 1){
187
            changeEventType = Type.MODIFIED;
188
        } else {
189
            changeEventType = Type.CREATED;
190
        }
191 fe785c1e Andreas Kohlbecker
192 3436c944 Andreas Kohlbecker
        Session session = getSession();
193
        logger.trace(this._toString() + ".onEditorSaveEvent - session: " + session.hashCode());
194 fe785c1e Andreas Kohlbecker
195 2f02081c Andreas Kohlbecker
        if(txStatus == null
196
//                || (conversationHolder != null && !conversationHolder.isTransactionActive())
197
                ){
198 fe785c1e Andreas Kohlbecker
            // no running transaction, start one ...
199
            startTransaction();
200
        }
201
202 262c713c Andreas Kohlbecker
        logger.trace(this._toString() + ".onEditorSaveEvent - merging bean into session");
203
        // merge the changes into the session, ...
204 60be1b8d Andreas Kohlbecker
205 0e072f9c Andreas Kohlbecker
        T mergedBean = mergedBean(bean);
206 02ec8d6b Andreas Kohlbecker
207 60be1b8d Andreas Kohlbecker
        // NOTE: saveOrUpdate is really needed here even if we to a merge before
208 262c713c Andreas Kohlbecker
        repo.getCommonService().saveOrUpdate(mergedBean);
209 fe785c1e Andreas Kohlbecker
        session.flush();
210
        commitTransction();
211 3436c944 Andreas Kohlbecker
212 262c713c Andreas Kohlbecker
        return new EntityChangeEvent(mergedBean.getClass(), mergedBean.getId(), changeEventType);
213
    }
214
215
    /**
216
     *
217
     * @param bean
218
     * @return a EntityChangeEvent in case the deletion was successful otherwise <code>null</code>.
219
     */
220
    public final EntityChangeEvent deleteBean(T bean) {
221 3436c944 Andreas Kohlbecker
222 262c713c Andreas Kohlbecker
        logger.trace(this._toString() + ".onEditorPreSaveEvent - starting transaction");
223 3436c944 Andreas Kohlbecker
224 fe785c1e Andreas Kohlbecker
        startTransaction();
225 262c713c Andreas Kohlbecker
        logger.trace(this._toString() + ".deleteBean - deleting" + bean.toString());
226
        DeleteResult result = service.delete(bean);
227
        if (result.isOk()) {
228 3436c944 Andreas Kohlbecker
229 fe785c1e Andreas Kohlbecker
            getSession().flush();
230
            commitTransction();
231 262c713c Andreas Kohlbecker
            logger.trace(this._toString() + ".deleteBean - transaction comitted");
232
            return new EntityChangeEvent(bean.getClass(), bean.getId(), Type.REMOVED);
233
        } else {
234
            String notificationTitle;
235
            StringBuffer messageBody = new StringBuffer();
236
            if (result.isAbort()) {
237
                notificationTitle = "The delete operation as abborded by the system.";
238
            } else {
239
                notificationTitle = "An error occured during the delete operation.";
240
            }
241
            if (!result.getExceptions().isEmpty()) {
242
                messageBody.append("<h3>").append("Exceptions:").append("</h3>").append("<ul>");
243
                result.getExceptions().forEach(e -> messageBody.append("<li>").append(e.getMessage()).append("</li>"));
244
                messageBody.append("</ul>");
245
            }
246
            if (!result.getRelatedObjects().isEmpty()) {
247
                messageBody.append("<h3>").append("Related objects exist:").append("</h3>").append("<ul>");
248
                result.getRelatedObjects().forEach(e -> {
249
                    messageBody.append("<li>");
250
                    if (IdentifiableEntity.class.isAssignableFrom(e.getClass())) {
251
                        messageBody.append(((IdentifiableEntity) e).getTitleCache());
252
                    } else {
253
                        messageBody.append(e.toString());
254
                    }
255
                    messageBody.append("</li>");
256
                });
257
258
                messageBody.append("</ul>");
259
            }
260
            Notification notification = new Notification(notificationTitle, messageBody.toString(),
261
                    com.vaadin.ui.Notification.Type.ERROR_MESSAGE, true);
262
            notification.show(UI.getCurrent().getPage());
263 2f02081c Andreas Kohlbecker
            txStatus = null;
264 262c713c Andreas Kohlbecker
        }
265
        return null;
266
    }
267
268 fe785c1e Andreas Kohlbecker
269
    protected void commitTransction() {
270
271 2f02081c Andreas Kohlbecker
//        if(conversationHolder != null){
272
//            conversationHolder.commit();
273
//        } else {
274
            repo.commitTransaction(txStatus);
275
            txStatus = null;
276
//        }
277 fe785c1e Andreas Kohlbecker
    }
278 3436c944 Andreas Kohlbecker
279 fe785c1e Andreas Kohlbecker
    /**
280
     * @param entityId
281
     */
282
    public T loadBean(int entityId) {
283 2f02081c Andreas Kohlbecker
//        conversationHolder.startTransaction();
284 fe785c1e Andreas Kohlbecker
        return service.find(entityId);
285 3436c944 Andreas Kohlbecker
    }
286
287 fe785c1e Andreas Kohlbecker
    public S getService() {
288
        return service;
289 3436c944 Andreas Kohlbecker
    }
290
291 fe785c1e Andreas Kohlbecker
292 262c713c Andreas Kohlbecker
}