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

    
22
import net.sf.ehcache.Cache;
23
import net.sf.ehcache.Element;
24

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

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

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

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

    
43
    private Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap;
44

    
45
    private List<CdmEntityInfo> notInCacheList;
46

    
47
    private CdmTransientEntityCacher cacher;
48

    
49
    private List<CdmEntityInfo> rootElements;
50

    
51
    StringBuilder debugOutput = new StringBuilder();
52

    
53
    public EntityCacherDebugResult() {
54
    }
55

    
56

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

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

    
70
        }
71
    }
72

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

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

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

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

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

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

    
102

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

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

    
112

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

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

    
129

    
130
                sb.append(" - " + cei.getField().getName() + ":" + cb.getUserFriendlyTypeName() + "/" + cb.getId() +
131
                        " in entity " + cbParent.getClass().getCanonicalName());
132
                if(cbParent instanceof CdmBase) {
133
                    sb.append(" with id : " + ((CdmBase)cbParent).getId());
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
                Object dupCbParent = dupCei.getParent().getObject();
143

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

    
156
        sb.append(System.getProperty("line.separator"));
157
        sb.append(System.getProperty("line.separator"));
158

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

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

    
168
                sb.append(System.getProperty("line.separator"));
169

    
170
                sb.append(" - "  + cei.getField().getName() + ":" + cb.getUserFriendlyTypeName() + "/" + cb.getId());
171

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

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

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

    
198

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

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

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

    
228

    
229
    }
230

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

    
238
        int originalMapSize = map.size();
239

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

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

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

    
264
        }
265

    
266
    }
