Project

General

Profile

Download (10.6 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.logging.log4j.LogManager;
17
import org.apache.logging.log4j.Logger;
18
import org.hibernate.HibernateException;
19
import org.springframework.beans.factory.annotation.Autowired;
20

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

    
38
/**
39
 * Provides generic save operations of modified cdm entities.
40
 *
41
 * @author a.kohlbecker
42
 * @since Apr 5, 2017
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 final static Logger logger = LogManager.getLogger();
50

    
51
    protected BeanInstantiator<DTO> beanInstantiator = null;
52

    
53
    @Autowired
54
    protected CdmBeanItemContainerFactory cdmBeanItemContainerFactory;
55

    
56
    @Autowired
57
    protected CdmFilterablePagingProviderFactory pagingProviderFactory;
58

    
59
    /**
60
     * @param beanInstantiator the beanInstantiator to set
61
     */
62
    public void setBeanInstantiator(BeanInstantiator<DTO> beanInstantiator) {
63
        this.beanInstantiator = beanInstantiator;
64
    }
65

    
66

    
67
    protected DTO createNewBean() {
68
        if(this.beanInstantiator != null){
69
            return beanInstantiator.createNewBean();
70
        }
71
        return defaultBeanInstantiator().createNewBean();
72
    }
73

    
74
    /**
75
     * @return
76
     */
77
    protected abstract BeanInstantiator<DTO> defaultBeanInstantiator();
78

    
79
    /**
80
     * if not null, this CRUD set is to be used to create a CdmAuthoritiy for the base entity which will be
81
     * granted to the current use as long this grant is not assigned yet.
82
     */
83
    protected EnumSet<CRUD> crud = null;
84

    
85

    
86
    private ICdmEntityUuidCacher cache;
87

    
88
    private java.util.Collection<CdmBase> rootEntities = new HashSet<>();
89

    
90
    public CdmEditorPresenterBase() {
91
        super();
92
        logger.trace(this._toString() + " constructor");
93
    }
94

    
95
    @Autowired
96
    protected CdmStore cdmStore;
97

    
98
    protected CdmAuthority newAuthorityCreated;
99

    
100
    @Override
101
    protected DTO loadBeanById(Object identifier) {
102

    
103
        CDM cdmEntitiy;
104
        if(identifier != null) {
105
            UUID uuidIdentifier = (UUID)identifier;
106
            // CdmAuthority is needed before the bean is loaded into the session.
107
            // otherwise adding the authority to the user would cause a flush
108
            cdmEntitiy = loadCdmEntity(uuidIdentifier);
109
            guaranteePerEntityCRUDPermissions(cdmEntitiy);
110
        } else {
111
            cdmEntitiy = loadCdmEntity(null);
112
            if(cdmEntitiy != null){
113
                guaranteePerEntityCRUDPermissions(cdmEntitiy);
114
            }
115
        }
116
        DTO dto = initializeCache(cdmEntitiy);
117

    
118
        return dto;
119
    }
120

    
121

    
122
    /**
123
     * @param cdmEntitiy the CDM entity to initialize the cache with.
124
     */
125
    protected final DTO initializeCache(CDM cdmEntitiy) {
126
        cache = new CdmTransientEntityAndUuidCacher(this);
127
        // need to use load but put see #7214
128
        cdmEntitiy = cache.load(cdmEntitiy);
129
        addRootEntity(cdmEntitiy);
130

    
131
        DTO dto = createDTODecorator(cdmEntitiy);
132
        return dto;
133
    }
134

    
135
    /**
136
     * @param cdmEntitiy
137
     * @return
138
     */
139
    protected abstract DTO createDTODecorator(CDM cdmEntitiy);
140

    
141
    /**
142
     * @param cdmEntitiy
143
     */
144
    @Override
145
    protected void adaptToUserPermission(DTO dto) {
146

    
147
        CDM cdmEntitiy = cdmEntity(dto);
148

    
149
        UserHelper userHelper = UserHelperAccess.userHelper();
150
        boolean canDelte = userHelper.userHasPermission(cdmEntitiy, CRUD.DELETE);
151
        boolean canEdit = userHelper.userHasPermission(cdmEntitiy, CRUD.UPDATE);
152

    
153
        if(AbstractPopupEditor.class.isAssignableFrom(getView().getClass())){
154
            AbstractPopupEditor popupView = ((AbstractPopupEditor)getView());
155

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

    
166
    }
167

    
168
    /**
169
     * @param dto
170
     * @return
171
     */
172
    protected abstract CDM cdmEntity(DTO dto);
173

    
174
    /**
175
     * @param identifier
176
     * @return
177
     */
178
    protected abstract CDM loadCdmEntity(UUID uuid);
179

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

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

    
192
    /**
193
     * @return
194
     */
195
    protected abstract IService<CDM> getService();
196

    
197
    @SuppressWarnings("unchecked")
198
    @Override
199
    // @EventBusListenerMethod // already annotated at super class
200
    public void onEditorPreSaveEvent(EditorPreSaveEvent preSaveEvent){
201

    
202
        if(!isFromOwnView(preSaveEvent)){
203
            return;
204
        }
205
        super.onEditorPreSaveEvent(preSaveEvent);
206
    }
207

    
208
    @Override
209
    // @EventBusListenerMethod // already annotated at super class
210
    public void onEditorSaveEvent(EditorSaveEvent<DTO> saveEvent){
211

    
212
        if(!isFromOwnView(saveEvent)){
213
            return;
214
        }
215

    
216
        // the bean is now updated with the changes made by the user
217
        DTO dto = saveEvent.getBean();
218
        CDM cdmEntity = cdmEntity(dto);
219

    
220
        if(logger.isTraceEnabled()){
221
            PersistentContextAnalyzer pca = new PersistentContextAnalyzer(cdmEntity);
222
            pca.printEntityGraph(System.err);
223
            pca.printCopyEntities(System.err);
224
        }
225

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

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

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

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

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

    
293
    @Override
294
    protected void deleteBean(DTO bean){
295
        CDM cdmEntity = cdmEntity(bean);
296
        EntityChangeEvent<?> changeEvent = cdmStore.deleteBean(cdmEntity, (AbstractView) getView());
297
        if(changeEvent != null){
298
            viewEventBus.publish(this, changeEvent);
299
        }
300
    }
301

    
302
    public void setGrantsForCurrentUser(EnumSet<CRUD> crud) {
303
        this.crud = crud;
304

    
305
    }
306

    
307
    @Override
308
    public ICdmEntityUuidCacher getCache() {
309
        return cache;
310
    }
311

    
312
    @Override
313
    public void addRootEntity(CdmBase entity) {
314
        rootEntities.add(entity);
315
    }
316

    
317
    @Override
318
    public Collection<CdmBase> getRootEntities() {
319
        return rootEntities;
320
    }
321

    
322
    @Override
323
    public void destroy() throws Exception {
324
        super.destroy();
325
        disposeCache();
326
    }
327

    
328
    @Override
329
    public void disposeCache() {
330
        cache.dispose();
331
    }
332
}
(13-13/15)