Project

General

Profile

Download (9.26 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.Level;
25
import org.apache.log4j.Logger;
26
import org.hibernate.Hibernate;
27
import org.hibernate.collection.internal.AbstractPersistentCollection;
28
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.CollectionProxy;
29
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.MapProxy;
30
import org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.SortedMapProxy;
31

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

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

    
44

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

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

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

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

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

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

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

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

    
61
    public CdmEntityCache(CdmBase entity){
62
        this.entities.add(entity);
63
        update();
64
    }
65

    
66
    @Override
67
    public boolean update() {
68

    
69
        entityPathList.clear();
70
        entityPathsMap.clear();
71
        objectsSeen.clear();
72
        copyEntitiyKeys.clear();
73

    
74
        for(CdmBase entity : entities){
75
        analyzeEntity(entity, "");
76
        }
77

    
78
        return copyEntitiyKeys.isEmpty();
79
    }
80

    
81
    /**
82
     *
83
     */
84
    protected void analyzeEntity(CdmBase bean, String propertyPath) {
85

    
86
        EntityKey entityKey = new EntityKey(bean);
87

    
88
        propertyPath += "[" + entityKey;
89
        String flags = "";
90
        CdmBase mappedEntity = entityyMap.put(entityKey, bean);
91

    
92
        if(mappedEntity != null && mappedEntity != bean) {
93
            copyEntitiyKeys.add(entityKey);
94
            flags += COPY_ENTITY + bean.hashCode();
95
        }
96

    
97
        flags = analyzeMore(bean, entityKey, flags, mappedEntity);
98

    
99
        if(!flags.isEmpty()){
100
            propertyPath += "(" + flags + ")";
101
        }
102
        propertyPath += "]";
103

    
104
        logger.debug(propertyPath);
105

    
106
        entityPathList.add(propertyPath);
107
        if(!entityPathsMap.containsKey(entityKey)){
108
            entityPathsMap.put(entityKey, new ArrayList<>());
109
        }
110
        entityPathsMap.get(entityKey).add(propertyPath);
111

    
112
        if(!objectsSeen.add(bean)){
113
            // avoid cycles, do not recurse into properties of objects that have been analyzed already
114
            return;
115
        }
116

    
117
        Set<PropertyDescriptor> properties = AbstractBeanInitializer.getProperties(bean, null);
118
        for(PropertyDescriptor prop : properties){
119

    
120
            try {
121
                Object propertyValue = PropertyUtils.getProperty(bean, prop.getName());
122

    
123
                if(propertyValue == null){
124
                    continue;
125
                }
126

    
127
                String propertyPathSuffix = "." + prop.getName();
128
                logger.debug("\t\tnext property:" + propertyPathSuffix);
129

    
130
                if(Hibernate.isInitialized(propertyValue)) {
131

    
132
                    if(CdmBase.class.isAssignableFrom(prop.getPropertyType())
133
                            || INomenclaturalReference.class.isAssignableFrom(prop.getPropertyType())
134
                            ){
135
                        analyzeEntity(HibernateProxyHelper.deproxy(propertyValue, CdmBase.class), propertyPath + propertyPathSuffix);
136
                        continue;
137
                    }
138

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

    
155
                    if(collection != null){
156
                        for(CdmBase collectionItem : collection){
157
                            analyzeEntity(HibernateProxyHelper.deproxy(collectionItem, CdmBase.class), propertyPath + propertyPathSuffix);
158
                        }
159
                    } else {
160
                        // logger.error("Unhandled property type " + propertyValue.getClass().getName());
161
                    }
162
                }
163

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

    
177
        }
178
    }
179

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

    
193

    
194
    public void printEntityGraph(PrintStream printStream){
195
        printLegend(printStream);
196
        for(String path : entityPathList) {
197
            printStream.println(path);
198
        }
199
    }
200

    
201
    public void printCopyEntities(PrintStream printStream){
202
        printLegend(printStream);
203
        for(EntityKey key : copyEntitiyKeys){
204
            for(String path : entityPathsMap.get(key)) {
205
                printStream.println(path);
206
            }
207
        }
208
    }
209

    
210
    /**
211
     * @param printStream
212
     */
213
    protected void printLegend(PrintStream printStream) {
214
        printStream.println(this.getClass().getSimpleName() + " legend: ");
215
        printStream.println("    - '!{objectHash}': detected copy entity, followed by object hash");
216
    }
217

    
218
    public class EntityKey {
219

    
220
        Class type;
221
        int id;
222

    
223
        public EntityKey(Class type, int id){
224
            this.type = type;
225
            this.id = id;
226
        }
227

    
228
        public EntityKey(CdmBase entity){
229
            type = entity.getClass();
230
            id = entity.getId();
231
        }
232

    
233
        /**
234
         * @return the type
235
         */
236
        public Class getType() {
237
            return type;
238
        }
239

    
240
        /**
241
         * @return the id
242
         */
243
        public int getId() {
244
            return id;
245
        }
246

    
247
        /**
248
         * {@inheritDoc}
249
         */
250
        @Override
251
        public int hashCode() {
252
            return new HashCodeBuilder(15, 33)
253
                    .append(type)
254
                    .append(id)
255
                    .toHashCode();
256
        }
257

    
258
        @Override
259
        public boolean equals(Object obj) {
260
            EntityKey other = (EntityKey)obj;
261
            return this.id == other.id && this.type == other.type;
262

    
263
        }
264

    
265
        @Override
266
        public String toString() {
267
            return type.getSimpleName() + "#" + getId();
268
        }
269

    
270
    }
271

    
272
    /**
273
     * {@inheritDoc}
274
     */
275
    @Override
276
    public <CDM extends CdmBase> CDM find(CDM value) {
277
        if(value != null){
278
            EntityKey entityKey = new EntityKey(HibernateProxyHelper.deproxy(value));
279
            return (CDM) entityyMap.get(entityKey);
280
        }
281
        return null;
282
    }
283

    
284
    /**
285
     * {@inheritDoc}
286
     */
287
    @Override
288
    public <CDM extends CdmBase> CDM find(Class<CDM> type, int id) {
289
        EntityKey entityKey = new EntityKey(type, id);
290
        return (CDM) entityyMap.get(entityKey);
291
    }
292

    
293
    /**
294
     * {@inheritDoc}
295
     */
296
    @Override
297
    public <CDM extends CdmBase> void add(CDM value) {
298
        entities.add(value);
299
        logger.setLevel(Level.DEBUG);
300
        analyzeEntity(value, "");
301
    }
302

    
303

    
304
}
(1-1/2)