Project

General

Profile

Download (9.42 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.cdm.cache;
10

    
11
import java.beans.PropertyDescriptor;
12
import java.io.PrintStream;
13
import java.lang.reflect.InvocationTargetException;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.List;
19
import java.util.Map;
20
import java.util.Set;
21

    
22
import org.apache.commons.beanutils.PropertyUtils;
23
import org.apache.commons.lang.builder.HashCodeBuilder;
24
import org.apache.log4j.Logger;
25
import org.hibernate.Hibernate;
26
import org.hibernate.collection.internal.AbstractPersistentCollection;
27
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.CollectionProxy;
28
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.MapProxy;
29
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.SortedMapProxy;
30

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

    
36
/**
37
 * @author a.kohlbecker
38
 * @since 08.11.2017
39
 *
40
 */
41
public class CdmEntityCache implements EntityCache {
42

    
43

    
44
    private final static Logger logger = Logger.getLogger(CdmEntityCache.class);
45

    
46
    private static final String COPY_ENTITY = "!";
47

    
48
    private Set<CdmBase> entities = new HashSet<>();
49

    
50
    private Map<EntityKey, CdmBase> entityyMap = new HashMap<>();
51

    
52
    private List<String> entityPathList = new ArrayList<>();
53

    
54
    private Map<EntityKey, List<String>> entityPathsMap = new HashMap<>();
55

    
56
    private Set<EntityKey> copyEntitiyKeys = new HashSet<>();
57

    
58
    private Set<Object> objectsSeen = new HashSet<>();
59

    
60
    /**
61
     * @param entity the first entity to be cached. can be <code>null</code>.
62
     *      Further entities can be added to the cache with {@link CdmEntityCache#add(CdmBase)}
63
     */
64
    public CdmEntityCache(CdmBase entity){
65
        if(entity != null){
66
            this.entities.add(entity);
67
            update();
68
        }
69
    }
70

    
71
    @Override
72
    public boolean update() {
73

    
74
        entityPathList.clear();
75
        entityPathsMap.clear();
76
        objectsSeen.clear();
77
        copyEntitiyKeys.clear();
78

    
79
        for(CdmBase entity : entities){
80
        analyzeEntity(entity, "");
81
        }
82

    
83
        return copyEntitiyKeys.isEmpty();
84
    }
85

    
86
    /**
87
     *
88
     */
89
    protected void analyzeEntity(CdmBase bean, String propertyPath) {
90

    
91
        EntityKey entityKey = new EntityKey(bean);
92

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

    
97
        if(mappedEntity != null && mappedEntity != bean) {
98
            copyEntitiyKeys.add(entityKey);
99
            flags += COPY_ENTITY + bean.hashCode();
100
        }
101

    
102
        flags = analyzeMore(bean, entityKey, flags, mappedEntity);
103

    
104
        if(!flags.isEmpty()){
105
            propertyPath += "(" + flags + ")";
106
        }
107
        propertyPath += "]";
108

    
109
        logger.debug(propertyPath);
110

    
111
        entityPathList.add(propertyPath);
112
        if(!entityPathsMap.containsKey(entityKey)){
113
            entityPathsMap.put(entityKey, new ArrayList<>());
114
        }
115
        entityPathsMap.get(entityKey).add(propertyPath);
116

    
117
        if(!objectsSeen.add(bean)){
118
            // avoid cycles, do not recurse into properties of objects that have been analyzed already
119
            return;
120
        }
121

    
122
        Set<PropertyDescriptor> properties = AbstractBeanInitializer.getProperties(bean, null);
123
        for(PropertyDescriptor prop : properties){
124

    
125
            try {
126
                Object propertyValue = PropertyUtils.getProperty(bean, prop.getName());
127

    
128
                if(propertyValue == null){
129
                    continue;
130
                }
131

    
132
                String propertyPathSuffix = "." + prop.getName();
133
                logger.debug("\t\tproperty:" + propertyPathSuffix);
134

    
135
                if(Hibernate.isInitialized(propertyValue)) {
136

    
137
                    if(CdmBase.class.isAssignableFrom(prop.getPropertyType())
138
                            || INomenclaturalReference.class.isAssignableFrom(prop.getPropertyType())
139
                            ){
140
                        analyzeEntity(HibernateProxyHelper.deproxy(propertyValue, CdmBase.class), propertyPath + propertyPathSuffix);
141
                        continue;
142
                    }
143

    
144
                    Collection<CdmBase> collection = null;
145
                    if(propertyValue instanceof AbstractPersistentCollection){
146
                        if (propertyValue  instanceof Collection) {
147
                            collection = (Collection<CdmBase>) propertyValue;
148
                        } else if (propertyValue instanceof Map) {
149
                            collection = ((Map<?,CdmBase>)propertyValue).values();
150
                        } else {
151
                            logger.error("unhandled subtype of AbstractPersistentCollection");
152
                        }
153
                    } else if (propertyValue instanceof CollectionProxy
154
                                || propertyValue instanceof MapProxy<?, ?>
155
                                || propertyValue instanceof SortedMapProxy<?, ?>){
156
                            //hibernate envers collections
157
                            collection = (Collection<CdmBase>)propertyValue;
158
                    }
159

    
160
                    if(collection != null){
161
                        for(CdmBase collectionItem : collection){
162
                            analyzeEntity(HibernateProxyHelper.deproxy(collectionItem, CdmBase.class), propertyPath + propertyPathSuffix);
163
                        }
164
                    } else {
165
                        // logger.error("Unhandled property type " + propertyValue.getClass().getName());
166
                    }
167
                }
168

    
169
            } catch (IllegalAccessException e) {
170
                String message = "Illegal access on property " + prop;
171
                logger.error(message);
172
                throw new RuntimeException(message, e);
173
            } catch (InvocationTargetException e) {
174
                String message = "Cannot invoke property " + prop + " not found";
175
                logger.error(message);
176
                throw new RuntimeException(message, e);
177
            } catch (NoSuchMethodException e) {
178
                String message = "Property " + prop.getName() + " not found for class " + bean.getClass();
179
                logger.error(message);
180
            }
181

    
182
        }
183
    }
184

    
185
    /**
186
     * Empty method which can be implemented by subclasses which do further analysis.
187
     *
188
     * @param bean
189
     * @param entityKey
190
     * @param flags
191
     * @param mappedEntity
192
     * @return
193
     */
194
    protected String analyzeMore(CdmBase bean, EntityKey entityKey, String flags, CdmBase mappedEntity) {
195
        return flags;
196
    }
197

    
198

    
199
    public void printEntityGraph(PrintStream printStream){
200
        printLegend(printStream);
201
        for(String path : entityPathList) {
202
            printStream.println(path);
203
        }
204
    }
205

    
206
    public void printCopyEntities(PrintStream printStream){
207
        printLegend(printStream);
208
        for(EntityKey key : copyEntitiyKeys){
209
            for(String path : entityPathsMap.get(key)) {
210
                printStream.println(path);
211
            }
212
        }
213
    }
214

    
215
    /**
216
     * @param printStream
217
     */
218
    protected void printLegend(PrintStream printStream) {
219
        printStream.println(this.getClass().getSimpleName() + " legend: ");
220
        printStream.println("    - '!{objectHash}': detected copy entity, followed by object hash");
221
    }
222

    
223
    public class EntityKey {
224

    
225
        Class type;
226
        int id;
227

    
228
        public EntityKey(Class type, int id){
229
            this.type = type;
230
            this.id = id;
231
        }
232

    
233
        public EntityKey(CdmBase entity){
234
            type = entity.getClass();
235
            id = entity.getId();
236
        }
237

    
238
        /**
239
         * @return the type
240
         */
241
        public Class getType() {
242
            return type;
243
        }
244

    
245
        /**
246
         * @return the id
247
         */
248
        public int getId() {
249
            return id;
250
        }
251

    
252
        /**
253
         * {@inheritDoc}
254
         */
255
        @Override
256
        public int hashCode() {
257
            return new HashCodeBuilder(15, 33)
258
                    .append(type)
259
                    .append(id)
260
                    .toHashCode();
261
        }
262

    
263
        @Override
264
        public boolean equals(Object obj) {
265
            EntityKey other = (EntityKey)obj;
266
            return this.id == other.id && this.type == other.type;
267

    
268
        }
269

    
270
        @Override
271
        public String toString() {
272
            return type.getSimpleName() + "#" + getId();
273
        }
274

    
275
    }
276

    
277
    /**
278
     * {@inheritDoc}
279
     */
280
    @Override
281
    public <CDM extends CdmBase> CDM find(CDM value) {
282
        if(value != null){
283
            EntityKey entityKey = new EntityKey(HibernateProxyHelper.deproxy(value));
284
            return (CDM) entityyMap.get(entityKey);
285
        }
286
        return null;
287
    }
288

    
289
    /**
290
     * {@inheritDoc}
291
     */
292
    @Override
293
    public <CDM extends CdmBase> CDM find(Class<CDM> type, int id) {
294
        EntityKey entityKey = new EntityKey(type, id);
295
        return (CDM) entityyMap.get(entityKey);
296
    }
297

    
298
    /**
299
     * {@inheritDoc}
300
     */
301
    @Override
302
    public <CDM extends CdmBase> void add(CDM value) {
303
        entities.add(value);
304
        analyzeEntity(value, "");
305
    }
306

    
307

    
308
}
(1-1/2)