Project

General

Profile

Download (8.92 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

    
16
import com.vaadin.ui.Notification;
17
import com.vaadin.ui.UI;
18

    
19
import eu.etaxonomy.cdm.api.application.CdmRepository;
20
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
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
    TransactionStatus tx = null;
44

    
45
    ConversationHolder conversationHolder = null;
46

    
47
    /**
48
     *
49
     * @param repo
50
     * @param service
51
     *            may be <code>null</code>, but delete operations will fail with
52
     *            a NullPointerException in this case.
53
     */
54
    public CdmStore(CdmRepository repo, S service) {
55

    
56
        this.repo = repo;
57
        this.service = service;
58

    
59
    }
60

    
61

    
62
    public TransactionStatus startConversationalTransaction() {
63
        checkExistingTransaction();
64
        getConversationHolder().bind();
65
        tx = getConversationHolder().startTransaction();
66
        return tx;
67
    }
68

    
69

    
70
    /**
71
     * @return
72
     */
73
    protected ConversationHolder getConversationHolder() {
74
        if(conversationHolder == null){
75
            conversationHolder = (ConversationHolder) repo.getBean("conversationHolder");
76
        }
77
        return conversationHolder;
78
    }
79
    /**
80
     * @return
81
     *
82
     */
83
    private TransactionStatus startTransaction(boolean readOnly) {
84
        checkExistingTransaction();
85
        return repo.startTransaction(readOnly);
86
    }
87

    
88

    
89
    /**
90
     *
91
     */
92
    protected void checkExistingTransaction() {
93
        if (tx != null) {
94
            // @formatter:off
95
            // holding the TransactionStatus as state is not good design. we
96
            // should change the save operation
97
            // in the EditorView so that the presenter can process the save in
98
            // one method call.
99
            // Problems:
100
            // 1. the fieldGroup needs a open session and read transaction
101
            // during the validation, otherwise
102
            // LazyInitialisationExceptions occur.
103
            // 2. passing the TransactionState to the view also doesn't seem
104
            // like a good idea.
105
            // @formatter:on
106
            throw new RuntimeException("Can't process a second save operation while another one is in progress.");
107
        }
108
    }
109

    
110
    /**
111
     * If the bean is contained in the session it is being updated by doing an
112
     * evict and merge. The fieldGroup is updated with the merged bean.
113
     *
114
     *
115
     * @param bean
116
     * @return The bean merged to the session or original bean in case a merge
117
     *         was not necessary.
118
     */
119
    public T mergedBean(T bean) {
120

    
121
        Session session = getSession();
122

    
123
        // session.clear();
124
        if (session.contains(bean)) {
125
            // evict bean before merge to avoid duplicate beans in same session
126
            logger.trace(this._toString() + ".mergedBean() - evict " + bean.toString());
127
            session.evict(bean);
128
        }
129

    
130
        logger.trace(this._toString() + ".mergedBean() - doing merge of" + bean.toString());
131
        // to avoid merge problems as described in
132
        // https://dev.e-taxonomy.eu/redmine/issues/6687
133
        // we are set the hibernate property
134
        // hibernate.event.merge.entity_copy_observer=allow
135
        @SuppressWarnings("unchecked")
136
        T mergedBean = (T) session.merge(bean);
137
        logger.trace(this._toString() + ".mergedBean() - bean after merge " + bean.toString());
138
        return mergedBean;
139

    
140
    }
141

    
142
    /**
143
     * @return
144
     */
145
    public Session getSession() {
146

    
147
        Session session = repo.getSession();
148
        logger.trace(this._toString() + ".getSession() - session:" + session.hashCode() + ", persistenceContext: "
149
                + ((SessionImplementor) session).getPersistenceContext() + " - " + session.toString());
150

    
151
        return session;
152
    }
153

    
154
    protected String _toString() {
155
        return this.getClass().getSimpleName() + "@" + this.hashCode();
156
    }
157

    
158
    /**
159
     *
160
     * @param bean
161
     * @param tx2
162
     * @param presaveSession
163
     * @return the merged bean, this bean is <b>not reloaded</b> from the
164
     *         persistent storage.
165
     */
166
    public EntityChangeEvent saveBean(T bean) {
167

    
168
        Type changeEventType;
169
        if(bean.getId() > 1){
170
            changeEventType = Type.MODIFIED;
171
        } else {
172
            changeEventType = Type.CREATED;
173
        }
174
        Session session = getSession();
175
        if(tx == null){
176
            startTransaction(false);
177
        }
178
        logger.trace(this._toString() + ".onEditorSaveEvent - session: " + session.hashCode());
179
        logger.trace(this._toString() + ".onEditorSaveEvent - merging bean into session");
180
        // merge the changes into the session, ...
181
        T mergedBean = mergedBean(bean);
182
        repo.getCommonService().saveOrUpdate(mergedBean);
183
        flushCommitAndCloseConversationTransaction();
184

    
185
        return new EntityChangeEvent(mergedBean.getClass(), mergedBean.getId(), changeEventType);
186
    }
187

    
188
    /**
189
     *
190
     * @param bean
191
     * @return a EntityChangeEvent in case the deletion was successful otherwise <code>null</code>.
192
     */
193
    public final EntityChangeEvent deleteBean(T bean) {
194

    
195
        logger.trace(this._toString() + ".onEditorPreSaveEvent - starting transaction");
196

    
197
        startTransaction(false);
198
        logger.trace(this._toString() + ".deleteBean - deleting" + bean.toString());
199
        DeleteResult result = service.delete(bean);
200
        if (result.isOk()) {
201

    
202
            flushCommitAndClose();
203
            logger.trace(this._toString() + ".deleteBean - transaction comitted");
204
            return new EntityChangeEvent(bean.getClass(), bean.getId(), Type.REMOVED);
205
        } else {
206
            String notificationTitle;
207
            StringBuffer messageBody = new StringBuffer();
208
            if (result.isAbort()) {
209
                notificationTitle = "The delete operation as abborded by the system.";
210
            } else {
211
                notificationTitle = "An error occured during the delete operation.";
212
            }
213
            if (!result.getExceptions().isEmpty()) {
214
                messageBody.append("<h3>").append("Exceptions:").append("</h3>").append("<ul>");
215
                result.getExceptions().forEach(e -> messageBody.append("<li>").append(e.getMessage()).append("</li>"));
216
                messageBody.append("</ul>");
217
            }
218
            if (!result.getRelatedObjects().isEmpty()) {
219
                messageBody.append("<h3>").append("Related objects exist:").append("</h3>").append("<ul>");
220
                result.getRelatedObjects().forEach(e -> {
221
                    messageBody.append("<li>");
222
                    if (IdentifiableEntity.class.isAssignableFrom(e.getClass())) {
223
                        messageBody.append(((IdentifiableEntity) e).getTitleCache());
224
                    } else {
225
                        messageBody.append(e.toString());
226
                    }
227
                    messageBody.append("</li>");
228
                });
229

    
230
                messageBody.append("</ul>");
231
            }
232
            Notification notification = new Notification(notificationTitle, messageBody.toString(),
233
                    com.vaadin.ui.Notification.Type.ERROR_MESSAGE, true);
234
            notification.show(UI.getCurrent().getPage());
235
            tx = null;
236
        }
237
        return null;
238
    }
239

    
240
    /**
241
     * @param session
242
     */
243
    protected void flushCommitAndClose() {
244
        Session session = getSession();
245
        session.flush();
246
        logger.trace(this._toString() + "session flushed");
247
        repo.commitTransaction(tx);
248
        tx = null;
249
        if(session.isOpen()){
250
            session.close();
251
        }
252
        session = null;
253

    
254
        logger.trace(this._toString() + "transaction comitted and session closed");
255
    }
256

    
257
    protected void flushCommitAndCloseConversationTransaction() {
258
        getConversationHolder().getSession().flush();
259
        logger.trace(this._toString() + "conversational session flushed");
260
        getConversationHolder().commit();
261
        getConversationHolder().close();
262
        tx = null;
263
        logger.trace(this._toString() + "conversational transaction comitted and session closed");
264
    }
265

    
266
}
(2-2/5)