Project

General

Profile

Download (10.7 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.CdmTransientEntityAndUuidCacher;
23
import eu.etaxonomy.cdm.debug.PersistentContextAnalyzer;
24
import eu.etaxonomy.cdm.model.ICdmEntityUuidCacher;
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
    protected BeanInstantiator<DTO> beanInstantiator = null;
52

    
53

    
54
    /**
55
     * @param beanInstantiator the beanInstantiator to set
56
     */
57
    public void setBeanInstantiator(BeanInstantiator<DTO> beanInstantiator) {
58
        this.beanInstantiator = beanInstantiator;
59
    }
60

    
61

    
62
    protected DTO createNewBean() {
63
        if(this.beanInstantiator != null){
64
            return beanInstantiator.createNewBean();
65
        }
66
        return defaultBeanInstantiator().createNewBean();
67
    }
68

    
69
    /**
70
     * @return
71
     */
72
    protected abstract BeanInstantiator<DTO> defaultBeanInstantiator();
73

    
74
    /**
75
     * if not null, this CRUD set is to be used to create a CdmAuthoritiy for the base entity which will be
76
     * granted to the current use as long this grant is not assigned yet.
77
     */
78
    protected EnumSet<CRUD> crud = null;
79

    
80

    
81
    private ICdmEntityUuidCacher cache;
82

    
83
    private java.util.Collection<CdmBase> rootEntities = new HashSet<>();
84

    
85
    public CdmEditorPresenterBase() {
86
        super();
87
        logger.trace(this._toString() + " constructor");
88
    }
89

    
90
    CdmStore<CDM, IService<CDM>> store ;
91

    
92
    protected CdmAuthority newAuthorityCreated;
93

    
94
    @Autowired
95
    protected CdmFilterablePagingProviderFactory pagingProviderFactory;
96

    
97
    protected CdmStore<CDM, IService<CDM>> getStore() {
98
        if(store == null){
99
            store = new CdmStore<>(getRepo(), getService());
100
        }
101
        return store;
102
    }
103

    
104
    @Override
105
    protected DTO loadBeanById(Object identifier) {
106

    
107
        CDM cdmEntitiy;
108
        if(identifier != null) {
109
            UUID uuidIdentifier = (UUID)identifier;
110
            // CdmAuthority is needed before the bean is loaded into the session.
111
            // otherwise adding the authority to the user would cause a flush
112
            cdmEntitiy = loadCdmEntity(uuidIdentifier);
113
            guaranteePerEntityCRUDPermissions(cdmEntitiy);
114
        } else {
115
            cdmEntitiy = loadCdmEntity(null);
116
            if(cdmEntitiy != null){
117
                guaranteePerEntityCRUDPermissions(cdmEntitiy);
118
            }
119
        }
120
        cache = new CdmTransientEntityAndUuidCacher(this);
121
        // need to use load but put see #7214
122
        cdmEntitiy = cache.load(cdmEntitiy);
123
        addRootEntity(cdmEntitiy);
124

    
125
        DTO dto = createDTODecorator(cdmEntitiy);
126

    
127
        return dto;
128
    }
129

    
130
    /**
131
     * @param cdmEntitiy
132
     * @return
133
     */
134
    protected abstract DTO createDTODecorator(CDM cdmEntitiy);
135

    
136
    /**
137
     * @param cdmEntitiy
138
     */
139
    @Override
140
    protected void adaptToUserPermission(DTO dto) {
141

    
142
        CDM cdmEntitiy = cdmEntity(dto);
143

    
144
        UserHelper userHelper = UserHelperAccess.userHelper();
145
        boolean canDelte = userHelper.userHasPermission(cdmEntitiy, CRUD.DELETE);
146
        boolean canEdit = userHelper.userHasPermission(cdmEntitiy, CRUD.UPDATE);
147

    
148
        User user = userHelper.user();
149

    
150
        if(AbstractCdmPopupEditor.class.isAssignableFrom(getView().getClass())){
151
            AbstractCdmPopupEditor popupView = ((AbstractCdmPopupEditor)getView());
152

    
153
            if(cdmEntitiy.isPersited() && !canEdit){
154
                popupView.setReadOnly(true); // never reset true to false here!
155
                logger.debug("setting editor to readonly");
156
            }
157
            if(!cdmEntitiy.isPersited() || !canDelte){
158
                popupView.withDeleteButton(false);
159
                logger.debug("removing delete button");
160
            }
161
        }
162

    
163
    }
164

    
165
    /**
166
     * @param dto
167
     * @return
168
     */
169
    protected abstract CDM cdmEntity(DTO dto);
170

    
171
    /**
172
     * @param identifier
173
     * @return
174
     */
175
    protected abstract CDM loadCdmEntity(UUID uuid);
176

    
177
    /**
178
     * Grant per entity CdmAuthority to the current user <b>for the bean which is not yet loaded</b>
179
     * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
180
     */
181
    protected abstract void guaranteePerEntityCRUDPermissions(UUID identifier);
182

    
183
    /**
184
     * Grant per entity CdmAuthority to the current user for the bean which is loaded
185
     * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
186
     */
187
     protected abstract void guaranteePerEntityCRUDPermissions(CDM bean);
188

    
189
    /**
190
     * @return
191
     */
192
    protected abstract IService<CDM> getService();
193

    
194
    @SuppressWarnings("unchecked")
195
    @Override
196
    // @EventBusListenerMethod // already annotated at super class
197
    public void onEditorPreSaveEvent(EditorPreSaveEvent preSaveEvent){
198

    
199
        if(!isFromOwnView(preSaveEvent)){
200
            return;
201
        }
202
        super.onEditorPreSaveEvent(preSaveEvent);
203
    }
204

    
205
    @Override
206
    // @EventBusListenerMethod // already annotated at super class
207
    public void onEditorSaveEvent(EditorSaveEvent<DTO> saveEvent){
208

    
209
        if(!isFromOwnView(saveEvent)){
210
            return;
211
        }
212

    
213
        // the bean is now updated with the changes made by the user
214
        DTO dto = saveEvent.getBean();
215
        CDM cdmEntity = cdmEntity(dto);
216

    
217
        if(logger.isTraceEnabled()){
218
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
219
            pca.printEntityGraph(System.err);
220
            pca.printCopyEntities(System.err);
221
        }
222

    
223
        if(logger.isTraceEnabled()){
224
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
225
            pca.printEntityGraph(System.err);
226
            pca.printCopyEntities(System.err);
227
        }
228
        EntityChangeEvent<?> changeEvent = null;
229
        try {
230
            dto = preSaveBean(dto);
231
            changeEvent = getStore().saveBean(cdmEntity, (AbstractView<?>) getView());
232

    
233
            if(changeEvent != null){
234
                viewEventBus.publish(this, changeEvent);
235
            }
236
        } catch (HibernateException e){
237
            if(newAuthorityCreated != null){
238
                UserHelperAccess.userHelper().removeAuthorityForCurrentUser(newAuthorityCreated);
239
            }
240
            throw e;
241
        } finally {
242
            postSaveBean(changeEvent);
243
        }
244
    }
245

    
246
    /**
247
     * This method is intended to be used for the following purposes:
248
     * <ol>
249
     *   <li>
250
     *   EditorPresenters for beans with transient properties can overwrite this method to
251
     *   update the beanItem with the changes made to the transient properties.
252
     *   This can be necessary because Vaadin MethodProperties are readonly when no setter is
253
     *   available. This can be the case with transient properties. Automatic updating
254
     *   of the property during the fieldGroup commit does not work in this case.
255
     *   Presenters, however, should <b>operate on DTOs instead, which can implement the missing setter</b>.</li>
256
     *
257
     *   <li>When modifying a bi-directional relation between two instances the user would
258
     *   need to have GrantedAuthorities for both sides of the relationship. This, however is not
259
     *   always possible. As a temporary solution the user can be granted the missing authority just
260
     *   for the time of saving the new relationship. You may also want to implement
261
     *   {@link #postSaveBean(EntityChangeEvent)} in this case.
262
     *   See {@link https://dev.e-taxonomy.eu/redmine/issues/7390 #7390}
263
     *   </li>
264
     * </ol>
265
     *
266
     */
267
    protected DTO preSaveBean(DTO bean) {
268
        // blank implementation, to be implemented by sub classes if needed
269
        return bean;
270
    }
271

    
272
    @Override
273
    protected
274
    final void saveBean(DTO bean){
275
        // blank implementation, since this is not needed in this or any sub class
276
        // see onEditorSaveEvent() instead
277
    }
278

    
279
    /**
280
     * Called after saving the DTO to the persistent storage.
281
     * This method is called in any case even if the save operation failed.
282
     * See {@link  #postSaveBean(EntityChangeEvent)}.
283
     *
284
     * @param changeEvent may be null in case of errors during the save operation
285
     */
286
    protected void postSaveBean(EntityChangeEvent changeEvent) {
287
        // blank implementation, to be implemented by sub classes if needed
288
    }
289

    
290
    @Override
291
    protected void deleteBean(DTO bean){
292
        CDM cdmEntity = cdmEntity(bean);
293
        EntityChangeEvent changeEvent = getStore().deleteBean(cdmEntity, (AbstractView) getView());
294
        if(changeEvent != null){
295
            viewEventBus.publish(this, changeEvent);
296
        }
297

    
298
    }
299

    
300
    /**
301
     * @param crud
302
     */
303
    public void setGrantsForCurrentUser(EnumSet<CRUD> crud) {
304
        this.crud = crud;
305

    
306
    }
307

    
308
    /**
309
     * {@inheritDoc}
310
     */
311
    @Override
312
    public ICdmEntityUuidCacher getCache() {
313
        return cache;
314
    }
315

    
316
    /**
317
     * {@inheritDoc}
318
     */
319
    @Override
320
    public void addRootEntity(CdmBase entity) {
321
        rootEntities.add(entity);
322
    }
323

    
324

    
325
    /**
326
     * {@inheritDoc}
327
     */
328
    @Override
329
    public Collection<CdmBase> getRootEntities() {
330
        return rootEntities;
331
    }
332

    
333

    
334
    /**
335
     * {@inheritDoc}
336
     */
337
    @Override
338
    public void destroy() throws Exception {
339
        super.destroy();
340
        disposeCache();
341
    }
342

    
343
    /**
344
     * {@inheritDoc}
345
     */
346
    @Override
347
    public void disposeCache() {
348
        cache.dispose();
349
    }
350
}
(13-13/14)