267

    
268
    private void debugRecursive(CdmBase cdmEntity,
269
            Set<CdmEntityInfo> alreadyVisitedEntities,
270
            CdmEntityInfo cei) {
271

    
272

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

    
276
        // start by getting the fields from the cdm entity
277
        String className = cdmEntity.getClass().getName();
278
        CdmModelFieldPropertyFromClass cmgmfc = cacher.getFromCdmlibModelCache(className);
279
        if(cmgmfc != null) {
280
            alreadyVisitedEntities.add(cei);
281
            List<String> fields = cmgmfc.getFields();
282
            for(String field : fields) {
283
                // retrieve the actual object corresponding to the field.
284
                // this object will be either a CdmBase or a Collection / Map
285
                // with CdmBase as the generic type
286

    
287
                CdmEntityInfo childCei = getDebugCdmBaseTypeFieldValue(cdmEntity, field, alreadyVisitedEntities, cei);
288
                if(!childCei.isProxy()) {
289
                    Object object = childCei.getObject();
290
                    if(object != null && object instanceof CdmBase) {
291
                        CdmBase cdmEntityInSubGraph = (CdmBase)object;
292
                        if(!containsIdenticalCdmEntity(alreadyVisitedEntities, cdmEntityInSubGraph)) {
293
                            logger.info("recursive debugging object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId());
294
                            debugRecursive(cdmEntityInSubGraph, alreadyVisitedEntities, childCei);
295
                        } else {
296
                            logger.info("object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId() + " already visited");
297
                        }
298
                    }
299
                }
300
            }
301
        } else {
302
            throw new CdmClientCacheException("CdmEntity with class " + cdmEntity.getClass().getName() + " is not found in the cdmlib model cache. " +
303
                    "The cache may be corrupted or not in sync with the latest model version" );
304
        }
305

    
306
    }
307

    
308

    
309
    private CdmEntityInfo getDebugCdmBaseTypeFieldValue(CdmBase cdmEntity,
310
            String fieldName,
311
            Set<CdmEntityInfo> alreadyVisitedEntities,
312
            CdmEntityInfo cei) {
313

    
314
        CdmEntityInfo childCei = null;
315
        Class<?> clazz = cdmEntity.getClass();
316
        try {
317
            // this call will search in the provided class as well as
318
            // the super classes until it finds the field
319
            Field field = ReflectionUtils.findField(clazz, fieldName);
320

    
321
            if(field == null) {
322
                throw new CdmClientCacheException("Field '" + fieldName
323
                        + "' not found when searching in class '" + clazz.getName() + "' and its supercalsses");
324
            }
325
            field.setAccessible(true);
326
            Object o = field.get(cdmEntity);
327

    
328
            CdmBase cdmEntityInSubGraph = null;
329

    
330
            boolean isHibernateProxy = false;
331
            boolean isPersistentCollection = false;
332

    
333
            childCei = new CdmEntityInfo(o);
334
            cei.addChild(childCei);
335
            childCei.setField(field);
336

    
337
            if(o != null) {
338

    
339
                if(o instanceof HibernateProxy) {
340
                    LazyInitializer hli = ((HibernateProxy)o).getHibernateLazyInitializer();
341
                    if(!hli.isUninitialized()) {
342
                        o = hli.getImplementation();
343
                    } else {
344
                        isHibernateProxy = true;
345
                    }
346
                }
347

    
348
                if(o instanceof PersistentCollection) {
349
                    PersistentCollection pc = ((PersistentCollection)o);
350
                    if(pc.wasInitialized()) {
351
                        o = ProxyUtils.getObject(pc);
352
                    } else {
353
                        isPersistentCollection = true;
354
                    }
355
                }
356
                childCei.setObject(o);
357
                childCei.setProxy(isHibernateProxy || isPersistentCollection);
358
                if(!isHibernateProxy && !isPersistentCollection) {
359

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

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

    
370
                        CdmBase cachedCdmEntityInSubGraph = cacher.getFromCache(cdmEntityInSubGraph);
371
                        // the only exception to updating the field to the latest value
372
                        // is the case where the field has been already initialised, cached and
373
                        // is not the same as the one in the cache, in which case we set the value
374
                        // of the field to the one found in the cache
375
                        if(cachedCdmEntityInSubGraph == null) {
376
                            // found a cdm entity which is not in cache - need to record this
377
                            //logger.info("  - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
378
                            addEntityNotInCache(childCei);
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(Set<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(Set<CdmEntityInfo> ceiSet, Object objectToCompare) {
416
        if(objectToCompare != null) {
417
            for(CdmEntityInfo cei : ceiSet) {
418
                if(cei.getObject() == objectToCompare) {
419
                    return true;
420
                }
421
            }
422
        }
423
        return false;
424
    }
425

    
426
    public class CdmEntityInfo {
427

    
428
        private Object object;
429
        private CdmEntityInfo parent;
430
        private List<CdmEntityInfo> children;
431
        private Field field;
432
        private String label;
433
        private boolean isProxy;
434

    
435
        public CdmEntityInfo(Object object) {
436
            this.object = object;
437
            isProxy = false;
438
            children = new ArrayList<CdmEntityInfo>();
439
        }
440

    
441
        public CdmEntityInfo getParent() {
442
            return parent;
443
        }
444

    
445
        public void setParent(CdmEntityInfo parent) {
446
            this.parent = parent;
447
        }
448

    
449
        public List<CdmEntityInfo> getChildren() {
450
            return children;
451
        }
452

    
453
        public void setChildren(List<CdmEntityInfo> children) {
454
            this.children = children;
455
        }
456

    
457
        public void addChild(CdmEntityInfo cei) {
458
            this.children.add(cei);
459
            cei.setParent(this);
460
        }
461

    
462
        public Field getField() {
463
            return field;
464
        }
465

    
466
        public void setField(Field field) {
467
            this.field = field;
468
        }
469

    
470

    
471
        public String getLabel() {
472
            String label;
473
            String fieldName = "";
474
            if(field != null) {
475
                fieldName = field.getName();
476
            }
477

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

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

    
512
        public void setLabel(String label) {
513
            this.label = label;
514
        }
515

    
516
        public Object getObject() {
517
            return object;
518
        }
519

    
520
        public void setObject(Object object) {
521
            this.object = object;
522
        }
523

    
524
        public boolean isProxy() {
525
            return isProxy;
526
        }
527

    
528
        public void setProxy(boolean isProxy) {
529
            this.isProxy = isProxy;
530
        }
531

    
532

    
533

    
534
    }
535

    
536
}
(8-8/9)