Project

General

Profile

Download (19.5 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.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, List<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
                Object cbParent = cei.getParent().getObject();
126

    
127

    
128
                sb.append(" - " + cei.getField().getName() + ":" + cb.getUserFriendlyTypeName() + "/" + cb.getId() +
129
                        " in entity " + cbParent.getClass().getCanonicalName());
130
                if(cbParent instanceof CdmBase) {
131
                    sb.append(" with id : " + ((CdmBase)cbParent).getId());
132
                }
133
                sb.append(System.getProperty("line.separator"));
134
                sb.append("  -- entity belongs to cache(s) : " + getCachesContainingEntity(cb));
135
                sb.append(System.getProperty("line.separator"));
136

    
137

    
138
                CdmEntityInfo dupCei = entry.getValue();
139
                CdmBase dupCb = (CdmBase) dupCei.getObject();
140
                Object dupCbParent = dupCei.getParent().getObject();
141

    
142
                sb.append(" - " + dupCei.getField().getName() + ":" + dupCb.getUserFriendlyTypeName() + "/" + dupCb.getId() +
143
                        " in entity " + dupCbParent.getClass().getCanonicalName());
144
                if(dupCbParent instanceof CdmBase) {
145
                    sb.append(" with id : " + ((CdmBase)dupCbParent).getId());
146
                }
147
                sb.append(System.getProperty("line.separator"));
148
                sb.append("  -- entity belongs to cache(s) : " + getCachesContainingEntity(dupCb));
149
                sb.append(System.getProperty("line.separator"));
150
                sb.append("-----------");
151
            }
152
        }
153

    
154
        sb.append(System.getProperty("line.separator"));
155
        sb.append(System.getProperty("line.separator"));
156

    
157
        if(notInCacheList.isEmpty()) {
158
            sb.append("No Entities found which are not in Cache.");
159
        } else {
160
            sb.append("Not In Cache Entities,");
161

    
162
            for(CdmEntityInfo cei : notInCacheList) {
163
                CdmBase cb = (CdmBase) cei.getObject();
164
                Object cbParent = cei.getParent().getObject();
165

    
166
                sb.append(System.getProperty("line.separator"));
167

    
168
                String fieldName = "";
169
                if(cei.getField() != null) {
170
                    fieldName = cei.getField().getName();
171
                }
172
                sb.append(" - "  + fieldName + ":" + cb.getUserFriendlyTypeName() + "/" + cb.getId());
173

    
174
                if(cbParent instanceof CdmBase) {
175
                    sb.append(" of entity " + ((CdmBase)cbParent).getUserFriendlyTypeName());
176
                } else {
177
                    sb.append(" of entity " + cbParent.getClass().getName());
178
                }
179
            }
180
        }
181
        sb.append(System.getProperty("line.separator"));
182
        return sb.toString();
183
    }
184

    
185
    private String getCachesContainingEntity(CdmBase cdmEntity) {
186
        Cache defaultCache = CdmRemoteCacheManager.getInstance().getDefaultCacheManager().getCache(CdmCacher.DEFAULT_CACHE_NAME);
187
        String caches = "";
188
        Element dce = defaultCache.get(cdmEntity.getUuid());
189
        if(dce != null && dce.getObjectValue() == cdmEntity) {
190
            caches = "{DC}";
191
        }
192

    
193
        Object cte = cacher.getFromCache(CdmTransientEntityCacher.generateKey(cdmEntity));
194
        if(cte != null && cte == cdmEntity) {
195
            caches += "{TC}";
196
        }
197
        return caches;
198
    }
