OpenInspectSessionsHandler : handler for opening the inspect session dialog
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / eu / etaxonomy / taxeditor / remoting / cache / EntityCacherDebugResult.java
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 org.apache.log4j.Logger;
23 import org.hibernate.collection.spi.PersistentCollection;
24 import org.hibernate.proxy.HibernateProxy;
25 import org.hibernate.proxy.LazyInitializer;
26 import org.springframework.util.ReflectionUtils;
27
28 import eu.etaxonomy.cdm.model.common.CdmBase;
29
30 /**
31 * @author cmathew
32 * @date 9 Feb 2015
33 *
34 */
35 public class EntityCacherDebugResult {
36
37 private static final Logger logger = Logger.getLogger(EntityCacherDebugResult.class);
38
39 private Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap;
40
41 private List<CdmEntityInfo> notInCacheList;
42
43 private CdmTransientEntityCacher cacher;
44
45 private List<CdmEntityInfo> rootElements;
46
47 StringBuilder debugOutput = new StringBuilder();
48
49 public EntityCacherDebugResult() {
50 }
51
52
53 public <T extends CdmBase> EntityCacherDebugResult(CdmTransientEntityCacher cacher, List<T> rootEntities) {
54 this.cacher = cacher;
55 init();
56
57 if(rootEntities != null && !rootEntities.isEmpty()) {
58 for(CdmBase rootEntity : rootEntities) {
59 debug(rootEntity, true);
60 String out = toString(duplicateCdmEntityMap, notInCacheList, rootEntity);
61 System.out.println(out);
62 debugOutput.append(out);
63 clear();
64 }
65
66 }
67 }
68
69 private void init() {
70 duplicateCdmEntityMap = new HashMap<CdmEntityInfo, CdmEntityInfo>();
71 notInCacheList = new ArrayList<CdmEntityInfo>();
72 rootElements = new ArrayList<CdmEntityInfo>();
73 }
74
75 private void clear() {
76 duplicateCdmEntityMap.clear();
77 notInCacheList.clear();
78 }
79
80 public void addDuplicateEntity(CdmEntityInfo cei, CdmEntityInfo cachedCei) {
81 duplicateCdmEntityMap.put(cei, cachedCei);
82 }
83
84 public void addEntityNotInCache(CdmEntityInfo cei) {
85 notInCacheList.add(cei);
86 }
87
88 public List<CdmEntityInfo> getRootElements() {
89 return rootElements;
90 }
91
92 private void print(Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap,
93 List<CdmEntityInfo> notInCacheList,
94 CdmBase rootEntity) {
95 System.out.println(toString(duplicateCdmEntityMap, notInCacheList, rootEntity));
96 }
97
98
99 @Override
100 public String toString() {
101 return debugOutput.toString();
102 }
103
104 private String toString(Map<CdmEntityInfo, CdmEntityInfo> duplicateCdmEntityMap,
105 List<CdmEntityInfo> notInCacheList,
106 CdmBase rootEntity) {
107
108
109 StringBuilder sb = new StringBuilder();
110 sb.append(System.getProperty("line.separator"));
111 sb.append("<<< Root Entity " + rootEntity.getUserFriendlyTypeName() + " with id " + rootEntity.getId() + " >>>");
112 sb.append(System.getProperty("line.separator"));
113 if(duplicateCdmEntityMap.isEmpty()) {
114 sb.append("No Duplicate CDM Entities.");
115 } else {
116 sb.append("Duplicate CDM Entities,");
117
118 for (Map.Entry<CdmEntityInfo, CdmEntityInfo> entry : duplicateCdmEntityMap.entrySet())
119 {
120 sb.append(System.getProperty("line.separator"));
121 CdmEntityInfo cei = entry.getKey();
122 CdmBase cb = (CdmBase) cei.getObject();
123 Object cbParent = cei.getParent().getObject();
124
125 CdmEntityInfo dupCei = entry.getValue();
126 CdmBase dupCb = (CdmBase) dupCei.getObject();
127 Object dupCbParent = dupCei.getParent().getObject();
128
129 sb.append(" - entity : " + cb.getUserFriendlyTypeName() + "/" + cb.getId() +
130 " as member " + cei.getField().getName() +
131 " of entity " + cbParent.getClass().getCanonicalName());
132 sb.append(System.getProperty("line.separator"));
133 sb.append(" - duplicate entity : " + dupCb.getUserFriendlyTypeName() + "/" + dupCb.getId() +
134 " as field " + dupCei.getField().getName() +
135 " of entity " + dupCbParent.getClass().getCanonicalName());
136 sb.append(System.getProperty("line.separator"));
137 sb.append("-----------");
138 }
139 }
140
141 sb.append(System.getProperty("line.separator"));
142 sb.append(System.getProperty("line.separator"));
143
144 if(notInCacheList.isEmpty()) {
145 sb.append("No Entities found which are not in Cache.");
146 } else {
147 sb.append("Not In Cache Entities,");
148
149 for(CdmEntityInfo cei : notInCacheList) {
150 CdmBase cb = (CdmBase) cei.getObject();
151 Object cbParent = cei.getParent().getObject();
152
153 sb.append(System.getProperty("line.separator"));
154
155 sb.append(" - entity : " + cb.getUserFriendlyTypeName() + "/" + cb.getId() +
156 " as field " + cei.getField().getName() +
157 " of entity" + cbParent.getClass().getCanonicalName());
158 }
159 }
160 sb.append(System.getProperty("line.separator"));
161 return sb.toString();
162 }
163
164
165 private void debug(CdmBase cdmEntity, boolean recursive) {
166 if(cdmEntity == null) {
167 return;
168 }
169 logger.info("---- starting recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId());
170 Set<CdmEntityInfo> alreadyVisitedEntities = new HashSet<CdmEntityInfo>();
171 CdmEntityInfo cei = new CdmEntityInfo(cdmEntity);
172 debugRecursive(cdmEntity, alreadyVisitedEntities, cei);
173 rootElements.add(cei);
174 alreadyVisitedEntities.clear();
175 logger.info("---- ending recursive debug for cdm entity " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId() + "\n");
176 }
177
178 private <T extends Object> void debugRecursive(T obj,
179 Set<CdmEntityInfo> alreadyVisitedEntities,
180 CdmEntityInfo cei) {
181 if(obj == null) {
182 return;
183 }
184 if(obj instanceof CdmBase) {
185 debugRecursive((CdmBase)obj, alreadyVisitedEntities, cei);
186 } else if (obj instanceof Map) {
187 debug((Map<T,T>)obj, alreadyVisitedEntities, cei);
188 } else if (obj instanceof Collection) {
189 debug((Collection<T>)obj, alreadyVisitedEntities, cei);
190 }
191
192 logger.info("No caching yet for type " + obj.getClass().getName());
193
194
195 }
196
197 private <T extends Object> void debug(Map<T,T> map,
198 Set<CdmEntityInfo> alreadyVisitedEntities,
199 CdmEntityInfo cei) {
200 if(map == null || map.isEmpty()) {
201 return;
202 }
203
204 int originalMapSize = map.size();
205
206 Iterator<Map.Entry<T,T>> iter = map.entrySet().iterator();
207 int i=0;
208 while ( iter.hasNext() ) {
209 Map.Entry<T,T> e = iter.next();
210 CdmEntityInfo childCei = new CdmEntityInfo(e);
211 cei.addChild(childCei);
212 debugRecursive(e.getKey(), alreadyVisitedEntities, childCei);
213 debugRecursive(e.getValue(), alreadyVisitedEntities, childCei);
214 }
215 }
216
217 private <T extends Object> void debug(Collection<T> collection,
218 Set<CdmEntityInfo> alreadyVisitedEntities,
219 CdmEntityInfo cei) {
220 int length = collection.size();
221 Object[] result = new Object[length];
222 Iterator<T> collectionItr = collection.iterator();
223
224 while(collectionItr.hasNext()) {
225 Object obj = collectionItr.next();
226 CdmEntityInfo childCei = new CdmEntityInfo(obj);
227 cei.addChild(childCei);
228 debugRecursive(obj, alreadyVisitedEntities, childCei);
229
230 }
231
232 }
233
234 private void debugRecursive(CdmBase cdmEntity,
235 Set<CdmEntityInfo> alreadyVisitedEntities,
236 CdmEntityInfo cei) {
237
238
239 // we want to recursive through the cdmEntity (and not the cachedCdmEntity)
240 // since there could be new or deleted objects in the cdmEntity sub-graph
241
242 // start by getting the fields from the cdm entity
243 String className = cdmEntity.getClass().getName();
244 CdmModelFieldPropertyFromClass cmgmfc = cacher.getFromCdmlibModelCache(className);
245 if(cmgmfc != null) {
246 alreadyVisitedEntities.add(cei);
247 List<String> fields = cmgmfc.getFields();
248 for(String field : fields) {
249 // retrieve the actual object corresponding to the field.
250 // this object will be either a CdmBase or a Collection / Map
251 // with CdmBase as the generic type
252
253 CdmEntityInfo childCei = getDebugCdmBaseTypeFieldValue(cdmEntity, field, alreadyVisitedEntities, cei);
254 if(!childCei.isProxy()) {
255 Object object = childCei.getObject();
256 if(object != null && object instanceof CdmBase) {
257 CdmBase cdmEntityInSubGraph = (CdmBase)object;
258 if(!containsIdenticalCdmEntity(alreadyVisitedEntities, cdmEntityInSubGraph)) {
259 logger.info("recursive debugging object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId());
260 debugRecursive(cdmEntityInSubGraph, alreadyVisitedEntities, childCei);
261 } else {
262 logger.info("object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId() + " already visited");
263 }
264 }
265 }
266 }
267 } else {
268 throw new CdmClientCacheException("CdmEntity with class " + cdmEntity.getClass().getName() + " is not found in the cdmlib model cache. " +
269 "The cache may be corrupted or not in sync with the latest model version" );
270 }
271
272 }
273
274
275 private CdmEntityInfo getDebugCdmBaseTypeFieldValue(CdmBase cdmEntity,
276 String fieldName,
277 Set<CdmEntityInfo> alreadyVisitedEntities,
278 CdmEntityInfo cei) {
279
280 CdmEntityInfo childCei = null;
281 Class<?> clazz = cdmEntity.getClass();
282 try {
283 // this call will search in the provided class as well as
284 // the super classes until it finds the field
285 Field field = ReflectionUtils.findField(clazz, fieldName);
286
287 if(field == null) {
288 throw new CdmClientCacheException("Field '" + fieldName
289 + "' not found when searching in class '" + clazz.getName() + "' and its supercalsses");
290 }
291 field.setAccessible(true);
292 Object o = field.get(cdmEntity);
293
294 CdmBase cdmEntityInSubGraph = null;
295
296 boolean isHibernateProxy = false;
297 boolean isPersistentCollection = false;
298
299 childCei = new CdmEntityInfo(o);
300 cei.addChild(childCei);
301 childCei.setField(field);
302
303 if(o != null) {
304
305 if(o instanceof HibernateProxy) {
306 LazyInitializer hli = ((HibernateProxy)o).getHibernateLazyInitializer();
307 if(!hli.isUninitialized()) {
308 o = hli.getImplementation();
309 } else {
310 isHibernateProxy = true;
311 }
312 }
313
314 if(o instanceof PersistentCollection) {
315 PersistentCollection pc = ((PersistentCollection)o);
316 if(pc.wasInitialized()) {
317 o = ProxyUtils.getObject(pc);
318 } else {
319 isPersistentCollection = true;
320 }
321 }
322 childCei.setObject(o);
323 childCei.setProxy(isHibernateProxy || isPersistentCollection);
324 if(!isHibernateProxy && !isPersistentCollection) {
325
326 if(CdmBase.class.isAssignableFrom(o.getClass())) {
327 logger.info("found initialised cdm entity '" + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
328 cdmEntityInSubGraph = (CdmBase)o;
329
330 //logger.info(" - found duplicate entity at " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
331 CdmEntityInfo dupCei = getDuplicate(alreadyVisitedEntities, cdmEntityInSubGraph);
332 if(dupCei != null) {
333 addDuplicateEntity(childCei, dupCei);
334 }
335
336 CdmBase cachedCdmEntityInSubGraph = cacher.getFromCache(cdmEntityInSubGraph);
337 // the only exception to updating the field to the latest value
338 // is the case where the field has been already initialised, cached and
339 // is not the same as the one in the cache, in which case we set the value
340 // of the field to the one found in the cache
341 if(cachedCdmEntityInSubGraph == null) {
342 // found a cdm entity which is not in cache - need to record this
343 //logger.info(" - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
344 addEntityNotInCache(childCei);
345 }
346
347 } else if(o instanceof Map) {
348 debugRecursive((Map)o, alreadyVisitedEntities, childCei);
349 } else if(o instanceof Collection) {
350 debugRecursive((Collection)o, alreadyVisitedEntities, childCei);
351 }
352
353 }
354 }
355 // we return the original cdm entity in the sub graph because we
356 // want to continue to recurse on the input cdm entity graph
357 // and not the one in the cache
358
359 return childCei;
360 } catch (SecurityException e) {
361 throw new CdmClientCacheException(e);
362 } catch (IllegalArgumentException e) {
363 throw new CdmClientCacheException(e);
364 } catch (IllegalAccessException e) {
365 throw new CdmClientCacheException(e);
366 }
367 }
368
369
370 private CdmEntityInfo getDuplicate(Set<CdmEntityInfo> alreadyVisitedEntities, Object objectToCompare) {
371 if(objectToCompare != null) {
372 for(CdmEntityInfo cei: alreadyVisitedEntities) {
373 if(objectToCompare.equals(cei.getObject()) && objectToCompare != cei.getObject()) {
374 return cei;
375 }
376 }
377 }
378 return null;
379 }
380
381 private boolean containsIdenticalCdmEntity(Set<CdmEntityInfo> ceiSet, Object objectToCompare) {
382 if(objectToCompare != null) {
383 for(CdmEntityInfo cei : ceiSet) {
384 if(cei.getObject() == objectToCompare) {
385 return true;
386 }
387 }
388 }
389 return false;
390 }
391
392 public class CdmEntityInfo {
393
394 private Object object;
395 private CdmEntityInfo parent;
396 private List<CdmEntityInfo> children;
397 private Field field;
398 private String label;
399 private boolean isProxy;
400
401 public CdmEntityInfo(Object object) {
402 this.object = object;
403 isProxy = false;
404 children = new ArrayList<CdmEntityInfo>();
405 }
406
407 public CdmEntityInfo getParent() {
408 return parent;
409 }
410
411 public void setParent(CdmEntityInfo parent) {
412 this.parent = parent;
413 }
414
415 public List<CdmEntityInfo> getChildren() {
416 return children;
417 }
418
419 public void setChildren(List<CdmEntityInfo> children) {
420 this.children = children;
421 }
422
423 public void addChild(CdmEntityInfo cei) {
424 this.children.add(cei);
425 cei.setParent(this);
426 }
427
428 public Field getField() {
429 return field;
430 }
431
432 public void setField(Field field) {
433 this.field = field;
434 }
435
436
437 public String getLabel() {
438 String label;
439 String fieldName = "";
440 if(field != null) {
441 fieldName = field.getName();
442 }
443 if(object != null) {
444 if(object instanceof HibernateProxy) {
445 LazyInitializer hli = ((HibernateProxy)object).getHibernateLazyInitializer();
446 if(hli.isUninitialized()) {
447 label = "[HibernateProxy] " + fieldName;
448 } else {
449 label = fieldName + "Object is a HibernateProxy, but initialised";
450 }
451 } else if(object instanceof PersistentCollection) {
452 PersistentCollection pc = ((PersistentCollection)object);
453 if(!pc.wasInitialized()) {
454 label = "[PersistentCollection] " + fieldName;
455 } else {
456 label = fieldName + "Object is a PersistentCollection, but initialised";
457 }
458 } else if(object instanceof Collection) {
459 String className = object.getClass().getName();
460 label = "[" + className + "] " + fieldName + " : " + String.valueOf(((Collection)object).size());
461 } else if(object instanceof Map) {
462 String className = object.getClass().getName();
463 label = "[" + className + "] " + fieldName + " : " + String.valueOf(((Map)object).size());
464 } else {
465 String className = object.getClass().getName();
466 label = "[" + className + "] " + fieldName + " : " + object.toString();
467 }
468 } else {
469 label = "[NULL] " + fieldName;
470 }
471 return label;
472 }
473
474 public void setLabel(String label) {
475 this.label = label;
476 }
477
478 public Object getObject() {
479 return object;
480 }
481
482 public void setObject(Object object) {
483 this.object = object;
484 }
485
486 public boolean isProxy() {
487 return isProxy;
488 }
489
490 public void setProxy(boolean isProxy) {
491 this.isProxy = isProxy;
492 }
493
494
495
496 }
497
498 }