2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.vaadin
.mvp
;
11 import java
.util
.Collection
;
12 import java
.util
.EnumSet
;
13 import java
.util
.HashSet
;
14 import java
.util
.UUID
;
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
;
21 import eu
.etaxonomy
.cdm
.api
.service
.IService
;
22 import eu
.etaxonomy
.cdm
.api
.util
.UserHelper
;
23 import eu
.etaxonomy
.cdm
.cache
.CdmTransientEntityWithUuidCacher
;
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
;
39 * Provides generic save operations of modified cdm entities.
41 * @author a.kohlbecker
44 public abstract class CdmEditorPresenterBase
<DTO
, CDM
extends CdmBase
, V
extends ApplicationView
<?
>>
45 extends AbstractEditorPresenter
<DTO
, V
>
46 implements CachingPresenter
{
48 private static final long serialVersionUID
= 2218185546277084261L;
50 private final static Logger logger
= LogManager
.getLogger();
52 protected BeanInstantiator
<DTO
> beanInstantiator
= null;
55 protected CdmBeanItemContainerFactory cdmBeanItemContainerFactory
;
58 protected CdmFilterablePagingProviderFactory pagingProviderFactory
;
61 * @param beanInstantiator the beanInstantiator to set
63 public void setBeanInstantiator(BeanInstantiator
<DTO
> beanInstantiator
) {
64 this.beanInstantiator
= beanInstantiator
;
68 protected DTO
createNewBean() {
69 if(this.beanInstantiator
!= null){
70 return beanInstantiator
.createNewBean();
72 return defaultBeanInstantiator().createNewBean();
78 protected abstract BeanInstantiator
<DTO
> defaultBeanInstantiator();
81 * if not null, this CRUD set is to be used to create a CdmAuthoritiy for the base entity which will be
82 * granted to the current use as long this grant is not assigned yet.
84 protected EnumSet
<CRUD
> crud
= null;
87 private ICdmEntityUuidCacher cache
;
89 private java
.util
.Collection
<CdmBase
> rootEntities
= new HashSet
<>();
91 public CdmEditorPresenterBase() {
93 logger
.trace(this._toString() + " constructor");
97 protected CdmStore cdmStore
;
99 protected CdmAuthority newAuthorityCreated
;
102 protected DTO
loadBeanById(Object identifier
) {
105 if(identifier
!= null) {
106 UUID uuidIdentifier
= (UUID
)identifier
;
107 // CdmAuthority is needed before the bean is loaded into the session.
108 // otherwise adding the authority to the user would cause a flush
109 cdmEntitiy
= loadCdmEntity(uuidIdentifier
);
110 guaranteePerEntityCRUDPermissions(cdmEntitiy
);
112 cdmEntitiy
= loadCdmEntity(null);
113 if(cdmEntitiy
!= null){
114 guaranteePerEntityCRUDPermissions(cdmEntitiy
);
117 DTO dto
= initializeCache(cdmEntitiy
);
124 * @param cdmEntitiy the CDM entity to initialize the cache with.
126 protected final DTO
initializeCache(CDM cdmEntitiy
) {
127 cache
= new CdmTransientEntityWithUuidCacher(this);
128 // need to use load but put see #7214
129 cdmEntitiy
= cache
.load(cdmEntitiy
);
130 addRootEntity(cdmEntitiy
);
132 DTO dto
= createDTODecorator(cdmEntitiy
);
140 protected abstract DTO
createDTODecorator(CDM cdmEntitiy
);
146 protected void adaptToUserPermission(DTO dto
) {
148 CDM cdmEntitiy
= cdmEntity(dto
);
150 UserHelper userHelper
= UserHelperAccess
.userHelper();
151 boolean canDelete
= userHelper
.userHasPermission(cdmEntitiy
, CRUD
.DELETE
);
152 boolean canEdit
= userHelper
.userHasPermission(cdmEntitiy
, CRUD
.UPDATE
);
154 if(AbstractPopupEditor
.class.isAssignableFrom(getView().getClass())){
155 AbstractPopupEditor
<?
,?
> popupView
= ((AbstractPopupEditor
<?
,?
>)getView());
157 if(cdmEntitiy
.isPersited() && !canEdit
){
158 popupView
.setReadOnly(true); // never reset true to false here!
159 logger
.debug("setting editor to readonly");
161 if(!cdmEntitiy
.isPersited() || !canDelete
){
162 popupView
.withDeleteButton(false);
163 logger
.debug("removing delete button");
173 protected abstract CDM
cdmEntity(DTO dto
);
179 protected abstract CDM
loadCdmEntity(UUID uuid
);
182 * Grant per entity CdmAuthority to the current user <b>for the bean which is not yet loaded</b>
183 * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
185 protected abstract void guaranteePerEntityCRUDPermissions(UUID identifier
);
188 * Grant per entity CdmAuthority to the current user for the bean which is loaded
189 * into the editor. The <code>CRUD</code> to be granted are stored in the <code>crud</code> field.
191 protected abstract void guaranteePerEntityCRUDPermissions(CDM bean
);
196 protected abstract IService
<CDM
> getService();
199 // @EventBusListenerMethod // already annotated at super class
200 public void onEditorPreSaveEvent(EditorPreSaveEvent
<DTO
> preSaveEvent
){
201 if(!isFromOwnView(preSaveEvent
)){
204 super.onEditorPreSaveEvent(preSaveEvent
);
208 // @EventBusListenerMethod // already annotated at super class
209 public void onEditorSaveEvent(EditorSaveEvent
<DTO
> saveEvent
){
211 if(!isFromOwnView(saveEvent
)){
215 // the bean is now updated with the changes made by the user
216 DTO dto
= saveEvent
.getBean();
217 CDM cdmEntity
= cdmEntity(dto
);
219 if(logger
.isTraceEnabled()){
220 PersistentContextAnalyzer pca
= new PersistentContextAnalyzer(cdmEntity
);
221 pca
.printEntityGraph(System
.err
);
222 pca
.printCopyEntities(System
.err
);
225 if(logger
.isTraceEnabled()){
226 PersistentContextAnalyzer pca
= new PersistentContextAnalyzer(cdmEntity
);
227 pca
.printEntityGraph(System
.err
);
228 pca
.printCopyEntities(System
.err
);
230 EntityChangeEvent
<?
> changeEvent
= null;
232 dto
= preSaveBean(dto
);
233 changeEvent
= cdmStore
.saveBean(cdmEntity
, (AbstractView
<?
>) getView());
235 if(changeEvent
!= null){
236 viewEventBus
.publish(this, changeEvent
);
238 } catch (HibernateException e
){
239 if(newAuthorityCreated
!= null){
240 UserHelperAccess
.userHelper().removeAuthorityForCurrentUser(newAuthorityCreated
);
244 postSaveBean(changeEvent
);
249 * This method is intended to be used for the following purposes:
252 * EditorPresenters for beans with transient properties can overwrite this method to
253 * update the beanItem with the changes made to the transient properties.
254 * This can be necessary because Vaadin MethodProperties are readonly when no setter is
255 * available. This can be the case with transient properties. Automatic updating
256 * of the property during the fieldGroup commit does not work in this case.
257 * Presenters, however, should <b>operate on DTOs instead, which can implement the missing setter</b>.</li>
259 * <li>When modifying a bi-directional relation between two instances the user would
260 * need to have GrantedAuthorities for both sides of the relationship. This, however is not
261 * always possible. As a temporary solution the user can be granted the missing authority just
262 * for the time of saving the new relationship. You may also want to implement
263 * {@link #postSaveBean(EntityChangeEvent)} in this case.
264 * See {@link https://dev.e-taxonomy.eu/redmine/issues/7390 #7390}
269 protected DTO
preSaveBean(DTO bean
) {
270 // blank implementation, to be implemented by sub classes if needed
276 final void saveBean(DTO bean
){
277 // blank implementation, since this is not needed in this or any sub class
278 // see onEditorSaveEvent() instead
282 * Called after saving the DTO to the persistent storage.
283 * This method is called in any case even if the save operation failed.
284 * See {@link #postSaveBean(EntityChangeEvent)}.
286 * @param changeEvent may be null in case of errors during the save operation
288 protected void postSaveBean(EntityChangeEvent
<?
> changeEvent
) {
289 // blank implementation, to be implemented by sub classes if needed
293 protected void deleteBean(DTO bean
){
294 CDM cdmEntity
= cdmEntity(bean
);
295 EntityChangeEvent
<?
> changeEvent
= cdmStore
.deleteBean(cdmEntity
, (AbstractView
<?
>) getView());
296 if(changeEvent
!= null){
297 viewEventBus
.publish(this, changeEvent
);
301 public void setGrantsForCurrentUser(EnumSet
<CRUD
> crud
) {
307 public ICdmEntityUuidCacher
getCache() {
312 public void addRootEntity(CdmBase entity
) {
313 rootEntities
.add(entity
);
317 public Collection
<CdmBase
> getRootEntities() {
322 public void destroy() throws Exception
{
328 public void disposeCache() {