Revision 703e8efc
Added by Cherian Mathew about 9 years ago
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/remoting/cache/CdmTransientEntityCacher.java | ||
---|---|---|
12 | 12 |
import java.lang.reflect.Field; |
13 | 13 |
import java.util.ArrayList; |
14 | 14 |
import java.util.Collection; |
15 |
import java.util.HashMap; |
|
15 | 16 |
import java.util.HashSet; |
16 | 17 |
import java.util.Iterator; |
17 | 18 |
import java.util.List; |
18 | 19 |
import java.util.Map; |
19 | 20 |
import java.util.Set; |
21 |
import java.util.TreeMap; |
|
22 |
import java.util.TreeSet; |
|
20 | 23 |
|
21 | 24 |
import javassist.util.proxy.ProxyFactory; |
22 | 25 |
import net.sf.ehcache.Cache; |
23 | 26 |
import net.sf.ehcache.Element; |
24 | 27 |
import net.sf.ehcache.config.CacheConfiguration; |
28 |
import net.sf.ehcache.config.SizeOfPolicyConfiguration; |
|
29 |
import net.sf.ehcache.statistics.LiveCacheStatistics; |
|
25 | 30 |
import net.sf.ehcache.store.MemoryStoreEvictionPolicy; |
26 | 31 |
|
27 | 32 |
import org.apache.log4j.Logger; |
33 |
import org.hibernate.collection.internal.PersistentList; |
|
34 |
import org.hibernate.collection.internal.PersistentMap; |
|
35 |
import org.hibernate.collection.internal.PersistentSet; |
|
36 |
import org.hibernate.collection.internal.PersistentSortedMap; |
|
37 |
import org.hibernate.collection.internal.PersistentSortedSet; |
|
28 | 38 |
import org.hibernate.collection.spi.PersistentCollection; |
29 | 39 |
import org.hibernate.proxy.HibernateProxy; |
30 | 40 |
import org.hibernate.proxy.LazyInitializer; |
... | ... | |
32 | 42 |
|
33 | 43 |
import eu.etaxonomy.cdm.api.cache.CdmServiceCacher; |
34 | 44 |
import eu.etaxonomy.cdm.model.common.CdmBase; |
45 |
import eu.etaxonomy.cdm.model.common.PersistentMultiLanguageText; |
|
46 |
import eu.etaxonomy.taxeditor.remoting.CdmRemotingException; |
|
35 | 47 |
import eu.etaxonomy.taxeditor.remoting.cache.EntityCacherDebugResult.CdmEntityInfo; |
36 | 48 |
import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager; |
37 | 49 |
|
... | ... | |
90 | 102 |
cdmlibModelCache = CdmRemoteCacheManager.getInstance().getCdmModelGetMethodsCache(); |
91 | 103 |
this.cdmEntitySessionManager = cdmEntitySessionManager; |
92 | 104 |
|
105 |
|
|
93 | 106 |
} |
94 | 107 |
|
95 | 108 |
public CdmTransientEntityCacher(Object obj, ICdmEntitySessionManager cdmEntitySessionManager) { |
... | ... | |
102 | 115 |
* @return |
103 | 116 |
*/ |
104 | 117 |
private CacheConfiguration getEntityCacheConfiguration(String cacheId) { |
118 |
SizeOfPolicyConfiguration sizeOfConfig = new SizeOfPolicyConfiguration(); |
|
119 |
sizeOfConfig.setMaxDepth(10000); |
|
120 |
sizeOfConfig.setMaxDepthExceededBehavior("abort"); |
|
105 | 121 |
// For a better understanding on how to size caches, refer to |
106 | 122 |
// http://ehcache.org/documentation/configuration/cache-size |
107 | 123 |
return new CacheConfiguration(cacheId, 500) |
108 | 124 |
.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU) |
109 |
.eternal(false)
|
|
125 |
//.eternal(true);
|
|
110 | 126 |
// default ttl and tti set to 2 hours |
111 | 127 |
.timeToLiveSeconds(60*60*2) |
112 |
.timeToIdleSeconds(60*60*2); |
|
128 |
.timeToIdleSeconds(60*60*2) |
|
129 |
.statistics(true) |
|
130 |
.sizeOfPolicy(sizeOfConfig); |
|
131 |
|
|
132 |
|
|
113 | 133 |
|
114 | 134 |
} |
115 | 135 |
|
... | ... | |
117 | 137 |
cdmServiceCacher = css; |
118 | 138 |
} |
119 | 139 |
|
140 |
public LiveCacheStatistics getCacheStatistics() { |
|
141 |
return cache.getLiveCacheStatistics(); |
|
142 |
} |
|
143 |
|
|
120 | 144 |
/** |
121 | 145 |
* Returns the cache corresponding to the cache id |
122 | 146 |
* |
... | ... | |
387 | 411 |
} |
388 | 412 |
} |
389 | 413 |
|
414 |
if(o != null && o instanceof PersistentCollection) { |
|
415 |
PersistentCollection pc = ((PersistentCollection)o); |
|
416 |
if(pc.wasInitialized()) { |
|
417 |
o = getObject(pc); |
|
418 |
field.set(cdmEntity, o); |
|
419 |
} |
|
420 |
} |
|
421 |
|
|
390 | 422 |
//field.set(cdmEntity, o); |
391 | 423 |
CdmBase cdmEntityInSubGraph = null; |
392 | 424 |
if(o != null |
... | ... | |
401 | 433 |
// up-to-date by setting to the value of the cdm entity being loaded |
402 | 434 |
// the only execption to this is found below |
403 | 435 |
field.set(cachedCdmEntity, o); |
436 |
|
|
404 | 437 |
// the only exception to updating the field to the latest value |
405 | 438 |
// is the case where the field has been already initialised, cached and |
406 | 439 |
// is not the same as the one in the cache, in which case we set the value |
... | ... | |
518 | 551 |
public void dispose() { |
519 | 552 |
cache.removeAll(); |
520 | 553 |
cache.flush(); |
521 |
|
|
554 |
cdmServiceCacher.getDefaultCacheManager().removeCache(cacheId); |
|
522 | 555 |
} |
523 | 556 |
|
524 | 557 |
|
... | ... | |
584 | 617 |
if(obj instanceof CdmBase) { |
585 | 618 |
return (T) debugRecursive((CdmBase)obj, alreadyVisitedEntities, entityCacherDebugResult); |
586 | 619 |
} else if (obj instanceof Map) { |
587 |
return (T) debugRecursive((Map<T,T>)obj, alreadyVisitedEntities, entityCacherDebugResult);
|
|
620 |
return (T) debug((Map<T,T>)obj, alreadyVisitedEntities, entityCacherDebugResult); |
|
588 | 621 |
} else if (obj instanceof Collection) { |
589 |
return (T) debugRecursive((Collection<T>)obj, alreadyVisitedEntities, entityCacherDebugResult);
|
|
622 |
return (T) debug((Collection<T>)obj, alreadyVisitedEntities, entityCacherDebugResult); |
|
590 | 623 |
} |
591 | 624 |
|
592 | 625 |
logger.info("No caching yet for type " + obj.getClass().getName()); |
... | ... | |
653 | 686 |
|
654 | 687 |
private CdmBase debugRecursive(CdmBase cdmEntity, Set<CdmBase> alreadyVisitedEntities, EntityCacherDebugResult entityCacherDebugResult) { |
655 | 688 |
|
656 |
CdmBase cachedCdmEntity = debug(cdmEntity); |
|
689 |
|
|
657 | 690 |
|
658 | 691 |
// we want to recursive through the cdmEntity (and not the cachedCdmEntity) |
659 | 692 |
// since there could be new or deleted objects in the cdmEntity sub-graph |
... | ... | |
669 | 702 |
// this object will be either a CdmBase or a Collection / Map |
670 | 703 |
// with CdmBase as the generic type |
671 | 704 |
|
672 |
CdmBase cdmEntityInSubGraph = getDebugCdmBaseTypeFieldValue(cdmEntity, cachedCdmEntity, field, alreadyVisitedEntities, entityCacherDebugResult);
|
|
705 |
CdmBase cdmEntityInSubGraph = getDebugCdmBaseTypeFieldValue(cdmEntity, field, alreadyVisitedEntities, entityCacherDebugResult); |
|
673 | 706 |
if(cdmEntityInSubGraph != null) { |
674 | 707 |
if(!alreadyVisitedEntities.contains(cdmEntityInSubGraph)) { |
675 | 708 |
logger.info("recursive debugging object of type " + cdmEntityInSubGraph.getClass().getName() + " with id " + cdmEntityInSubGraph.getId()); |
... | ... | |
683 | 716 |
throw new CdmClientCacheException("CdmEntity with class " + cdmEntity.getClass().getName() + " is not found in the cdmlib model cache. " + |
684 | 717 |
"The cache may be corrupted or not in sync with the latest model version" ); |
685 | 718 |
} |
686 |
return cachedCdmEntity;
|
|
719 |
return cdmEntity; |
|
687 | 720 |
} |
688 | 721 |
|
689 | 722 |
|
690 | 723 |
private CdmBase getDebugCdmBaseTypeFieldValue(CdmBase cdmEntity, |
691 |
CdmBase cachedCdmEntity, |
|
692 | 724 |
String fieldName, |
693 | 725 |
Set<CdmBase> alreadyVisitedEntities, |
694 | 726 |
EntityCacherDebugResult entityCacherDebugResult) { |
695 | 727 |
|
696 |
// this method attempts to make sure that for any two objects found in |
|
697 |
// the object graph, if they are equal then they should also be the same, |
|
698 |
// which is crucial for the merge to work |
|
699 |
if(cachedCdmEntity == null) { |
|
700 |
throw new CdmClientCacheException("When trying to set field value, the cached cdm entity cannot be null"); |
|
701 |
} |
|
702 | 728 |
|
703 | 729 |
Class<?> clazz = cdmEntity.getClass(); |
704 | 730 |
try { |
... | ... | |
715 | 741 |
if(o != null && o instanceof HibernateProxy) { |
716 | 742 |
LazyInitializer hli = ((HibernateProxy)o).getHibernateLazyInitializer(); |
717 | 743 |
if(!hli.isUninitialized()) { |
718 |
o = ((HibernateProxy) o).getHibernateLazyInitializer().getImplementation(); |
|
744 |
o = hli.getImplementation(); |
|
745 |
} |
|
746 |
} |
|
747 |
|
|
748 |
if(o != null && o instanceof PersistentCollection) { |
|
749 |
PersistentCollection pc = ((PersistentCollection)o); |
|
750 |
if(pc.wasInitialized()) { |
|
751 |
o = getObject(pc); |
|
719 | 752 |
} |
720 | 753 |
} |
721 | 754 |
CdmBase cdmEntityInSubGraph = null; |
... | ... | |
726 | 759 |
if(CdmBase.class.isAssignableFrom(o.getClass())) { |
727 | 760 |
logger.info("found initialised cdm entity '" + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId()); |
728 | 761 |
cdmEntityInSubGraph = (CdmBase)o; |
762 |
for(CdmBase visitedEntity : alreadyVisitedEntities) { |
|
763 |
if(cdmEntityInSubGraph.equals(visitedEntity) && cdmEntityInSubGraph != visitedEntity) { |
|
764 |
//logger.info(" - found duplicate entity at " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId()); |
|
765 |
CdmEntityInfo cei = entityCacherDebugResult.new CdmEntityInfo(cdmEntityInSubGraph, cdmEntity, field); |
|
766 |
CdmEntityInfo dupCei = entityCacherDebugResult.new CdmEntityInfo(visitedEntity, null, field); |
|
767 |
entityCacherDebugResult.addDuplicateEntity(cei, dupCei); |
|
768 |
} |
|
769 |
} |
|
770 |
|
|
729 | 771 |
CdmBase cachedCdmEntityInSubGraph = getFromCache(cdmEntityInSubGraph); |
730 | 772 |
// the only exception to updating the field to the latest value |
731 | 773 |
// is the case where the field has been already initialised, cached and |
732 | 774 |
// is not the same as the one in the cache, in which case we set the value |
733 | 775 |
// of the field to the one found in the cache |
734 |
if(cachedCdmEntityInSubGraph != null) { |
|
735 |
if(cachedCdmEntityInSubGraph != cdmEntityInSubGraph) { |
|
736 |
logger.info(" - found duplicate entity at " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId()); |
|
737 |
CdmEntityInfo cei = entityCacherDebugResult.new CdmEntityInfo(cdmEntityInSubGraph, cdmEntity, field); |
|
738 |
CdmEntityInfo cachedCei = entityCacherDebugResult.new CdmEntityInfo(cachedCdmEntityInSubGraph, null, field); |
|
739 |
entityCacherDebugResult.addDuplicateEntity(cei, cachedCei); |
|
740 |
} |
|
741 |
} else { |
|
776 |
|
|
777 |
if(cachedCdmEntityInSubGraph == null) { |
|
742 | 778 |
// found a cdm entity which is not in cache - need to record this |
743 |
logger.info(" - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId()); |
|
779 |
//logger.info(" - found entity not in cache " + fieldName + "' in object of type " + clazz.getName() + " with id " + cdmEntity.getId());
|
|
744 | 780 |
CdmEntityInfo cei = entityCacherDebugResult.new CdmEntityInfo(cdmEntityInSubGraph, cdmEntity, field); |
745 | 781 |
entityCacherDebugResult.addEntityNotInCache(cei); |
746 | 782 |
} |
... | ... | |
763 | 799 |
} |
764 | 800 |
} |
765 | 801 |
|
802 |
public static Object getObject(PersistentCollection pc) { |
|
803 |
if(pc != null) { |
|
804 |
if(pc instanceof PersistentSet) { |
|
805 |
return new HashSet((Set)pc); |
|
806 |
} |
|
807 |
if(pc instanceof PersistentSortedSet) { |
|
808 |
return new TreeSet((Set)pc); |
|
809 |
} |
|
810 |
if(pc instanceof PersistentList) { |
|
811 |
return new ArrayList((List)pc); |
|
812 |
} |
|
813 |
if(pc instanceof PersistentMap || pc instanceof PersistentMultiLanguageText) { |
|
814 |
return new HashMap((Map)pc); |
|
815 |
} |
|
816 |
if(pc instanceof PersistentSortedMap) { |
|
817 |
return new TreeMap((Map)pc); |
|
818 |
} |
|
819 |
throw new CdmRemotingException("Cannot get Collection field for type " + pc.getClass().getName()); |
|
820 |
} |
|
821 |
return null; |
|
822 |
} |
|
766 | 823 |
|
767 | 824 |
} |
Also available in: Unified diff
CdmRemoteCacheManager : added cache getter methods
CdmTransientEntityCacher, CdmEntitySession, CdmEntitySessionManager, ICdmEntitySession, ICdmEntitySessionManager, ICdmEntitySessionManagerObserver, MockCdmEntitySession, MockCdmEntitySessionManager , eu.etaxonomy.taxeditor.cdmlib/src/main/resources/cdmlib-ehcache.xml : added statistics config , getter method for live stats and observer to update when sessions are bound and disposed
eu.etaxonomy.taxeditor.store/plugin.xml, SessionsViewPart : added new sessions debug view
MatchStrategyConfigurator : moved to cdmlib
ParseHandler, AbstractMatchingPreferences, NonViralNameMatchingPreference, ReferenceMatchingPreference, TeamOrPersonMatchingPreference : refactored after removal of MatchStrategyConfigurator replaced by enum MatchStrategy