Project

General

Profile

« Previous | Next » 

Revision 2b9b03a1

Added by Andreas Kohlbecker over 4 years ago

fix #7154 CdmEntityCache can update cached entities and using this in ToOneRelatedEntityReloader and in
SpecimenTypeDesignationWorkingsetEditorPresenter to update the ui with modified collections

View differences:

src/main/java/eu/etaxonomy/cdm/cache/CdmEntityCache.java
19 19
import java.util.Map;
20 20
import java.util.Set;
21 21

  
22
import org.apache.commons.beanutils.BeanUtilsBean;
22 23
import org.apache.commons.beanutils.PropertyUtils;
24
import org.apache.commons.beanutils.PropertyUtilsBean;
23 25
import org.apache.commons.lang.builder.HashCodeBuilder;
24 26
import org.apache.log4j.Logger;
25 27
import org.hibernate.Hibernate;
......
30 32

  
31 33
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
32 34
import eu.etaxonomy.cdm.model.common.CdmBase;
35
import eu.etaxonomy.cdm.model.common.VersionableEntity;
33 36
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
34 37
import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
35 38

  
......
88 91
     */
89 92
    protected void analyzeEntity(CdmBase bean, String propertyPath) {
90 93

  
94
        CdmBase proxyBean = bean;
95

  
96
        bean = HibernateProxyHelper.deproxy(proxyBean, CdmBase.class);
97

  
91 98
        EntityKey entityKey = new EntityKey(bean);
92 99

  
93 100
        propertyPath += "[" + entityKey;
94 101
        String flags = "";
95
        CdmBase mappedEntity = entityyMap.put(entityKey, bean);
102
        CdmBase mappedEntity = entityyMap.put(entityKey, proxyBean);
96 103

  
97 104
        if(mappedEntity != null && mappedEntity != bean) {
98 105
            copyEntitiyKeys.add(entityKey);
......
154 161
                                || propertyValue instanceof MapProxy<?, ?>
155 162
                                || propertyValue instanceof SortedMapProxy<?, ?>){
156 163
                            //hibernate envers collections
164
                            // FIXME this won't work!!!!
157 165
                            collection = (Collection<CdmBase>)propertyValue;
158 166
                    }
159 167

  
......
276 284

  
277 285
    /**
278 286
     * {@inheritDoc}
287
     * <p>
288
     * In case the cached bean is a HibernateProxy it will be unproxied
289
     * before returning it.
279 290
     */
280 291
    @Override
281 292
    public <CDM extends CdmBase> CDM find(CDM value) {
282 293
        if(value != null){
283 294
            EntityKey entityKey = new EntityKey(HibernateProxyHelper.deproxy(value));
295
            CDM cachedBean = (CDM) entityyMap.get(entityKey);
296
            if(cachedBean != null){
297
                return (CDM) HibernateProxyHelper.deproxy(cachedBean, CdmBase.class);
298
            }
299
        }
300
        return null;
301
    }
302

  
303
    private <CDM extends CdmBase> CDM findProxy(CDM value) {
304
        if(value != null){
305
            EntityKey entityKey = new EntityKey(HibernateProxyHelper.deproxy(value));
284 306
            return (CDM) entityyMap.get(entityKey);
285 307
        }
286 308
        return null;
......
288 310

  
289 311
    /**
290 312
     * {@inheritDoc}
313
     * <p>
314
     * In case the cached bean is a HibernateProxy it will be unproxied
315
     * before returning it.
291 316
     */
292 317
    @Override
293 318
    public <CDM extends CdmBase> CDM find(Class<CDM> type, int id) {
294 319
        EntityKey entityKey = new EntityKey(type, id);
295
        return (CDM) entityyMap.get(entityKey);
320
        CDM cachedBean = (CDM) entityyMap.get(entityKey);
321
        if(cachedBean != null){
322
            return (CDM) HibernateProxyHelper.deproxy(cachedBean, CdmBase.class);
323
        }
324
        return null;
325
    }
326

  
327
    @Override
328
    public <CDM extends CdmBase> CDM findAndUpdate(CDM value){
329
        CDM cachedBean = findProxy(value);
330
        if(cachedBean != null && VersionableEntity.class.isAssignableFrom(cachedBean.getClass())){
331
            updatedCachedIfEarlier((VersionableEntity)cachedBean, (VersionableEntity)value);
332
        }
333
        if(cachedBean != null){
334
            return (CDM) HibernateProxyHelper.deproxy(cachedBean, CdmBase.class);
335
        }
336
        return null;
337

  
338
    }
339

  
340
    /**
341
     * @param cachedValue
342
     * @param value
343
     */
344
    private <CDM extends VersionableEntity> void updatedCachedIfEarlier(CDM cachedValue, CDM value) {
345
       if(cachedValue != null && value != null && value.getUpdated() != null){
346
           if(cachedValue.getUpdated() == null || value.getUpdated().isAfter(cachedValue.getUpdated())){
347
               try {
348
                copyProperties(cachedValue, value);
349
            } catch (IllegalAccessException e) {
350
                /* should never happen */
351
                e.printStackTrace();
352
            } catch (InvocationTargetException e) {
353
                /* critical! re-throw as runtime exception, which is ok in the context of a vaadin app */
354
                throw new RuntimeException(e);
355
            }
356
           }
357
       }
358

  
359
    }
360

  
361
    /**
362
     * partially copy of {@link BeanUtilsBean#copyProperties(Object, Object)}
363
     */
364
    private <CDM extends VersionableEntity> void copyProperties(CDM dest, CDM orig) throws IllegalAccessException, InvocationTargetException {
365

  
366
        PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance().getPropertyUtils();
367
        PropertyDescriptor[] origDescriptors =
368
                propertyUtils.getPropertyDescriptors(orig);
369
            for (int i = 0; i < origDescriptors.length; i++) {
370
                String name = origDescriptors[i].getName();
371
                if ("class".equals(name)) {
372
                    continue; // No point in trying to set an object's class
373
                }
374
                if (propertyUtils.isReadable(orig, name) &&
375
                        propertyUtils.isWriteable(dest, name)) {
376
                    try {
377
                        if(CdmBase.class.isAssignableFrom(propertyUtils.getPropertyType(dest, name))){
378
                            CdmBase origValue = (CdmBase)propertyUtils.getSimpleProperty(orig, name);
379
                            if(!Hibernate.isInitialized(origValue)){
380
                                // ignore uninitialized entities
381
                                continue;
382
                            }
383
                            // only copy entities if origValue either is a completely different entity (A)
384
                            // or if origValue is updated (B).
385
                            CdmBase destValue = (CdmBase)propertyUtils.getSimpleProperty(dest, name);
386
                            if(destValue == null && origValue == null){
387
                                continue;
388
                            }
389
                            if(
390
                                    origValue != null && destValue == null ||
391
                                    origValue == null && destValue != null ||
392

  
393
                                    origValue.getId() != destValue.getId() ||
394
                                    origValue.getClass() != destValue.getClass() ||
395
                                    origValue.getUuid() != destValue.getUuid()){
396
                                // (A)
397
                                BeanUtilsBean.getInstance().copyProperty(dest, name, origValue);
398

  
399
                            } else {
400
                                // (B) recurse into findAndUpdate
401
                                findAndUpdate(origValue);
402
                            }
403
                        } else {
404
                                Object value = propertyUtils.getSimpleProperty(orig, name);
405
                                BeanUtilsBean.getInstance().copyProperty(dest, name, value);
406
                        }
407

  
408
                    } catch (NoSuchMethodException e) {
409
                        // Should not happen
410
                    }
411
                }
412
            }
413

  
296 414
    }
297 415

  
298 416
    /**
src/main/java/eu/etaxonomy/cdm/cache/EntityCache.java
15 15
 * @since 09.11.2017
16 16
 *
17 17
 */
18
/**
19
 * @author a.kohlbecker
20
 * @since Jan 4, 2018
21
 *
22
 */
18 23
public interface EntityCache {
19 24

  
20 25
    /**
21 26
     * @param value
22
     * @return
27
     *
28
     * @return the cached entity if it is found in the cache otherwise null
23 29
     */
24 30
    public <CDM extends CdmBase> CDM find(CDM value);
25 31

  
......
35 41
    public <CDM extends CdmBase> CDM find(Class<CDM> type, int id);
36 42

  
37 43

  
44
    /**
45
     * Find the <code>value</code> in the cache and update it in case it
46
     * has an later <code>updatedWhen</code> value.
47
     * The properties of later updated entity will be copied over to the cached entity before it is returned.
48
     *
49
     * @param value the value to be searched in the cache
50
     *
51
     * @return the cached entity if it is found in the cache otherwise null
52
     */
53
    public <CDM extends CdmBase> CDM findAndUpdate(CDM value);
54

  
55

  
38 56
}
src/main/java/eu/etaxonomy/cdm/service/RegistrationWorkingSetService.java
65 65
            "name.nomenclaturalReference.authorship",
66 66
            "name.nomenclaturalReference.inReference",
67 67
            "name.rank",
68
            "name.homotypicalGroup.typifiedNames",
68 69
            "name.status.type",
69 70
            "name.typeDesignations", // important !!"
70 71
            // institution
src/main/java/eu/etaxonomy/cdm/vaadin/event/ToOneRelatedEntityReloader.java
62 62
        EntityCache cache = cachingPresenter.getCache();
63 63
        if(cache != null){
64 64
            cache.update();
65
            CDM cachedEntity = cache.find(value);
65
            CDM cachedEntity = cache.findAndUpdate(value);
66 66
            if(cachedEntity == null){
67 67
                cache.add(value);
68 68
            } else if(
src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationWorkingsetEditorPresenter.java
8 8
*/
9 9
package eu.etaxonomy.cdm.vaadin.view.name;
10 10

  
11
import java.util.Arrays;
11 12
import java.util.EnumSet;
13
import java.util.HashSet;
14
import java.util.Set;
12 15

  
13 16
import org.springframework.beans.factory.annotation.Autowired;
14 17
import org.springframework.context.event.EventListener;
......
28 31
import eu.etaxonomy.cdm.service.ISpecimenTypeDesignationWorkingSetService;
29 32
import eu.etaxonomy.cdm.vaadin.component.CdmBeanItemContainerFactory;
30 33
import eu.etaxonomy.cdm.vaadin.component.CollectionRowItemCollection;
34
import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
31 35
import eu.etaxonomy.cdm.vaadin.event.ToOneRelatedEntityButtonUpdater;
32 36
import eu.etaxonomy.cdm.vaadin.event.ToOneRelatedEntityReloader;
33 37
import eu.etaxonomy.cdm.vaadin.model.registration.RegistrationTermLists;
......
39 43
import eu.etaxonomy.vaadin.component.ToOneRelatedEntityCombobox;
40 44
import eu.etaxonomy.vaadin.mvp.AbstractEditorPresenter;
41 45
import eu.etaxonomy.vaadin.mvp.AbstractPopupEditor;
42
import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
43
import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent.Reason;
44 46
/**
45 47
 * SpecimenTypeDesignationWorkingsetPopupEditorView implementation must override the showInEditor() method,
46 48
 * see {@link #prepareAsFieldGroupDataSource()} for details.
......
79 81

  
80 82
    private CollectionPopupEditor collectionPopuEditor;
81 83

  
82
    private CollectionRowItemCollection collectionPopuEditorSourceRow;
84
    private Set<CollectionRowItemCollection> collectionPopuEditorSourceRows = new HashSet<>();
83 85

  
84 86
    protected CdmStore<Registration, IRegistrationService> getStore() {
85 87
        if(store == null){
......
144 146
        getView().getTypeDesignationsCollectionField().addElementAddedListener(e -> addTypeDesignation(e.getElement()));
145 147

  
146 148

  
149
        collectionPopuEditorSourceRows.clear();
147 150
        getView().getTypeDesignationsCollectionField().setEditorInstantiator(new AbstractElementCollection.Instantiator<SpecimenTypeDesignationDTORow>() {
148 151

  
149 152
            CdmFilterablePagingProvider<Collection, Collection> collectionPagingProvider = new CdmFilterablePagingProvider<Collection, Collection>(getRepo().getCollectionService());
......
175 178
                row.collection.getSelect().addValueChangeListener(new ToOneRelatedEntityButtonUpdater<Collection>(row.collection));
176 179
                row.collection.getSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Collection>(row.collection.getSelect(),
177 180
                        SpecimenTypeDesignationWorkingsetEditorPresenter.this));
178
                row.collection.addClickListenerAddEntity( e -> doCollectionEditorAdd(row));
181
                row.collection.addClickListenerAddEntity( e -> doCollectionEditorAdd());
179 182
                row.collection.addClickListenerEditEntity(e -> {
180 183
                        if(row.collection.getValue() != null){
181
                            doCollectionEditorEdit(row, row.collection.getValue().getId());
184
                            doCollectionEditorEdit(row.collection.getValue().getId());
182 185
                        }
183 186
                    });
184 187

  
......
195 198

  
196 199
                getView().applyDefaultComponentStyle(row.components());
197 200

  
201
                collectionPopuEditorSourceRows.add(row);
202

  
198 203
                return row;
199 204
            }
200 205

  
......
272 277
        }
273 278
    }
274 279

  
275
    public void doCollectionEditorAdd(CollectionRowItemCollection rowItemCollection) {
280
    public void doCollectionEditorAdd() {
276 281

  
277
        collectionPopuEditorSourceRow = rowItemCollection;
278 282
        collectionPopuEditor = getNavigationManager().showInPopup(CollectionPopupEditor.class);
279 283

  
280 284
        collectionPopuEditor.grantToCurrentUser(COLLECTION_EDITOR_CRUD);
......
282 286
        collectionPopuEditor.loadInEditor(null);
283 287
    }
284 288

  
285
    public void doCollectionEditorEdit(CollectionRowItemCollection rowItemCollection, int collectionId) {
289
    public void doCollectionEditorEdit(int collectionId) {
286 290

  
287
        collectionPopuEditorSourceRow = rowItemCollection;
288 291
        collectionPopuEditor = getNavigationManager().showInPopup(CollectionPopupEditor.class);
289 292

  
290 293
        collectionPopuEditor.grantToCurrentUser(COLLECTION_EDITOR_CRUD);
......
292 295
        collectionPopuEditor.loadInEditor(collectionId);
293 296
    }
294 297

  
295
    @EventListener
296
    public void onDoneWithPopupEvent(DoneWithPopupEvent event){
297

  
298
        if(event.getPopup() == collectionPopuEditor){
299
            if(event.getReason() == Reason.SAVE){
298
    @EventListener(condition = "#event.entityType == T(eu.etaxonomy.cdm.model.occurrence.Collection)")
299
    public void onCollectionEvent(EntityChangeEvent event){
300 300

  
301
                Collection newCollection = collectionPopuEditor.getBean();
302

  
303
                // TODO the bean contained in the popup editor is not yet updated at this point.
304
                //      so re reload it using the uuid since new beans will not have an Id at this point.
305
                newCollection = getRepo().getCollectionService().find(newCollection.getUuid());
306
                ToOneRelatedEntityCombobox<Collection> combobox = collectionPopuEditorSourceRow.getComponent(ToOneRelatedEntityCombobox.class, 2);
307
                combobox.setValue(newCollection);
308
            }
301
        Collection newCollection = getRepo().getCollectionService().load(event.getEntityId(), Arrays.asList(new String[]{"$.institute"}));
302
        cache.findAndUpdate(newCollection);
309 303

  
310
            collectionPopuEditor = null;
311
            collectionPopuEditorSourceRow = null;
304
        for( CollectionRowItemCollection row : collectionPopuEditorSourceRows) {
305
            ToOneRelatedEntityCombobox<Collection> combobox = row.getComponent(ToOneRelatedEntityCombobox.class, 2);
306
            combobox.reload();
312 307
        }
313 308
    }
314 309

  
src/main/java/eu/etaxonomy/cdm/vaadin/view/reference/ReferencePopupEditorView.java
11 11
import com.vaadin.ui.ListSelect;
12 12

  
13 13
import eu.etaxonomy.cdm.model.reference.Reference;
14
import eu.etaxonomy.cdm.vaadin.component.common.TeamOrPersonField;
14 15
import eu.etaxonomy.vaadin.component.ToOneRelatedEntityCombobox;
15 16
import eu.etaxonomy.vaadin.mvp.ApplicationView;
16 17

  
......
25 26

  
26 27
    public ToOneRelatedEntityCombobox<Reference> getInReferenceCombobox();
27 28

  
29
    public TeamOrPersonField getAuthorshipField();
30

  
28 31

  
29 32
}
src/main/java/eu/etaxonomy/vaadin/component/ToOneRelatedEntityCombobox.java
106 106
    }
107 107

  
108 108
    /**
109
     * reload the selected entity from the persistent storage
110
     */
111
    public void reload() {
112
        getSelect().refresh();
113
        getSelect().discard(); // reload from data source
114

  
115
    }
116

  
117
    /**
109 118
     * {@inheritDoc}
110 119
     */
111 120
    @Override
......
164 173
     */
165 174
    @Override
166 175
    public void setValue(V newFieldValue) throws com.vaadin.data.Property.ReadOnlyException, ConversionException {
167
        lazySelect.setValue(newFieldValue);
168 176
        lazySelect.refresh();
177
        lazySelect.setValue(newFieldValue);
169 178
    }
170 179

  
171 180
    @Override
src/test/java/eu/etaxonomy/cdm/cache/EntityCacheTest.java
9 9
package eu.etaxonomy.cdm.cache;
10 10

  
11 11

  
12
import org.joda.time.DateTime;
12 13
import org.junit.Assert;
13 14
import org.junit.Test;
14 15

  
......
22 23
public class EntityCacheTest {
23 24

  
24 25
    @Test
25
    public void EntityKeyTest() {
26
    public void entityKeyTest() {
26 27

  
27 28
        Collection collection1 = Collection.NewInstance();
28 29
        Collection collection2 = Collection.NewInstance();
......
43 44
        Assert.assertNotNull(cache.find(collection1));
44 45
    }
45 46

  
47
    @Test
48
    public void testFindOrUpdate() {
49

  
50
        Collection collection = Collection.NewInstance();
51
        collection.setId(10);
52
        collection.setCode("A");
53

  
54
        CdmEntityCache cache = new CdmEntityCache(collection);
55

  
56
        Assert.assertEquals(collection, cache.find(Collection.class, 10));
57
        Assert.assertEquals("A", cache.find(Collection.class, 10).getCode());
58

  
59
        Collection copyCollection = collection.clone();
60
        copyCollection.setId(collection.getId()); // clone does not copy the id!
61
        copyCollection.setUuid(collection.getUuid());
62
        copyCollection.setCode("B");
63
        copyCollection.setUpdated(new DateTime(2017, 12, 1, 0, 0));
64
        Assert.assertEquals("B", cache.findAndUpdate(copyCollection).getCode());
65

  
66
        copyCollection = collection.clone();
67
        copyCollection.setId(10); // clone does not copy the id!
68
        copyCollection.setUuid(collection.getUuid());
69
        copyCollection.setCode("C");
70
        copyCollection.setUpdated(new DateTime(2018, 1, 1, 0, 0));
71
        Assert.assertEquals("C", cache.findAndUpdate(copyCollection).getCode());
72

  
73
    }
74

  
46 75
}
src/test/java/eu/etaxonomy/vaadin/mvp/CdmEditorPresenterTest.java
20 20

  
21 21
import eu.etaxonomy.cdm.api.application.CdmRepository;
22 22
import eu.etaxonomy.cdm.model.reference.Reference;
23
import eu.etaxonomy.cdm.vaadin.component.common.TeamOrPersonField;
23 24
import eu.etaxonomy.cdm.vaadin.view.reference.ReferenceEditorPresenter;
24 25
import eu.etaxonomy.cdm.vaadin.view.reference.ReferencePopupEditorView;
25 26
import eu.etaxonomy.vaadin.component.ToOneRelatedEntityCombobox;
......
76 77
            return null;
77 78
        }
78 79

  
80
        /**
81
         * {@inheritDoc}
82
         */
83
        @Override
84
        public TeamOrPersonField getAuthorshipField() {
85
            return null;
86
        }
87

  
79 88

  
80 89

  
81 90
    }

Also available in: Unified diff