Project

General

Profile

Download (10.1 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.vaadin.mvp;
10

    
11
import java.util.Collection;
12
import java.util.EnumSet;
13
import java.util.HashSet;
14
import java.util.UUID;
15

    
16
import org.apache.log4j.Logger;
17
import org.hibernate.HibernateException;
18
import org.springframework.beans.factory.annotation.Autowired;
19

    
20
import eu.etaxonomy.cdm.api.service.IService;
21
import eu.etaxonomy.cdm.api.utility.UserHelper;
22
import eu.etaxonomy.cdm.cache.CdmTransientEntityCacher;
23
import eu.etaxonomy.cdm.debug.PersistentContextAnalyzer;
24
import eu.etaxonomy.cdm.model.ICdmCacher;
25
import eu.etaxonomy.cdm.model.common.CdmBase;
26
import eu.etaxonomy.cdm.model.common.User;
27
import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
28
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
29
import eu.etaxonomy.cdm.service.CdmFilterablePagingProviderFactory;
30
import eu.etaxonomy.cdm.service.CdmStore;
31
import eu.etaxonomy.cdm.service.UserHelperAccess;
32
import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
33
import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
34
import eu.etaxonomy.vaadin.mvp.event.EditorPreSaveEvent;
35
import eu.etaxonomy.vaadin.mvp.event.EditorSaveEvent;
36

    
37
/**
38
 * Provides generic save operations of modified cdm entities.
39
 *
40
 * @author a.kohlbecker
41
 * @since Apr 5, 2017
42
 *
43
 */
44
public abstract class CdmEditorPresenterBase<DTO, CDM extends CdmBase, V extends ApplicationView<?>> extends AbstractEditorPresenter<DTO, V>
45
    implements CachingPresenter {
46

    
47
    private static final long serialVersionUID = 2218185546277084261L;
48

    
49
    private static final Logger logger = Logger.getLogger(CdmEditorPresenterBase.class);
50

    
51
    /**
52
     * if not null, this CRUD set is to be used to create a CdmAuthoritiy for the base entity which will be
53
     * granted to the current use as long this grant is not assigned yet.
54
     */
55
    protected EnumSet<CRUD> crud = null;
56

    
57

    
58
    private ICdmCacher cache;
59

    
60
    private java.util.Collection<CdmBase> rootEntities = new HashSet<>();
61

    
62
    public CdmEditorPresenterBase() {
63
        super();
64
        logger.trace(this._toString() + " constructor");
65
    }
66

    
67
    CdmStore<CDM, IService<CDM>> store ;
68

    
69
    protected CdmAuthority newAuthorityCreated;
70

    
71
    @Autowired
72
    protected CdmFilterablePagingProviderFactory pagingProviderFactory;
73

    
74
    protected CdmStore<CDM, IService<CDM>> getStore() {
75
        if(store == null){
76
            store = new CdmStore<>(getRepo(), getService());
77
        }
78
        return store;
79
    }
80

    
81
    @Override
82
    protected DTO loadBeanById(Object identifier) {
83

    
84
        CDM cdmEntitiy;
85
        if(identifier != null) {
86
            UUID uuidIdentifier = (UUID)identifier;
87
            // CdmAuthority is needed before the bean is loaded into the session.
88
            // otherwise adding the authority to the user would cause a flush
89
            cdmEntitiy = loadCdmEntity(uuidIdentifier);
90
            guaranteePerEntityCRUDPermissions(cdmEntitiy);
91
        } else {
92
            cdmEntitiy = loadCdmEntity(null);
93
            if(cdmEntitiy != null){
94
                guaranteePerEntityCRUDPermissions(cdmEntitiy);
95
            }
96
        }
97
        cache = new CdmTransientEntityCacher(this);
98
        // need to use load but put see #7214
99
        cdmEntitiy = cache.load(cdmEntitiy);
100
        addRootEntity(cdmEntitiy);
101

    
102
        DTO dto = createDTODecorator(cdmEntitiy);
103

    
104
        return dto;
105
    }
106

    
107
    /**
108
     * @param cdmEntitiy
109
     * @return
110
     */
111
    protected abstract DTO createDTODecorator(CDM cdmEntitiy);
112

    
113
    /**
114
     * @param cdmEntitiy
115
     */
116
    @Override
117
    protected void adaptToUserPermission(DTO dto) {
118

    
119
        CDM cdmEntitiy = cdmEntity(dto);
120

    
121
        UserHelper userHelper = UserHelperAccess.userHelper();
122
        boolean canDelte = userHelper.userHasPermission(cdmEntitiy, CRUD.DELETE);
123
        boolean canEdit = userHelper.userHasPermission(cdmEntitiy, CRUD.UPDATE);
124

    
125
        User user = userHelper.user();
126

    
127
        if(AbstractCdmPopupEditor.class.isAssignableFrom(getView().getClass())){
128
            AbstractCdmPopupEditor popupView = ((AbstractCdmPopupEditor)getView());
129

    
130
            if(!canEdit){
131
                popupView.setReadOnly(true); // never reset true to false here!
132
                logger.debug("setting editor to readonly");
133
            }
134
            if(!canDelte){
135
                popupView.withDeleteButton(false);
136
                logger.debug("removing delete button");
137
            }
138
        }
139

    
140
    }
141

    
142
    /**
143
     * @param dto
144
     * @return
145
     */
146
    protected abstract CDM cdmEntity(DTO dto);
147

    
148
    /**
149
     * @param identifier
150
     * @return
151
     */
152
    protected abstract CDM loadCdmEntity(UUID uuid);
153

    
154
    /**
155
     * Grant per entity CdmAuthority to the current user <b>for the bean which is not yet loaded</b>
156
     * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
157
     */
158
    protected abstract void guaranteePerEntityCRUDPermissions(UUID identifier);
159

    
160
    /**
161
     * Grant per entity CdmAuthority to the current user for the bean which is loaded
162
     * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
163
     */
164
     protected abstract void guaranteePerEntityCRUDPermissions(CDM bean);
165

    
166
    /**
167
     * @return
168
     */
169
    protected abstract IService<CDM> getService();
170

    
171
    @SuppressWarnings("unchecked")
172
    @Override
173
    // @EventBusListenerMethod // already annotated at super class
174
    public void onEditorPreSaveEvent(EditorPreSaveEvent preSaveEvent){
175

    
176
        if(!isFromOwnView(preSaveEvent)){
177
            return;
178
        }
179
        super.onEditorPreSaveEvent(preSaveEvent);
180
    }
181

    
182
    @Override
183
    // @EventBusListenerMethod // already annotated at super class
184
    public void onEditorSaveEvent(EditorSaveEvent<DTO> saveEvent){
185

    
186
        if(!isFromOwnView(saveEvent)){
187
            return;
188
        }
189

    
190
        // the bean is now updated with the changes made by the user
191
        DTO dto = saveEvent.getBean();
192
        CDM cdmEntity = cdmEntity(dto);
193

    
194
        if(logger.isTraceEnabled()){
195
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
196
            pca.printEntityGraph(System.err);
197
            pca.printCopyEntities(System.err);
198
        }
199

    
200
        if(logger.isTraceEnabled()){
201
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
202
            pca.printEntityGraph(System.err);
203
            pca.printCopyEntities(System.err);
204
        }
205
        EntityChangeEvent<?> changeEvent = null;
206
        try {
207
            dto = preSaveBean(dto);
208
            changeEvent = getStore().saveBean(cdmEntity, (AbstractView<?>) getView());
209

    
210
            if(changeEvent != null){
211
                viewEventBus.publish(this, changeEvent);
212
            }
213
        } catch (HibernateException e){
214
            if(newAuthorityCreated != null){
215
                UserHelperAccess.userHelper().removeAuthorityForCurrentUser(newAuthorityCreated);
216
            }
217
            throw e;
218
        } finally {
219
            postSaveBean(changeEvent);
220
        }
221
    }
222

    
223
    /**
224
     * This method is intended to be used for the following purposes:
225
     * <ol>
226
     *   <li>
227
     *   EditorPresenters for beans with transient properties can overwrite this method to
228
     *   update the beanItem with the changes made to the transient properties.
229
     *   This can be necessary because Vaadin MethodProperties are readonly when no setter is
230
     *   available. This can be the case with transient properties. Automatic updating
231
     *   of the property during the fieldGroup commit does not work in this case.
232
     *   Presenters, however, should <b>operate on DTOs instead, which can implement the missing setter</b>.</li>
233
     *
234
     *   <li>When modifying a bi-directional relation between two instances the user would
235
     *   need to have GrantedAuthorities for both sides of the relationship. This, however is not
236
     *   always possible. As a temporary solution the user can be granted the missing authority just
237
     *   for the time of saving the new relationship. You may also want to implement
238
     *   {@link #postSaveBean(EntityChangeEvent)} in this case.
239
     *   See {@link https://dev.e-taxonomy.eu/redmine/issues/7390 #7390}
240
     *   </li>
241
     * </ol>
242
     *
243
     */
244
    protected DTO preSaveBean(DTO bean) {
245
        // blank implementation, to be implemented by sub classes if needed
246
        return bean;
247
    }
248

    
249
    @Override
250
    protected
251
    final void saveBean(DTO bean){
252
        // blank implementation, since this is not needed in this or any sub class
253
        // see onEditorSaveEvent() instead
254
    }
255

    
256
    /**
257
     * Called after saving the DTO to the persistent storage.
258
     * This method is called in any case even if the save operation failed.
259
     * See {@link  #postSaveBean(EntityChangeEvent)}.
260
     *
261
     * @param changeEvent may be null in case of errors during the save operation
262
     */
263
    protected void postSaveBean(EntityChangeEvent changeEvent) {
264
        // blank implementation, to be implemented by sub classes if needed
265
    }
266

    
267
    @Override
268
    protected void deleteBean(DTO bean){
269
        CDM cdmEntity = cdmEntity(bean);
270
        EntityChangeEvent changeEvent = getStore().deleteBean(cdmEntity, (AbstractView) getView());
271
        if(changeEvent != null){
272
            viewEventBus.publish(this, changeEvent);
273
        }
274

    
275
    }
276

    
277
    /**
278
     * @param crud
279
     */
280
    public void setGrantsForCurrentUser(EnumSet<CRUD> crud) {
281
        this.crud = crud;
282

    
283
    }
284

    
285
    /**
286
     * {@inheritDoc}
287
     */
288
    @Override
289
    public ICdmCacher getCache() {
290
        return cache;
291
    }
292

    
293
    /**
294
     * {@inheritDoc}
295
     */
296
    @Override
297
    public void addRootEntity(CdmBase entity) {
298
        rootEntities.add(entity);
299
    }
300

    
301

    
302
    /**
303
     * {@inheritDoc}
304
     */
305
    @Override
306
    public Collection<CdmBase> getRootEntities() {
307
        return rootEntities;
308
    }
309

    
310

    
311
    /**
312
     * {@inheritDoc}
313
     */
314
    @Override
315
    public void destroy() throws Exception {
316
        super.destroy();
317
        disposeCache();
318
    }
319

    
320
    /**
321
     * {@inheritDoc}
322
     */
323
    @Override
324
    public void disposeCache() {
325
        cache.dispose();
326
    }
327
}
(13-13/13)