Project

General

Profile

Download (20 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.Iterator;
17
import java.util.List;
18
import java.util.Map;
19

    
20
import net.sf.ehcache.Cache;
21
import net.sf.ehcache.CacheManager;
22
import net.sf.ehcache.Element;
23

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

    
30
import eu.etaxonomy.cdm.api.cache.CdmCacher;
31
import eu.etaxonomy.cdm.model.common.CdmBase;
32

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

    
40
    private static final Logger logger = Logger.getLogger(EntityCacherDebugResult.class);
41

    
42
    private Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap;
43

    
44
    private List<CdmEntityInfo> notInCacheList;
45

    
46
    private CdmTransientEntityCacher cacher;
47

    
48
    private List<CdmEntityInfo> rootElements;
49

    
50
    StringBuilder debugOutput = new StringBuilder();
51

    
52
    public EntityCacherDebugResult() {
53
    }
54

    
55

    
56
    public <T extends CdmBase> EntityCacherDebugResult(CdmTransientEntityCacher cacher, Collection<T> rootEntities) {
57
        this.cacher = cacher;
58
        init();
59

    
60
        if(rootEntities != null && !rootEntities.isEmpty()) {
61
            for(CdmBase rootEntity : rootEntities) {
62
                debug(rootEntity, true);
63
                String out = toString(duplicateCdmEntityMap, notInCacheList, rootEntity);
64
                System.out.println(out);
65
                debugOutput.append(out);
66
                clear();
67
            }
68

    
69
        }
70
    }
71

    
72
    private void init() {
73
        duplicateCdmEntityMap = new HashMap<CdmEntityInfo, CdmEntityInfo>();
74
        notInCacheList = new ArrayList<CdmEntityInfo>();
75
        rootElements = new ArrayList<CdmEntityInfo>();
76
    }
77

    
78
    private void clear() {
79
        duplicateCdmEntityMap.clear();
80
        notInCacheList.clear();
81
    }
82

    
83
    public void addDuplicateEntity(CdmEntityInfo cei, CdmEntityInfo cachedCei) {
84
        duplicateCdmEntityMap.put(cei, cachedCei);
85
    }
86

    
87
    public void addEntityNotInCache(CdmEntityInfo cei) {
88
        notInCacheList.add(cei);
89
    }
90

    
91
    public List<CdmEntityInfo> getRootElements() {
92
        return rootElements;
93
    }
94

    
95
    private void print(Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap,
96
            List<CdmEntityInfo> notInCacheList,
97
            CdmBase rootEntity) {
98
        System.out.println(toString(duplicateCdmEntityMap, notInCacheList, rootEntity));
99
    }
100

    
101

    
102
    @Override
103
    public String toString() {
104
        return debugOutput.toString();
105
    }
106

    
107
    private String toString(Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap,
108
            List<CdmEntityInfo> notInCacheList,
109
            CdmBase rootEntity) {
110

    
111

    
112
        StringBuilder sb = new StringBuilder();
113
        sb.append(System.getProperty("line.separator"));
114
        sb.append("<<< Root Entity " + rootEntity.getUserFriendlyTypeName() + " with id " + rootEntity.getId() + " >>>");
115
        sb.append(System.getProperty("line.separator"));
116
        if(duplicateCdmEntityMap.isEmpty()) {
117
            sb.append("No Duplicate CDM Entities.");
118
        } else {
119
            sb.append("Duplicate CDM Entities,");
120

    
121
            for (Map.Entry<CdmEntityInfo, CdmEntityInfo> entry : duplicateCdmEntityMap.entrySet())
122
            {
123
                sb.append(System.getProperty("line.separator"));
124
                CdmEntityInfo cei = entry.getKey();
125
                CdmBase cb = (CdmBase) cei.getObject();
126

    
127
                sb.append(" - " + cei.getField().getName() + ":" + cb.getUserFriendlyTypeName() + "/" + cb.getId());
128
                if(cei.getParent() != null) {
129
                    Object cbParent = cei.getParent().getObject();
130
                    sb.append("     in entity " + cbParent.getClass().getCanonicalName());
131
                    if(cbParent instanceof CdmBase) {
132

    
133
                        sb.append(" with id : " + ((CdmBase)cbParent).getId());
134
                    }
135
                }
136
                sb.append(System.getProperty("line.separator"));
137
                sb.append("  -- entity belongs to cache(s) : " + getCachesContainingEntity(cb));
138
                sb.append(System.getProperty("line.separator"));
139

    
140

    
141
                CdmEntityInfo dupCei = entry.getValue();
142
                CdmBase dupCb = (CdmBase) dupCei.getObject();
143

    
144
                String dupCeiFieldName = "";
145
                if(dupCei.getField() != null) {
146
                    dupCeiFieldName = dupCei.getField().getName();
147
                }
148
                sb.append(" - " + dupCeiFieldName + ":" + dupCb.getUserFriendlyTypeName() + "/" + dupCb.getId());
149
                if(dupCei.getParent() != null) {
150
                    Object dupCbParent = dupCei.getParent().getObject();
151
                    sb.append("      in entity " + dupCbParent.getClass().getCanonicalName());
152
                    if(dupCbParent instanceof CdmBase) {
153
                        sb.append(" with id : " + ((CdmBase)dupCbParent).getId());
154
                    }
155
                }
156
                sb.append(System.getProperty("line.separator"));
157
                sb.append("  -- entity belongs to cache(s) : " + getCachesContainingEntity(dupCb));
158
                sb.append(System.getProperty("line.separator"));
159
                sb.append("-----------");
160
            }
161
        }
162

    
163
        sb.append(System.getProperty("line.separator"));
164
        sb.append(System.getProperty("line.separator"));
165

    
166
        if(notInCacheList.isEmpty()) {
167
            sb.append("No Entities found which are not in Cache.");
168
        } else {
169
            sb.append("Not In Cache Entities,");
170

    
171
            for(CdmEntityInfo cei : notInCacheList) {
172
                CdmBase cb = (CdmBase) cei.getObject();
173
                Object cbParent = cei.getParent().getObject();
174

    
175
                sb.append(System.getProperty("line.separator"));
176

    
177
                String fieldName = "";
178
                if(cei.getField() != null) {
179
                    fieldName = cei.getField().getName();
180
                }
181
                sb.append(" - "  + fieldName + ":" + cb.getUserFriendlyTypeName() + "/" + cb.getId());
182

    
183
                if(cbParent instanceof CdmBase) {
184
                    sb.append(" of entity " + ((CdmBase)cbParent).getUserFriendlyTypeName());
185
                } else {
186
                    sb.append(" of entity " + cbParent.getClass().getName());
187
                }
188
            }
189
        }
190
        sb.append(System.getProperty("line.separator"));
191
        return sb.toString();
192
    }
193

    
194
    private String getCachesContainingEntity(CdmBase cdmEntity) {
195
        Cache defaultCache = CacheManager.create().getCache(CdmCacher.DEFAULT_CACHE_NAME);
196
        String caches = "";
197
        Element dce = defaultCache.get(cdmEntity.getUuid());
198
        if(dce != null && dce.getObjectValue() == cdmEntity) {
199
            caches = "{DC}";
200
        }
201

    
202
        Object cte = cacher.getFromCache(CdmTransientEntityCacher.generateKey(cdmEntity));
203
        if(cte != null && cte == cdmEntity) {
204
            caches += "{TC}";
205
        }
206
        return caches;
207
    }
208

    
209

    
210
    private void debug(CdmBase cdmEntity, boolean recursive) {
211
        if(cdmEntity == null) {
212
            return;
213
        }
214
        logger.info("---- starting recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId());
215
        List<CdmEntityInfo> alreadyVisitedEntities = new ArrayList<CdmEntityInfo>();
216
        CdmEntityInfo cei = new CdmEntityInfo(ProxyUtils.deproxy(cdmEntity));
217
        debugRecursive(cdmEntity, alreadyVisitedEntities, cei);
218
        rootElements.add(cei);
219
        alreadyVisitedEntities.clear();
220
        logger.info("---- ending recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId() + "\n");
221
    }
222

    
223
    private <T extends Object> void debugRecursive(T obj,
224
            List<CdmEntityInfo> alreadyVisitedEntities,
225
            CdmEntityInfo cei) {
226
        if(obj == null) {
227
            return;
228
        }
229
        if(obj instanceof CdmBase) {
230
            debugRecursive((CdmBase)obj, alreadyVisitedEntities, cei);
231
        } else if (obj instanceof Map) {
232
            debug((Map<T,T>)obj, alreadyVisitedEntities, cei);
233
        } else if (obj instanceof Collection) {
234
            debug((Collection<T>)obj, alreadyVisitedEntities, cei);
235
        }
236

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

    
239

    
240
    }
241

    
242
    private <T extends Object> void debug(Map<T,T> map,
243
            List<CdmEntityInfo> alreadyVisitedEntities,
244
            CdmEntityInfo cei) {
245
        if(map == null || map.isEmpty()) {
246
            return;
247
        }
248

    
249
        Iterator<Map.Entry<T,T>> iter = map.entrySet().iterator();
250
        while ( iter.hasNext() ) {
251
            Map.Entry<T,T> e = iter.next();
252
            CdmEntityInfo childCei = new CdmEntityInfo(e);
253
            cei.addChild(childCei);
254

    
255
            CdmEntityInfo keyCei = new CdmEntityInfo(ProxyUtils.deproxy(e.getKey()));
256
            childCei.addChild(keyCei);
257
            CdmEntityInfo valueCei = new CdmEntityInfo(ProxyUtils.deproxy(e.getValue()));
258
            childCei.addChild(valueCei);
259

    
260
            debugRecursive(e.getKey(), alreadyVisitedEntities, keyCei);
261
            debugRecursive(e.getValue(), alreadyVisitedEntities, valueCei);
262
        }
263
    }
264

    
265
    private <T extends Object> void debug(Collection<T> collection,
266
            List<CdmEntityInfo> alreadyVisitedEntities,
267
            CdmEntityInfo cei) {
268
        Iterator<T> collectionItr = collection.iterator();
269

    
270
        while(collectionItr.hasNext()) {
271
            Object obj = collectionItr.next();
272
            boolean alreadyVisited = false;
273
            for (CdmEntityInfo entityInfo: alreadyVisitedEntities) {
274
                if(obj.equals(entityInfo.getObject())){
275
                    alreadyVisited = true;
276
                    break;
277
                }
278
            }
279
            if(!alreadyVisited){
280
                CdmEntityInfo childCei = new CdmEntityInfo(ProxyUtils.deproxy(obj));
281
                cei.addChild(childCei);
282
                debugRecursive(obj, alreadyVisitedEntities, childCei);
283
            }
284

    
285
        }
286

    
287
    }
288

    
289
    private void debugRecursive(CdmBase cdmEntity,
290
            List<CdmEntityInfo> alreadyVisitedEntities,
291
            CdmEntityInfo cei) {
292

    
293
        CdmBase cachedCdmEntityInSubGraph = null;
294

    
295
        if(cei.getObject() instanceof CdmBase) {
296
           CdmBase cb =  (CdmBase)cei.getObject();
297
           cachedCdmEntityInSubGraph = cacher.getFromCache(cb);
298
           if(cachedCdmEntityInSubGraph != cb) {
299
               // found a cdm entity which is not in cache - need to record this
300
               //logger.info("  - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
301
               addEntityNotInCache(cei);
302
           }
303
        }
304

    
305

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

    
309
        // start by getting the fields from the cdm entity
310
        String className = cdmEntity.getClass().getName();
311
        CdmModelFieldPropertyFromClass cmgmfc = cacher.getFromCdmlibModelCache(className);
312
        if(cmgmfc != null) {
313
            alreadyVisitedEntities.add(cei);
314
            List<String> fields = cmgmfc.getFields();
315
            for(String field : fields) {
316
                // retrieve the actual object corresponding to the field.
317
                // this object will be either a CdmBase or a Collection / Map
318
                // with CdmBase as the generic type
319
                CdmEntityInfo childCei = getDebugCdmBaseTypeFieldValue(cdmEntity, field, alreadyVisitedEntities, cei);
320
                if(!childCei.isProxy()) {
321
                    Object object = childCei.getObject();
322
                    if(object != null && object instanceof CdmBase) {
323
                        CdmBase cdmEntityInSubGraph = (CdmBase)object;
324
                        if(!containsIdenticalCdmEntity(alreadyVisitedEntities, cdmEntityInSubGraph)) {
325
                            logger.info("recursive debugging object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId());
326
                            debugRecursive(cdmEntityInSubGraph, alreadyVisitedEntities, childCei);
327
                        } else {
328
                            logger.info("object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId() + " already visited");
329
                        }
330
                    }
331
                }
332
            }
333
        } else {
334
            throw new CdmClientCacheException("CdmEntity with class " + cdmEntity.getClass().getName() + " is not found in the cdmlib model cache. " +
335
                    "The cache may be corrupted or not in sync with the latest model version" );
336
        }
337

    
338
    }
339

    
340

    
341
    private CdmEntityInfo getDebugCdmBaseTypeFieldValue(CdmBase cdmEntity,
342
            String fieldName,
343
            List<CdmEntityInfo> alreadyVisitedEntities,
344
            CdmEntityInfo cei) {
345

    
346
        CdmEntityInfo childCei = null;
347
        Class<?> clazz = cdmEntity.getClass();
348
        try {
349
            // this call will search in the provided class as well as
350
            // the super classes until it finds the field
351
            Field field = ReflectionUtils.findField(clazz, fieldName);
352

    
353
            if(field == null) {
354
                throw new CdmClientCacheException("Field '" + fieldName
355
                        + "' not found when searching in class '" + clazz.getName() + "' and its supercalsses");
356
            }
357
            field.setAccessible(true);
358
            Object o = field.get(cdmEntity);
359
            o = ProxyUtils.deproxy(o);
360
            CdmBase cdmEntityInSubGraph = null;
361

    
362
            childCei = new CdmEntityInfo(o);
363
            cei.addChild(childCei);
364
            childCei.setField(field);
365

    
366
            if(o != null) {
367
                boolean isProxy = ProxyUtils.isProxy(o);
368

    
369
                childCei.setProxy(isProxy);
370
                if(!isProxy) {
371
                    childCei.setObject(o);
372
                    if(CdmBase.class.isAssignableFrom(o.getClass())) {
373
                        logger.info("found initialised cdm entity '" + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
374
                        cdmEntityInSubGraph  = (CdmBase)o;
375

    
376
                        //logger.info("  - found duplicate entity at " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
377
                        CdmEntityInfo dupCei = getDuplicate(alreadyVisitedEntities, cdmEntityInSubGraph);
378
                        if(dupCei != null) {
379
                            addDuplicateEntity(childCei, dupCei);
380
                        }
381

    
382
                    } else if(o instanceof Map) {
383
                        debugRecursive((Map)o, alreadyVisitedEntities, childCei);
384
                    } else if(o instanceof Collection) {
385
                        debugRecursive((Collection)o, alreadyVisitedEntities, childCei);
386
                    }
387

    
388
                }
389
            }
390
            // we return the original cdm entity in the sub graph because we
391
            // want to continue to recurse on the input cdm entity graph
392
            // and not the one in the cache
393

    
394
            return childCei;
395
        } catch (SecurityException e) {
396
            throw new CdmClientCacheException(e);
397
        } catch (IllegalArgumentException e) {
398
            throw new CdmClientCacheException(e);
399
        } catch (IllegalAccessException e) {
400
            throw new CdmClientCacheException(e);
401
        }
402
    }
403

    
404

    
405
    private CdmEntityInfo getDuplicate(List<CdmEntityInfo> alreadyVisitedEntities, Object objectToCompare) {
406
        if(objectToCompare != null ) {
407
            for(CdmEntityInfo cei: alreadyVisitedEntities) {
408
                if(objectToCompare.equals(cei.getObject()) && objectToCompare != cei.getObject()) {
409
                    return cei;
410
                }
411
            }
412
        }
413
        return null;
414
    }
415

    
416
    private boolean containsIdenticalCdmEntity(List<CdmEntityInfo> ceiSet, Object objectToCompare) {
417
        boolean foundIdentical = false;
418
        if(objectToCompare != null) {
419
            for(CdmEntityInfo cei : ceiSet) {
420
                if(cei.getObject() == objectToCompare) {
421
                    foundIdentical = true;
422
                }
423
//                } else if(objectToCompare.equals(cei.getObject())) {
424
//                    return false;
425
//                }
426
            }
427
        }
428
        return foundIdentical;
429
    }
430

    
431
    public class CdmEntityInfo {
432

    
433
        private Object object;
434
        private CdmEntityInfo parent;
435
        private List<CdmEntityInfo> children;
436
        private Field field;
437
        private String label;
438
        private boolean isProxy;
439

    
440
        public CdmEntityInfo(Object object) {
441
            this.object = object;
442
            isProxy = false;
443
            children = new ArrayList<CdmEntityInfo>();
444
        }
445

    
446
        public CdmEntityInfo getParent() {
447
            return parent;
448
        }
449

    
450
        public void setParent(CdmEntityInfo parent) {
451
            this.parent = parent;
452
        }
453

    
454
        public List<CdmEntityInfo> getChildren() {
455
            return children;
456
        }
457

    
458
        public void setChildren(List<CdmEntityInfo> children) {
459
            this.children = children;
460
        }
461

    
462
        public void addChild(CdmEntityInfo cei) {
463
            this.children.add(cei);
464
            cei.setParent(this);
465
        }
466

    
467
        public Field getField() {
468
            return field;
469
        }
470

    
471
        public void setField(Field field) {
472
            this.field = field;
473
        }
474

    
475

    
476
        public String getLabel() {
477
            String label;
478
            String fieldName = "";
479
            if(field != null) {
480
                fieldName = field.getName();
481
            }
482

    
483
            if(object != null) {
484
                String className = object.getClass().getName();
485
                if(object instanceof HibernateProxy) {
486
                    LazyInitializer hli = ((HibernateProxy)object).getHibernateLazyInitializer();
487
                    if(hli.isUninitialized()) {
488
                        className = "HibernateProxy";
489
                    } else {
490
                        className = "InitialisedHibernateProxy";
491
                    }
492
                    label = "[" + className + "] " + fieldName;
493
                } else if(object instanceof PersistentCollection) {
494
                    PersistentCollection pc = ((PersistentCollection)object);
495
                    if(!pc.wasInitialized()) {
496
                        className = "PersistentCollection";
497
                    } else {
498
                        className = "InitialisedPersistentCollection";
499
                    }
500
                    label = "[" + className + "] " + fieldName;
501
                } else if(object instanceof Collection) {
502
                    label = "[" + className + "] " + fieldName + " : " + String.valueOf(((Collection)object).size());
503
                } else if(object instanceof Map) {
504
                    label = "[" + className + "] " + fieldName + " : " + String.valueOf(((Map)object).size());
505
                } else if(object instanceof CdmBase) {
506
                    label = getCachesContainingEntity((CdmBase)object) +  "[" + className + ",id" + ((CdmBase)object).getId() + "] " + fieldName + " : " + object.toString();
507
                } else {
508
                    label = "[" + className + "] " + fieldName + " : " + object.toString();
509
                }
510
            } else {
511
                label = "[NULL] " + fieldName;
512
            }
513
            return label;
514
        }
515

    
516
        public void setLabel(String label) {
517
            this.label = label;
518
        }
519

    
520
        public Object getObject() {
521
            return object;
522
        }
523

    
524
        public void setObject(Object object) {
525
            this.object = object;
526
        }
527

    
528
        public boolean isProxy() {
529
            return isProxy;
530
        }
531

    
532
        public void setProxy(boolean isProxy) {
533
            this.isProxy = isProxy;
534
        }
535

    
536

    
537

    
538
    }
539

    
540
}
(9-9/10)