Project

General

Profile

Download (20 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2015 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.taxeditor.remoting.cache;
10

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

    
19
import net.sf.ehcache.Cache;
20
import net.sf.ehcache.CacheManager;
21
import net.sf.ehcache.Element;
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.api.cache.CdmCacher;
30
import eu.etaxonomy.cdm.model.common.CdmBase;
31

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

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

    
41
    private Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap;
42

    
43
    private List<CdmEntityInfo> notInCacheList;
44

    
45
    private CdmTransientEntityCacher cacher;
46

    
47
    private List<CdmEntityInfo> rootElements;
48

    
49
    StringBuilder debugOutput = new StringBuilder();
50

    
51
    public EntityCacherDebugResult() {
52
    }
53

    
54

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

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

    
68
        }
69
    }
70

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

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

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

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

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

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

    
100

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

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

    
110

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

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

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

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

    
139

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

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

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

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

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

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

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

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

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

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

    
208

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

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

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

    
238

    
239
    }
240

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

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

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

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

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

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

    
284
        }
285

    
286
    }
287

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

    
292
        CdmBase cachedCdmEntityInSubGraph = null;
293

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

    
304

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

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

    
337
    }
338

    
339

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

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

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

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

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

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

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

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

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

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

    
403

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

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

    
430
    public class CdmEntityInfo {
431

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

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

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

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

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

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

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

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

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

    
474

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

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

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

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

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

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

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

    
535

    
536

    
537
    }
538

    
539
}
(9-9/10)