2 * Copyright (C) 2014 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.taxeditor
.remoting
.cache
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Collection
;
13 import java
.util
.HashMap
;
14 import java
.util
.HashSet
;
15 import java
.util
.List
;
18 import java
.util
.UUID
;
20 import org
.apache
.log4j
.Logger
;
22 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationState
;
23 import eu
.etaxonomy
.cdm
.api
.cache
.CdmServiceCacher
;
24 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
;
25 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CdmEntityIdentifier
;
26 import eu
.etaxonomy
.cdm
.model
.ICdmCacher
;
27 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
28 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
29 import eu
.etaxonomy
.taxeditor
.session
.ICdmEntitySessionManager
;
30 import net
.sf
.ehcache
.Cache
;
31 import net
.sf
.ehcache
.CacheManager
;
32 import net
.sf
.ehcache
.Element
;
33 import net
.sf
.ehcache
.Status
;
34 import net
.sf
.ehcache
.config
.CacheConfiguration
;
35 import net
.sf
.ehcache
.config
.SizeOfPolicyConfiguration
;
36 import net
.sf
.ehcache
.statistics
.LiveCacheStatistics
;
40 * This cache guarantees that
41 * - all objects put will be ancestors of CdmBase
42 * - all CdmBase objects in the cache will be already de-proxied
43 * - after any CdmBase object is put in the cache,
44 * all non-null / non-proxy CdmBase objects in the sub-graph
45 * will also be present in the cache.
52 public class CdmTransientEntityCacher
implements ICdmCacher
{
54 private static final Logger logger
= Logger
.getLogger(CdmTransientEntityCacher
.class);
57 private final ICdmEntitySessionManager cdmEntitySessionManager
;
59 private static CdmServiceCacher cdmServiceCacher
;
61 private final String cacheId
;
63 private final Cache cache
;
65 private final CacheLoader cacheLoader
;
67 private final Map
<UUID
, CdmBase
> newEntitiesMap
= new HashMap
<UUID
, CdmBase
>();
69 public CdmTransientEntityCacher(String cacheId
, ICdmEntitySessionManager cdmEntitySessionManager
) {
70 this.cacheId
= cacheId
;
72 cache
= new Cache(getEntityCacheConfiguration(cacheId
));
74 CacheManager
.create().removeCache(cache
.getName());
75 CacheManager
.create().addCache(cache
);
77 this.cdmEntitySessionManager
= cdmEntitySessionManager
;
79 cacheLoader
= new CacheLoader(this);
83 public CdmTransientEntityCacher(Object sessionOwner
, ICdmEntitySessionManager cdmEntitySessionManager
) {
84 this(generateCacheId(sessionOwner
), cdmEntitySessionManager
);
87 public static String
generateCacheId(Object sessionOwner
) {
88 return sessionOwner
.getClass().getName() + String
.valueOf(sessionOwner
.hashCode());
92 * Returns the default cache configuration.
96 private CacheConfiguration
getEntityCacheConfiguration(String cacheId
) {
97 SizeOfPolicyConfiguration sizeOfConfig
= new SizeOfPolicyConfiguration();
98 sizeOfConfig
.setMaxDepth(100);
99 sizeOfConfig
.setMaxDepthExceededBehavior("abort");
101 return new CacheConfiguration(cacheId
, 0)
104 .sizeOfPolicy(sizeOfConfig
)
105 .overflowToOffHeap(false);
109 public static void setDefaultCacher(CdmServiceCacher css
) {
110 cdmServiceCacher
= css
;
113 public LiveCacheStatistics
getCacheStatistics() {
114 if(cache
.getStatus() == Status
.STATUS_ALIVE
) {
115 return cache
.getLiveCacheStatistics();
122 * Returns the cache corresponding to the cache id
127 private Cache
getCache() {
128 return CacheManager
.create().getCache(cacheId
);
131 public <T
extends Object
> T
load(T obj
, boolean update
) {
132 return cacheLoader
.load(obj
, true, update
);
135 public <T
extends Object
> Map
<T
,T
> load(Map
<T
,T
> map
, boolean update
){
136 return cacheLoader
.load(map
, true, update
);
139 public <T
extends Object
> Collection
<T
> load(Collection
<T
> collection
, boolean update
){
140 return cacheLoader
.load(collection
, true, update
);
143 public CdmBase
load(CdmBase cdmEntity
, boolean update
) {
144 return cacheLoader
.load(cdmEntity
, true, update
);
148 private CdmBase
load(CdmEntityIdentifier cei
, boolean update
) {
149 return CdmApplicationState
.getCommonService().findWithUpdate(cei
.getCdmClass(), cei
.getId());
153 public UpdateResult
load(UpdateResult result
, boolean update
) {
154 // probably a good time to broadcast to other sessions
156 Set
<CdmBase
> updatedObjects
= result
.getUpdatedObjects();
157 Set
<CdmBase
> reloadedObjects
= new HashSet
<CdmBase
>();
158 Set
<CdmEntityIdentifier
> updatedCdmIds
= result
.getUpdatedCdmIds();
159 boolean updatedCdmIdsIsEmpty
= updatedCdmIds
.isEmpty();
161 // if the cdm identifier set contains identifiers of objects already
162 // present in the updated objects set reomve them
163 for(CdmBase updatedObject
: updatedObjects
) {
164 if(updatedObject
!= null && exists(new CdmEntityCacheKey(updatedObject
.getClass(), updatedObject
.getId()))) {
165 CdmEntityIdentifier cdmEntityIdentifier
= new CdmEntityIdentifier(updatedObject
.getId(), updatedObject
.getClass());
166 if(!updatedCdmIdsIsEmpty
&& updatedCdmIds
.contains(cdmEntityIdentifier
)) {
167 updatedCdmIds
.remove(cdmEntityIdentifier
);
169 reloadedObjects
.add(cacheLoader
.load(updatedObject
, true, update
));
173 // remote load cdm identifiers of objects which already exist
176 for(CdmEntityIdentifier cei
: updatedCdmIds
) {
177 if(exists(new CdmEntityCacheKey(cei
.getCdmClass(), cei
.getId()))) {
178 reloadedObjects
.add(load(cei
, update
));
182 updatedObjects
.clear();
183 result
.addUpdatedObjects(reloadedObjects
);
187 public MergeResult
<CdmBase
> load(MergeResult
<CdmBase
> mergeResult
, boolean update
) {
188 return cacheLoader
.load(mergeResult
, true, update
);
191 public CdmModelFieldPropertyFromClass
getFromCdmlibModelCache(String className
) {
192 return cacheLoader
.getFromCdmlibModelCache(className
);
196 public void addNewEntity(CdmBase newEntity
) {
197 if(newEntity
!= null && newEntity
.getId() == 0 && newEntity
.getUuid() != null) {
198 newEntitiesMap
.put(newEntity
.getUuid(), newEntity
);
203 public void put(CdmBase cdmEntity
) {
205 CdmBase cachedCdmEntity
= cdmServiceCacher
.load(cdmEntity
);
206 if(cachedCdmEntity
!= null) {
207 logger
.info("Cdm Entity with id : " + cdmEntity
.getId() + " already exists in permanent cache. Ignoring put.");
210 CdmEntityCacheKey id
= new CdmEntityCacheKey(cdmEntity
);
212 cachedCdmEntity
= getFromCache(id
);
213 if(cachedCdmEntity
== null) {
214 CdmBase cdmEntityToCache
= cdmEntity
;
215 CdmBase newEntity
= newEntitiesMap
.get(cdmEntity
.getUuid());
216 if(newEntity
!= null) {
217 newEntity
.setId(cdmEntity
.getId());
218 cdmEntityToCache
= newEntity
;
220 getCache().put(new Element(id
, cdmEntityToCache
));
221 cdmEntityToCache
.initListener();
222 newEntitiesMap
.remove(cdmEntity
.getUuid());
223 logger
.info(" - object of type " + cdmEntityToCache
.getClass().getName() + " with id " + cdmEntityToCache
.getId() + " put in cache");
226 logger
.info(" - object of type " + cdmEntity
.getClass().getName() + " with id " + cdmEntity
.getId() + " already exists");
230 private Element
getCacheElement(CdmEntityCacheKey key
) {
231 return getCache().get(key
);
235 public CdmBase
getFromCache(CdmEntityCacheKey id
) {
236 Element e
= getCacheElement(id
);
241 return (CdmBase
) e
.getObjectValue();
245 public CdmBase
getFromCache(Class
<?
extends CdmBase
> clazz
, int id
) {
246 CdmEntityCacheKey cacheId
= generateKey(clazz
,id
);
247 return getFromCache(cacheId
);
251 public CdmBase
getFromCache(CdmBase cdmBase
) {
253 CdmEntityCacheKey cacheId
= generateKey((CdmBase
)ProxyUtils
.deproxy(cdmBase
));
254 // first try this cache
255 CdmBase cachedCdmEntity
= getFromCache(cacheId
);
257 if(cachedCdmEntity
== null) {
258 // ... then try the permanent cache
259 cachedCdmEntity
= cdmServiceCacher
.getFromCache(cdmBase
.getUuid());
262 return cachedCdmEntity
;
265 public CdmBase
getFromCache(CdmBase cdmBase
, Class
<?
extends CdmBase
> clazz
) {
267 cdmBase
= CdmBase
.deproxy(cdmBase
, clazz
);
268 return getFromCache(cdmBase
);
271 public List
<CdmBase
> getAllEntities() {
272 List
<CdmBase
> entities
= new ArrayList
<CdmBase
>();
273 Map
<String
, CdmBase
> elementsMap
= getCache().getAllWithLoader(getCache().getKeys(), null);
274 for (Map
.Entry
<String
, CdmBase
> entry
: elementsMap
.entrySet()) {
275 entities
.add(entry
.getValue());
280 public boolean exists(CdmEntityCacheKey key
) {
281 return (getCacheElement(key
) != null);
284 public boolean existsAndIsNotNull(CdmEntityCacheKey id
) {
285 return getFromCache(id
) != null;
288 public void clear() {
292 public void dispose() {
293 CacheManager
.create().removeCache(cache
.getName());
295 newEntitiesMap
.clear();
300 public static CdmEntityCacheKey
generateKey(Class
<?
extends CdmBase
> clazz
, int id
) {
301 return new CdmEntityCacheKey(clazz
, id
);
305 public static CdmEntityCacheKey
generateKey(CdmBase cdmBase
) {
306 Class
<?
extends CdmBase
> entityClass
= cdmBase
.getClass();
307 int id
= cdmBase
.getId();
308 return new CdmEntityCacheKey(entityClass
, id
);
312 public CdmBase
load(CdmBase cdmEntity
) {
313 return load(cdmEntity
, true);
317 public boolean isCachable(CdmBase cdmEntity
) {
322 public boolean exists(CdmBase cdmBase
) {
323 return exists(generateKey(cdmBase
));