Project

General

Profile

Download (16.3 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
 * Copyright (C) 2015 EDIT
4
 * European Distributed Institute of Taxonomy
5
 * http://www.e-taxonomy.eu
6
 *
7
 * The contents of this file are subject to the Mozilla Public License Version 1.1
8
 * See LICENSE.TXT at the top of this package for the full license terms.
9
 */
10
package eu.etaxonomy.taxeditor.remoting.cache;
11

    
12
import java.lang.reflect.Field;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.IdentityHashMap;
17
import java.util.Iterator;
18
import java.util.List;
19
import java.util.Map;
20

    
21
import javassist.util.proxy.ProxyFactory;
22

    
23
import org.apache.log4j.Logger;
24
import org.hibernate.collection.spi.PersistentCollection;
25
import org.hibernate.proxy.HibernateProxy;
26
import org.hibernate.proxy.LazyInitializer;
27
import org.springframework.util.ReflectionUtils;
28

    
29
import eu.etaxonomy.cdm.model.common.CdmBase;
30

    
31
/**
32
 * @author cmathew
33
 * @date 9 Feb 2015
34
 *
35
 */
36
public class EntityCacherDebugResult {
37

    
38
    private static final Logger logger = Logger.getLogger(EntityCacherDebugResult.class);
39

    
40
    private Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap;
41

    
42
    private List<CdmEntityInfo> notInCacheList;
43

    
44
    private CdmTransientEntityCacher cacher;
45

    
46
    private List<CdmEntityInfo> rootElements;
47

    
48
    public EntityCacherDebugResult() {
49
    }
50

    
51

    
52
    public <T extends CdmBase> EntityCacherDebugResult(CdmTransientEntityCacher cacher, List<T> rootEntities) {
53
        this.cacher = cacher;
54
        init();
55
        if(rootEntities != null && !rootEntities.isEmpty()) {
56
            for(CdmBase rootEntity : rootEntities) {
57
                debug(rootEntity, true);
58
                print();
59
                clear();
60
            }
61

    
62
        }
63
    }
64

    
65
    private void init() {
66
        duplicateCdmEntityMap = new HashMap<CdmEntityInfo, CdmEntityInfo>();
67
        notInCacheList = new ArrayList<CdmEntityInfo>();
68
        rootElements = new ArrayList<CdmEntityInfo>();
69
    }
70

    
71
    private void clear() {
72
        duplicateCdmEntityMap.clear();
73
        notInCacheList.clear();
74
    }
75

    
76
    public void addDuplicateEntity(CdmEntityInfo cei, CdmEntityInfo cachedCei) {
77
        duplicateCdmEntityMap.put(cei, cachedCei);
78
    }
79

    
80
    public void addEntityNotInCache(CdmEntityInfo cei) {
81
        notInCacheList.add(cei);
82
    }
83

    
84
    public List<CdmEntityInfo> getRootElements() {
85
        return rootElements;
86
    }
87

    
88
    private void print() {
89
        System.out.println(toString());
90
    }
91

    
92

    
93
    @Override
94
    public String toString() {
95
        StringBuilder sb = new StringBuilder();
96
        if(duplicateCdmEntityMap.isEmpty()) {
97
            sb.append("No Duplicate CDM Entities.");
98
        } else {
99
            sb.append("Duplicate CDM Entities,");
100

    
101
            for (Map.Entry<CdmEntityInfo, CdmEntityInfo> entry : duplicateCdmEntityMap.entrySet())
102
            {
103
                sb.append(System.getProperty("line.separator"));
104
                CdmEntityInfo cei = entry.getKey();
105
                CdmBase cb = (CdmBase) cei.getObject();
106
                CdmBase cbParent = (CdmBase) cei.getParent().getObject();
107

    
108
                CdmEntityInfo dupCei = entry.getValue();
109
                CdmBase dupCb = (CdmBase) dupCei.getObject();
110
                CdmBase dupCbParent = (CdmBase) dupCei.getParent().getObject();
111

    
112
                sb.append(" - entity : " + cb.getUserFriendlyTypeName() + "/" + cb.getId() +
113
                        " as field " + cei.getField().getName() +
114
                        " of entity " + cbParent.getUserFriendlyTypeName() + "/" + cbParent.getId());
115
                sb.append(System.getProperty("line.separator"));
116
                sb.append(" - duplicate entity : " + dupCb.getUserFriendlyTypeName() + "/" + dupCb.getId() +
117
                        " as field " + dupCei.getField().getName() +
118
                        " of entity " + dupCbParent.getUserFriendlyTypeName() + "/" + dupCbParent.getId());
119
                sb.append(System.getProperty("line.separator"));
120
                sb.append("-----------");
121
            }
122
        }
123

    
124
        sb.append(System.getProperty("line.separator"));
125
        sb.append(System.getProperty("line.separator"));
126

    
127
        if(notInCacheList.isEmpty()) {
128
            sb.append("No Entities found which are not in Cache.");
129
        } else {
130
            sb.append("Not In Cache Entities,");
131

    
132
            for(CdmEntityInfo cei : notInCacheList) {
133
                CdmBase cb = (CdmBase) cei.getObject();
134
                CdmBase cbParent = (CdmBase) cei.getParent().getObject();
135

    
136
                sb.append(System.getProperty("line.separator"));
137

    
138
                sb.append(" - entity : " + cb.getUserFriendlyTypeName() + "/" + cb.getId() +
139
                        " as field " + cei.getField().getName() +
140
                        " of entity" + cbParent.getUserFriendlyTypeName() + "/" + cbParent.getId());
141
            }
142
        }
143

    
144
        return sb.toString();
145
    }
146

    
147

    
148
    private void debug(CdmBase cdmEntity, boolean recursive) {
149
        if(cdmEntity == null) {
150
            return;
151
        }
152
        logger.info("---- starting recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId());
153
        IdentityHashMap<Object, CdmEntityInfo> alreadyVisitedEntities = new IdentityHashMap<Object, CdmEntityInfo>();
154
        CdmEntityInfo cei = new CdmEntityInfo(cdmEntity);
155
        debugRecursive(cdmEntity, alreadyVisitedEntities, cei);
156
        rootElements.add(cei);
157
        alreadyVisitedEntities.clear();
158
        logger.info("---- ending recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId() + "\n");
159
    }
160

    
161
    private <T extends Object> void debugRecursive(T obj,
162
            IdentityHashMap<Object, CdmEntityInfo> alreadyVisitedEntities,
163
            CdmEntityInfo cei) {
164
        if(obj == null) {
165
            return;
166
        }
167
        if(obj instanceof CdmBase) {
168
            debugRecursive((CdmBase)obj, alreadyVisitedEntities, cei);
169
        } else if (obj instanceof Map) {
170
           debug((Map<T,T>)obj, alreadyVisitedEntities, cei);
171
        } else if (obj instanceof Collection) {
172
            debug((Collection<T>)obj, alreadyVisitedEntities, cei);
173
        }
174

    
175
        logger.info("No caching yet for type " + obj.getClass().getName());
176

    
177

    
178
    }
179

    
180
    private <T extends Object> void debug(Map<T,T> map,
181
            IdentityHashMap<Object, CdmEntityInfo> alreadyVisitedEntities,
182
            CdmEntityInfo cei) {
183
        if(map == null || map.isEmpty()) {
184
            return;
185
        }
186

    
187
        int originalMapSize = map.size();
188

    
189
        Iterator<Map.Entry<T,T>> iter = map.entrySet().iterator();
190
        int i=0;
191
        while ( iter.hasNext() ) {
192
            Map.Entry<T,T> e = iter.next();
193
            CdmEntityInfo childCei = new CdmEntityInfo(e);
194
            cei.addChild(childCei);
195
            debugRecursive(e.getKey(), alreadyVisitedEntities, childCei);
196
            debugRecursive(e.getValue(), alreadyVisitedEntities, childCei);
197
        }
198
    }
199

    
200
    private <T extends Object> void debug(Collection<T> collection,
201
            IdentityHashMap<Object, CdmEntityInfo> alreadyVisitedEntities,
202
            CdmEntityInfo cei) {
203
        int length = collection.size();
204
        Object[] result = new Object[length];
205
        Iterator<T> collectionItr = collection.iterator();
206

    
207
        while(collectionItr.hasNext()) {
208
            Object obj = collectionItr.next();
209
            CdmEntityInfo childCei = new CdmEntityInfo(obj);
210
            cei.addChild(childCei);
211
            debugRecursive(obj, alreadyVisitedEntities, childCei);
212

    
213
        }
214

    
215
    }
216

    
217
    private void debugRecursive(CdmBase cdmEntity,
218
            IdentityHashMap<Object, CdmEntityInfo> alreadyVisitedEntities,
219
            CdmEntityInfo cei) {
220

    
221
        // we want to recursive through the cdmEntity (and not the cachedCdmEntity)
222
        // since there could be new or deleted objects in the cdmEntity sub-graph
223

    
224
        // start by getting the fields from the cdm entity
225
        String className = cdmEntity.getClass().getName();
226
        CdmModelFieldPropertyFromClass cmgmfc = cacher.getFromCdmlibModelCache(className);
227
        if(cmgmfc != null) {
228
            alreadyVisitedEntities.put(cdmEntity, cei);
229
            List<String> fields = cmgmfc.getFields();
230
            for(String field : fields) {
231
                // retrieve the actual object corresponding to the field.
232
                // this object will be either a CdmBase or a Collection / Map
233
                // with CdmBase as the generic type
234

    
235
                CdmEntityInfo childCei = getDebugCdmBaseTypeFieldValue(cdmEntity, field, alreadyVisitedEntities, cei);
236
                CdmBase cdmEntityInSubGraph = (CdmBase)childCei.getObject();
237
                if(cdmEntityInSubGraph != null) {
238
                    if(!alreadyVisitedEntities.keySet().contains(cdmEntityInSubGraph)) {
239
                        logger.info("recursive debugging object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId());
240
                        debugRecursive(cdmEntityInSubGraph, alreadyVisitedEntities, childCei);
241
                    } else {
242
                        logger.info("object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId() + " already visited");
243
                    }
244
                }
245
            }
246
        } else {
247
            throw new CdmClientCacheException("CdmEntity with class " + cdmEntity.getClass().getName() + " is not found in the cdmlib model cache. " +
248
                    "The cache may be corrupted or not in sync with the latest model version" );
249
        }
250

    
251
    }
252

    
253

    
254
    private CdmEntityInfo getDebugCdmBaseTypeFieldValue(CdmBase cdmEntity,
255
            String fieldName,
256
            IdentityHashMap<Object, CdmEntityInfo> alreadyVisitedEntities,
257
            CdmEntityInfo cei) {
258

    
259
        CdmEntityInfo childCei = null;
260
        Class<?> clazz = cdmEntity.getClass();
261
        try {
262
            // this call will search in the provided class as well as
263
            // the super classes until it finds the field
264
            Field field = ReflectionUtils.findField(clazz, fieldName);
265

    
266
            if(field == null) {
267
                throw new CdmClientCacheException("Field '" + fieldName
268
                        + "' not found when searching in class '" + clazz.getName() + "' and its supercalsses");
269
            }
270
            field.setAccessible(true);
271
            Object o = field.get(cdmEntity);
272
            if(o != null) {
273
                if(o instanceof HibernateProxy) {
274
                    LazyInitializer hli = ((HibernateProxy)o).getHibernateLazyInitializer();
275
                    if(!hli.isUninitialized()) {
276
                        o = hli.getImplementation();
277
                    }
278
                }
279

    
280
                if(o instanceof PersistentCollection) {
281
                    PersistentCollection pc = ((PersistentCollection)o);
282
                    if(pc.wasInitialized()) {
283
                        o = ProxyUtils.getObject(pc);
284
                    }
285
                }
286
            }
287
            childCei = new CdmEntityInfo(o);
288
            cei.addChild(childCei);
289
            childCei.setField(field);
290
            CdmBase cdmEntityInSubGraph = null;
291
            if(o != null
292
                    && !ProxyFactory.isProxyClass(o.getClass())
293
                    && !(o instanceof PersistentCollection) ) {
294

    
295
                if(CdmBase.class.isAssignableFrom(o.getClass())) {
296
                    logger.info("found initialised cdm entity '" + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
297
                    cdmEntityInSubGraph  = (CdmBase)o;
298

    
299
                    if(alreadyVisitedEntities.keySet().contains(cdmEntityInSubGraph)) {
300
                        //logger.info("  - found duplicate entity at " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
301
                        CdmEntityInfo dupCei = alreadyVisitedEntities.get(cdmEntityInSubGraph);
302
                        addDuplicateEntity(childCei, dupCei);
303
                    }
304

    
305

    
306
                    CdmBase cachedCdmEntityInSubGraph = cacher.getFromCache(cdmEntityInSubGraph);
307
                    // the only exception to updating the field to the latest value
308
                    // is the case where the field has been already initialised, cached and
309
                    // is not the same as the one in the cache, in which case we set the value
310
                    // of the field to the one found in the cache
311
                    if(cachedCdmEntityInSubGraph == null) {
312
                        // found a cdm entity which is not in cache - need to record this
313
                        //logger.info("  - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
314
                        addEntityNotInCache(childCei);
315
                    }
316
                } else if(o instanceof Map) {
317
                    debugRecursive((Map)o, alreadyVisitedEntities, childCei);
318
                } else if(o instanceof Collection) {
319
                    debugRecursive((Collection)o, alreadyVisitedEntities, childCei);
320
                }
321
            }
322
            // we return the original cdm entity in the sub graph because we
323
            // want to continue to recurse on the input cdm entity graph
324
            // and not the one in the cache
325
            return childCei;
326
        } catch (SecurityException e) {
327
            throw new CdmClientCacheException(e);
328
        } catch (IllegalArgumentException e) {
329
            throw new CdmClientCacheException(e);
330
        } catch (IllegalAccessException e) {
331
            throw new CdmClientCacheException(e);
332
        }
333
    }
334

    
335

    
336

    
337

    
338
    public class CdmEntityInfo {
339

    
340
        private final Object object;
341
        private CdmEntityInfo parent;
342
        private List<CdmEntityInfo> children;
343
        private Field field;
344
        private String label;
345

    
346
        public CdmEntityInfo(Object object) {
347
            this.object = object;
348
            children = new ArrayList<CdmEntityInfo>();
349
        }
350

    
351
        public CdmEntityInfo getParent() {
352
            return parent;
353
        }
354

    
355
        public void setParent(CdmEntityInfo parent) {
356
            this.parent = parent;
357
        }
358

    
359
        public List<CdmEntityInfo> getChildren() {
360
            return children;
361
        }
362

    
363
        public void setChildren(List<CdmEntityInfo> children) {
364
            this.children = children;
365
        }
366

    
367
        public void addChild(CdmEntityInfo cei) {
368
            this.children.add(cei);
369
            cei.setParent(this);
370
        }
371

    
372
        public Field getField() {
373
            return field;
374
        }
375

    
376
        public void setField(Field field) {
377
            this.field = field;
378
        }
379

    
380

    
381
        public String getLabel() {
382
            String label;
383
            String fieldName = "";
384
            if(field != null) {
385
                fieldName = field.getName();
386
            }
387
            if(object != null) {
388
                if(object instanceof HibernateProxy) {
389
                    LazyInitializer hli = ((HibernateProxy)object).getHibernateLazyInitializer();
390
                    if(hli.isUninitialized()) {
391
                        label = "[HibernateProxy] " + fieldName;
392
                    } else {
393
                        label = fieldName + "Object is a HibernateProxy, but initialised";
394
                    }
395
                } else if(object instanceof PersistentCollection) {
396
                    PersistentCollection pc = ((PersistentCollection)object);
397
                    if(!pc.wasInitialized()) {
398
                        label = "[PersistentCollection] " + fieldName;
399
                    } else {
400
                        label = fieldName + "Object is a PersistentCollection, but initialised";
401
                    }
402
                } else if(object instanceof Collection) {
403
                    String className = object.getClass().getName();
404
                    label = "[" + className + "] " + fieldName + String.valueOf(((Collection)object).size());
405
                } else if(object instanceof Map) {
406
                    String className = object.getClass().getName();
407
                    label = "[" + className + "] " + fieldName + String.valueOf(((Map)object).size());
408
                } else {
409
                    String className = object.getClass().getName();
410
                    label = "[" + className + "] " + fieldName;
411
                }
412
            } else {
413
                label = "[NULL] " + fieldName;
414
            }
415
            return label;
416
        }
417

    
418
        public void setLabel(String label) {
419
            this.label = label;
420
        }
421

    
422
        public Object getObject() {
423
            return object;
424
        }
425

    
426

    
427

    
428
    }
429

    
430
}
(8-8/10)