199

    
200

    
201
    private void debug(CdmBase cdmEntity, boolean recursive) {
202
        if(cdmEntity == null) {
203
            return;
204
        }
205
        logger.info("---- starting recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId());
206
        List<CdmEntityInfo> alreadyVisitedEntities = new ArrayList<CdmEntityInfo>();
207
        CdmEntityInfo cei = new CdmEntityInfo(cdmEntity);
208
        debugRecursive(cdmEntity, alreadyVisitedEntities, cei);
209
        rootElements.add(cei);
210
        alreadyVisitedEntities.clear();
211
        logger.info("---- ending recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId() + "\n");
212
    }
213

    
214
    private <T extends Object> void debugRecursive(T obj,
215
            List<CdmEntityInfo> alreadyVisitedEntities,
216
            CdmEntityInfo cei) {
217
        if(obj == null) {
218
            return;
219
        }
220
        if(obj instanceof CdmBase) {
221
            debugRecursive((CdmBase)obj, alreadyVisitedEntities, cei);
222
        } else if (obj instanceof Map) {
223
            debug((Map<T,T>)obj, alreadyVisitedEntities, cei);
224
        } else if (obj instanceof Collection) {
225
            debug((Collection<T>)obj, alreadyVisitedEntities, cei);
226
        }
227

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

    
230

    
231
    }
232

    
233
    private <T extends Object> void debug(Map<T,T> map,
234
            List<CdmEntityInfo> alreadyVisitedEntities,
235
            CdmEntityInfo cei) {
236
        if(map == null || map.isEmpty()) {
237
            return;
238
        }
239

    
240
        int originalMapSize = map.size();
241

    
242
        Iterator<Map.Entry<T,T>> iter = map.entrySet().iterator();
243
        int i=0;
244
        while ( iter.hasNext() ) {
245
            Map.Entry<T,T> e = iter.next();
246
            CdmEntityInfo childCei = new CdmEntityInfo(e);
247
            cei.addChild(childCei);
248
            debugRecursive(e.getKey(), alreadyVisitedEntities, childCei);
249
            debugRecursive(e.getValue(), alreadyVisitedEntities, childCei);
250
        }
251
    }
252

    
253
    private <T extends Object> void debug(Collection<T> collection,
254
            List<CdmEntityInfo> alreadyVisitedEntities,
255
            CdmEntityInfo cei) {
256
        int length = collection.size();
257
        Object[] result = new Object[length];
258
        Iterator<T> collectionItr = collection.iterator();
259

    
260
        while(collectionItr.hasNext()) {
261
            Object obj = collectionItr.next();
262
            CdmEntityInfo childCei = new CdmEntityInfo(obj);
263
            cei.addChild(childCei);
264
            debugRecursive(obj, alreadyVisitedEntities, childCei);
265

    
266
        }
267

    
268
    }
269

    
270
    private void debugRecursive(CdmBase cdmEntity,
271
            List<CdmEntityInfo> alreadyVisitedEntities,
272
            CdmEntityInfo cei) {
273

    
274
        CdmBase cachedCdmEntityInSubGraph = null;
275

    
276
        if(cei.getObject() instanceof CdmBase) {
277
           CdmBase cb =  (CdmBase)cei.getObject();
278
           cachedCdmEntityInSubGraph = cacher.getFromCache(cb);
279
           if(cachedCdmEntityInSubGraph != cb) {
280
               // found a cdm entity which is not in cache - need to record this
281
               //logger.info("  - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
282
               addEntityNotInCache(cei);
283
           }
284
        }
285

    
286

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

    
290
        // start by getting the fields from the cdm entity
291
        String className = cdmEntity.getClass().getName();
292
        CdmModelFieldPropertyFromClass cmgmfc = cacher.getFromCdmlibModelCache(className);
293
        if(cmgmfc != null) {
294
            alreadyVisitedEntities.add(cei);
295
            List<String> fields = cmgmfc.getFields();
296
            for(String field : fields) {
297
                // retrieve the actual object corresponding to the field.
298
                // this object will be either a CdmBase or a Collection / Map
299
                // with CdmBase as the generic type
300
                String f = field;
301
                CdmEntityInfo childCei = getDebugCdmBaseTypeFieldValue(cdmEntity, field, alreadyVisitedEntities, cei);
302
                if(!childCei.isProxy()) {
303
                    Object object = childCei.getObject();
304
                    if(object != null && object instanceof CdmBase) {
305
                        CdmBase cdmEntityInSubGraph = (CdmBase)object;
306
                        if(!containsIdenticalCdmEntity(alreadyVisitedEntities, cdmEntityInSubGraph)) {
307
                            logger.info("recursive debugging object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId());
308
                            debugRecursive(cdmEntityInSubGraph, alreadyVisitedEntities, childCei);
309
                        } else {
310
                            logger.info("object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId() + " already visited");
311
                        }
312
                    }
313
                }
314
            }
315
        } else {
316
            throw new CdmClientCacheException("CdmEntity with class " + cdmEntity.getClass().getName() + " is not found in the cdmlib model cache. " +
317
                    "The cache may be corrupted or not in sync with the latest model version" );
318
        }
319

    
320
    }
321

    
322

    
323
    private CdmEntityInfo getDebugCdmBaseTypeFieldValue(CdmBase cdmEntity,
324
            String fieldName,
325
            List<CdmEntityInfo> alreadyVisitedEntities,
326
            CdmEntityInfo cei) {
327

    
328
        CdmEntityInfo childCei = null;
329
        Class<?> clazz = cdmEntity.getClass();
330
        try {
331
            // this call will search in the provided class as well as
332
            // the super classes until it finds the field
333
            Field field = ReflectionUtils.findField(clazz, fieldName);
334

    
335
            if(field == null) {
336
                throw new CdmClientCacheException("Field '" + fieldName
337
                        + "' not found when searching in class '" + clazz.getName() + "' and its supercalsses");
338
            }
339
            field.setAccessible(true);
340
            Object o = field.get(cdmEntity);
341

    
342
            CdmBase cdmEntityInSubGraph = null;
343

    
344
            boolean isHibernateProxy = false;
345
            boolean isPersistentCollection = false;
346

    
347
            childCei = new CdmEntityInfo(o);
348
            cei.addChild(childCei);
349
            childCei.setField(field);
350

    
351
            if(o != null) {
352

    
353
                if(o instanceof HibernateProxy) {
354
                    isHibernateProxy = true;
355
                }
356

    
357
                if(o instanceof PersistentCollection) {
358
                    isPersistentCollection = true;
359
                }
360
                childCei.setObject(o);
361
                childCei.setProxy(isHibernateProxy || isPersistentCollection);
362
                if(!isHibernateProxy && !isPersistentCollection) {
363

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

    
368
                        //logger.info("  - found duplicate entity at " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
369
                        CdmEntityInfo dupCei = getDuplicate(alreadyVisitedEntities, cdmEntityInSubGraph);
370
                        if(dupCei != null) {
371
                            addDuplicateEntity(childCei, dupCei);
372
                        }
373

    
374
                    } else if(o instanceof Map) {
375
                        debugRecursive((Map)o, alreadyVisitedEntities, childCei);
376
                    } else if(o instanceof Collection) {
377
                        debugRecursive((Collection)o, alreadyVisitedEntities, childCei);
378
                    }
379

    
380
                }
381
            }
382
            // we return the original cdm entity in the sub graph because we
383
            // want to continue to recurse on the input cdm entity graph
384
            // and not the one in the cache
385

    
386
            return childCei;
387
        } catch (SecurityException e) {
388
            throw new CdmClientCacheException(e);
389
        } catch (IllegalArgumentException e) {
390
            throw new CdmClientCacheException(e);
391
        } catch (IllegalAccessException e) {
392
            throw new CdmClientCacheException(e);
393
        }
394
    }
395

    
396

    
397
    private CdmEntityInfo getDuplicate(List<CdmEntityInfo> alreadyVisitedEntities, Object objectToCompare) {
398
        if(objectToCompare != null ) {
399
            for(CdmEntityInfo cei: alreadyVisitedEntities) {
400
                if(objectToCompare.equals(cei.getObject()) && objectToCompare != cei.getObject()) {
401
                    return cei;
402
                }
403
            }
404
        }
405
        return null;
406
    }
407

    
408
    private boolean containsIdenticalCdmEntity(List<CdmEntityInfo> ceiSet, Object objectToCompare) {
409
        boolean foundIdentical = false;
410
        if(objectToCompare != null) {
411
            for(CdmEntityInfo cei : ceiSet) {
412
                if(cei.getObject() == objectToCompare) {
413
                    foundIdentical = true;
414
                } else if(objectToCompare.equals(cei.getObject())) {
415
                    return false;
416
                }
417
            }
418
        }
419
        return foundIdentical;
420
    }
421

    
422
    public class CdmEntityInfo {
423

    
424
        private Object object;
425
        private CdmEntityInfo parent;
426
        private List<CdmEntityInfo> children;
427
        private Field field;
428
        private String label;
429
        private boolean isProxy;
430

    
431
        public CdmEntityInfo(Object object) {
432
            this.object = object;
433
            isProxy = false;
434
            children = new ArrayList<CdmEntityInfo>();
435
        }
436

    
437
        public CdmEntityInfo getParent() {
438
            return parent;
439
        }
440

    
441
        public void setParent(CdmEntityInfo parent) {
442
            this.parent = parent;
443
        }
444

    
445
        public List<CdmEntityInfo> getChildren() {
446
            return children;
447
        }
448

    
449
        public void setChildren(List<CdmEntityInfo> children) {
450
            this.children = children;
451
        }
452

    
453
        public void addChild(CdmEntityInfo cei) {
454
            this.children.add(cei);
455
            cei.setParent(this);
456
        }
457

    
458
        public Field getField() {
459
            return field;
460
        }
461

    
462
        public void setField(Field field) {
463
            this.field = field;
464
        }
465

    
466

    
467
        public String getLabel() {
468
            String label;
469
            String fieldName = "";
470
            if(field != null) {
471
                fieldName = field.getName();
472
            }
473

    
474
            if(object != null) {
475
                String className = object.getClass().getName();
476
                if(object instanceof HibernateProxy) {
477
                    LazyInitializer hli = ((HibernateProxy)object).getHibernateLazyInitializer();
478
                    if(hli.isUninitialized()) {
479
                        className = "HibernateProxy";
480
                    } else {
481
                        className = "InitialisedHibernateProxy";
482
                    }
483
                    label = "[" + className + "] " + fieldName;
484
                } else if(object instanceof PersistentCollection) {
485
                    PersistentCollection pc = ((PersistentCollection)object);
486
                    if(!pc.wasInitialized()) {
487
                        className = "PersistentCollection";
488
                    } else {
489
                        className = "InitialisedPersistentCollection";
490
                    }
491
                    label = "[" + className + "] " + fieldName;
492
                } else if(object instanceof Collection) {
493
                    label = "[" + className + "] " + fieldName + " : " + String.valueOf(((Collection)object).size());
494
                } else if(object instanceof Map) {
495

    
496
                    label = "[" + className + "] " + fieldName + " : " + String.valueOf(((Map)object).size());
497
                } else if(object instanceof CdmBase) {
498
                    label = getCachesContainingEntity((CdmBase)object) +  "[" + className + ",id" + ((CdmBase)object).getId() + "] " + fieldName + " : " + object.toString();
499
                } else {
500
                    label = "[" + className + "] " + fieldName + " : " + object.toString();
501
                }
502
            } else {
503
                label = "[NULL] " + fieldName;
504
            }
505
            return label;
506
        }
507

    
508
        public void setLabel(String label) {
509
            this.label = label;
510
        }
511

    
512
        public Object getObject() {
513
            return object;
514
        }
515

    
516
        public void setObject(Object object) {
517
            this.object = object;
518
        }
519

    
520
        public boolean isProxy() {
521
            return isProxy;
522
        }
523

    
524
        public void setProxy(boolean isProxy) {
525
            this.isProxy = isProxy;
526
        }
527

    
528

    
529

    
530
    }
531

    
532
}
(8-8/9)