Project

General

Profile

Download (9.69 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
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
import org.springframework.transaction.support.DefaultTransactionDefinition;
16

    
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
import eu.etaxonomy.vaadin.mvp.AbstractView;
28

    
29
/**
30
 * @author a.kohlbecker
31
 * @since Jun 26, 2017
32
 *
33
 * TODO better naming of this class, ServiceWrapper, ServiceOperator, ...?
34
 *
35
 */
36
public class CdmStore<T extends CdmBase, S extends IService<T>> {
37

    
38
    private static final Logger logger = Logger.getLogger(CdmStore.class);
39

    
40
    private CdmRepository repo;
41

    
42
    private S service;
43

    
44
    TransactionStatus txStatus = null;
45

    
46
//    ConversationHolder conversationHolder = null;
47
//
48
//    /**
49
//     * @return the conversationHolder
50
//     */
51
//    public ConversationHolder getConversationHolder() {
52
//        return conversationHolder;
53
//    }
54

    
55
    protected DefaultTransactionDefinition txDefinition = null;
56

    
57
    /**
58
     *
59
     * @param repo
60
     * @param service
61
     *            may be <code>null</code>, but delete operations will fail with
62
     *            a NullPointerException in this case.
63
     */
64
    public CdmStore(CdmRepository repo, S service) {
65

    
66
        this.repo = repo;
67
        this.service = service;
68

    
69
    }
70

    
71
//    /**
72
//     * constructor which takes a ConversationHolder. The supplying class of the conversationHolder needs
73
//     * to care for <code>bind()</code>, <code>unbind()</code> and <code>close()</code> since the store is
74
//     * only responsible for starting and committing of transactions.
75
//     *
76
//     * @param repo
77
//     * @param service
78
//     * @param conversationHolder
79
//     */
80
//    public CdmStore(CdmRepository repo, S service, ConversationHolder conversationHolder) {
81
//
82
//        this.repo = repo;
83
//        this.service = service;
84
//        this.conversationHolder = conversationHolder;
85
//
86
//    }
87

    
88
    /**
89
     * @return
90
     *
91
     */
92
    public TransactionStatus startTransaction() {
93
//        if(conversationHolder != null && !conversationHolder.isTransactionActive()){
94
//            //conversationHolder.setDefinition(getTransactionDefinition());
95
//            return conversationHolder.startTransaction();
96
//        } else {
97
            checkExistingTransaction();
98
            txStatus = repo.startTransaction();
99
            return txStatus;
100
//        }
101
    }
102

    
103
    /**
104
     *
105
     */
106
    protected void checkExistingTransaction() {
107
        if (txStatus != null) {
108
            // @formatter:off
109
            // holding the TransactionStatus as state is not good design. we
110
            // should change the save operation
111
            // in the EditorView so that the presenter can process the save in
112
            // one method call.
113
            // Problems:
114
            // 1. the fieldGroup needs a open session and read transaction
115
            // during the validation, otherwise
116
            // LazyInitialisationExceptions occur.
117
            // 2. passing the TransactionState to the view also doesn't seem
118
            // like a good idea.
119
            // @formatter:on
120
            throw new RuntimeException("Opening a second transaction in the same" + this.getClass().getSimpleName() + " is not supported");
121
        }
122
    }
123

    
124
    /**
125
     * If the bean is contained in the session it is being updated by doing an
126
     * evict and merge. The fieldGroup is updated with the merged bean.
127
     *
128
     *
129
     * @param bean
130
     * @return The bean merged to the session or original bean in case a merge
131
     *         was not necessary.
132
     */
133
    public T mergedBean(T bean) {
134

    
135
        Session session = getSession();
136

    
137
        // session.clear();
138
        if (session.contains(bean)) {
139
            // evict bean before merge to avoid duplicate beans in same session
140
            logger.trace(this._toString() + ".mergedBean() - evict " + bean.toString());
141
            session.evict(bean);
142
        }
143

    
144
        logger.trace(this._toString() + ".mergedBean() - doing merge of" + bean.toString());
145
        // to avoid merge problems as described in
146
        // https://dev.e-taxonomy.eu/redmine/issues/6687
147
        // we are set the hibernate property
148
        // hibernate.event.merge.entity_copy_observer=allow
149
        @SuppressWarnings("unchecked")
150
        T mergedBean = (T) session.merge(bean);
151
        logger.trace(this._toString() + ".mergedBean() - bean after merge " + bean.toString());
152
        return mergedBean;
153

    
154
    }
155

    
156
    /**
157
     * @return
158
     */
159
    private Session getSession() {
160

    
161
        Session session;
162
//        if(conversationHolder != null){
163
//            session = conversationHolder.getSession();
164
//        } else {
165
            session = repo.getSession();
166
//        }
167
        logger.trace(this._toString() + ".getSession() - session:" + session.hashCode() + ", persistenceContext: "
168
                + ((SessionImplementor) session).getPersistenceContext() + " - " + session.toString());
169

    
170
        return session;
171
    }
172

    
173
    protected String _toString() {
174
        return this.getClass().getSimpleName() + "@" + this.hashCode();
175
    }
176

    
177
    /**
178
     *
179
     * @param bean
180
     *
181
     * @return the merged bean, this bean is <b>not reloaded</b> from the
182
     *         persistent storage.
183
     */
184
    public EntityChangeEvent saveBean(T bean, AbstractView view) {
185

    
186
        Type changeEventType;
187
        if(bean.getId() > 1){
188
            changeEventType = Type.MODIFIED;
189
        } else {
190
            changeEventType = Type.CREATED;
191
        }
192

    
193
        Session session = getSession();
194
        logger.trace(this._toString() + ".onEditorSaveEvent - session: " + session.hashCode());
195

    
196
        if(txStatus == null
197
//                || (conversationHolder != null && !conversationHolder.isTransactionActive())
198
                ){
199
            // no running transaction, start one ...
200
            startTransaction();
201
        }
202

    
203
        logger.trace(this._toString() + ".onEditorSaveEvent - merging bean into session");
204
        // merge the changes into the session, ...
205

    
206
        T mergedBean = mergedBean(bean);
207

    
208
        // NOTE: saveOrUpdate is really needed here even if we to a merge before
209
        // repo.getCommonService().saveOrUpdate(mergedBean);
210
        session.flush();
211
        commitTransction();
212

    
213
        return new EntityChangeEvent(mergedBean, changeEventType, view);
214
    }
215

    
216
    /**
217
     *
218
     * @param bean
219
     * @return a EntityChangeEvent in case the deletion was successful otherwise <code>null</code>.
220
     */
221
    public final EntityChangeEvent deleteBean(T bean, AbstractView view) {
222

    
223
        logger.trace(this._toString() + ".onEditorPreSaveEvent - starting transaction");
224

    
225
        startTransaction();
226
        logger.trace(this._toString() + ".deleteBean - deleting" + bean.toString());
227
        DeleteResult result = service.delete(bean);
228
        if (result.isOk()) {
229
            getSession().flush();
230
            commitTransction();
231
            logger.trace(this._toString() + ".deleteBean - transaction comitted");
232
            return new EntityChangeEvent(bean, Type.REMOVED, view);
233
        } else {
234
            handleDeleteresultInError(result);
235
            txStatus = null;
236
        }
237
        return null;
238
    }
239

    
240
    /**
241
     * @param result
242
     */
243
    public static void handleDeleteresultInError(DeleteResult result) {
244
        String notificationTitle;
245
        StringBuffer messageBody = new StringBuffer();
246
        if (result.isAbort()) {
247
            notificationTitle = "The delete operation as abborded by the system.";
248
        } else {
249
            notificationTitle = "An error occured during the delete operation.";
250
        }
251
        if (!result.getExceptions().isEmpty()) {
252
            messageBody.append("<h3>").append("Exceptions:").append("</h3>").append("<ul>");
253
            result.getExceptions().forEach(e -> messageBody.append("<li>").append(e.getMessage()).append("</li>"));
254
            messageBody.append("</ul>");
255
        }
256
        if (!result.getRelatedObjects().isEmpty()) {
257
            messageBody.append("<h3>").append("Related objects exist:").append("</h3>").append("<ul>");
258
            result.getRelatedObjects().forEach(e -> {
259
                messageBody.append("<li>");
260
                if (IdentifiableEntity.class.isAssignableFrom(e.getClass())) {
261
                    messageBody.append(((IdentifiableEntity) e).getTitleCache());
262
                } else {
263
                    messageBody.append(e.toString());
264
                }
265
                messageBody.append("</li>");
266
            });
267

    
268
            messageBody.append("</ul>");
269
        }
270
        Notification notification = new Notification(notificationTitle, messageBody.toString(),
271
                com.vaadin.ui.Notification.Type.ERROR_MESSAGE, true);
272
        notification.show(UI.getCurrent().getPage());
273
    }
274

    
275

    
276
    protected void commitTransction() {
277

    
278
//        if(conversationHolder != null){
279
//            conversationHolder.commit();
280
//        } else {
281
            repo.commitTransaction(txStatus);
282
            txStatus = null;
283
//        }
284
    }
285

    
286
    /**
287
     * @param entityId
288
     */
289
    public T loadBean(int entityId) {
290
//        conversationHolder.startTransaction();
291
        return service.find(entityId);
292
    }
293

    
294
    public S getService() {
295
        return service;
296
    }
297

    
298

    
299
}
(3-3/9)