3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.taxeditor
.remoting
.cache
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.HashMap
;
15 import java
.util
.HashSet
;
16 import java
.util
.List
;
19 import java
.util
.UUID
;
21 import net
.sf
.ehcache
.Cache
;
22 import net
.sf
.ehcache
.CacheManager
;
23 import net
.sf
.ehcache
.Element
;
24 import net
.sf
.ehcache
.Status
;
25 import net
.sf
.ehcache
.config
.CacheConfiguration
;
26 import net
.sf
.ehcache
.config
.SizeOfPolicyConfiguration
;
27 import net
.sf
.ehcache
.statistics
.LiveCacheStatistics
;
29 import org
.apache
.log4j
.Logger
;
31 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationState
;
32 import eu
.etaxonomy
.cdm
.api
.cache
.CdmServiceCacher
;
33 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
;
34 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CdmEntityIdentifier
;
35 import eu
.etaxonomy
.cdm
.model
.ICdmCacher
;
36 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
37 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
38 import eu
.etaxonomy
.taxeditor
.session
.ICdmEntitySessionManager
;
42 * This cache guarantees that
43 * - all objects put will be ancestors of CdmBase
44 * - all CdmBase objects in the cache will be already de-proxied
45 * - after any CdmBase object is put in the cache,
46 * all non-null / non-proxy CdmBase objects in the sub-graph
47 * will also be present in the cache.
54 public class CdmTransientEntityCacher
implements ICdmCacher
{
56 private static final Logger logger
= Logger
.getLogger(CdmTransientEntityCacher
.class);
59 private final ICdmEntitySessionManager cdmEntitySessionManager
;
61 private static CdmServiceCacher cdmServiceCacher
;
63 private final String cacheId
;
65 private final Cache cache
;
67 private final CacheLoader cacheLoader
;
69 private final Map
<UUID
, CdmBase
> newEntitiesMap
= new HashMap
<UUID
, CdmBase
>();
71 public CdmTransientEntityCacher(String cacheId
, ICdmEntitySessionManager cdmEntitySessionManager
) {
72 this.cacheId
= cacheId
;
74 cache
= new Cache(getEntityCacheConfiguration(cacheId
));
76 CacheManager
.create().removeCache(cache
.getName());
77 CacheManager
.create().addCache(cache
);
79 this.cdmEntitySessionManager
= cdmEntitySessionManager
;
81 cacheLoader
= new CacheLoader(this);
85 public CdmTransientEntityCacher(Object sessionOwner
, ICdmEntitySessionManager cdmEntitySessionManager
) {
86 this(generateCacheId(sessionOwner
), cdmEntitySessionManager
);
89 public static String
generateCacheId(Object sessionOwner
) {
90 return sessionOwner
.getClass().getName() + String
.valueOf(sessionOwner
.hashCode());
94 * Returns the default cache configuration.
98 private CacheConfiguration
getEntityCacheConfiguration(String cacheId
) {
99 SizeOfPolicyConfiguration sizeOfConfig
= new SizeOfPolicyConfiguration();
100 sizeOfConfig
.setMaxDepth(100);
101 sizeOfConfig
.setMaxDepthExceededBehavior("abort");
103 return new CacheConfiguration(cacheId
, 0)
106 .sizeOfPolicy(sizeOfConfig
)
107 .overflowToOffHeap(false);
111 public static void setDefaultCacher(CdmServiceCacher css
) {
112 cdmServiceCacher
= css
;
115 public LiveCacheStatistics
getCacheStatistics() {
116 if(cache
.getStatus() == Status
.STATUS_ALIVE
) {
117 return cache
.getLiveCacheStatistics();
124 * Returns the cache corresponding to the cache id
129 private Cache
getCache() {
130 return CacheManager
.create().getCache(cacheId
);
133 public <T
extends Object
> T
load(T obj
, boolean update
) {
134 return cacheLoader
.load(obj
, true, update
);
137 public <T
extends Object
> Map
<T
,T
> load(Map
<T
,T
> map
, boolean update
){
138 return cacheLoader
.load(map
, true, update
);
141 public <T
extends Object
> Collection
<T
> load(Collection
<T
> collection
, boolean update
){
142 return cacheLoader
.load(collection
, true, update
);
145 public CdmBase
load(CdmBase cdmEntity
, boolean update
) {
146 return cacheLoader
.load(cdmEntity
, true, update
);
150 private CdmBase
load(CdmEntityIdentifier cei
, boolean update
) {
151 return CdmApplicationState
.getCommonService().findWithUpdate(cei
.getCdmClass(), cei
.getId());
155 public UpdateResult
load(UpdateResult result
, boolean update
) {
156 // probably a good time to broadcast to other sessions
158 Set
<CdmBase
> updatedObjects
= result
.getUpdatedObjects();
159 Set
<CdmBase
> reloadedObjects
= new HashSet
<CdmBase
>();
160 Set
<CdmEntityIdentifier
> updatedCdmIds
= result
.getUpdatedCdmIds();
161 boolean updatedCdmIdsIsEmpty
= updatedCdmIds
.isEmpty();
163 // if the cdm identifier set contains identifiers of objects already
164 // present in the updated objects set reomve them
165 for(CdmBase updatedObject
: updatedObjects
) {
166 if(updatedObject
!= null && exists(new CdmEntityCacheKey(updatedObject
.getClass(), updatedObject
.getId()))) {
167 CdmEntityIdentifier cdmEntityIdentifier
= new CdmEntityIdentifier(updatedObject
.getId(), updatedObject
.getClass());
168 if(!updatedCdmIdsIsEmpty
&& updatedCdmIds
.contains(cdmEntityIdentifier
)) {
169 updatedCdmIds
.remove(cdmEntityIdentifier
);
171 reloadedObjects
.add(cacheLoader
.load(updatedObject
, true, update
));
175 // remote load cdm identifiers of objects which already exist
178 for(CdmEntityIdentifier cei
: updatedCdmIds
) {
179 if(exists(new CdmEntityCacheKey(cei
.getCdmClass(), cei
.getId()))) {
180 reloadedObjects
.add(load(cei
, update
));
184 updatedObjects
.clear();
185 result
.addUpdatedObjects(reloadedObjects
);
189 public MergeResult
<CdmBase
> load(MergeResult
<CdmBase
> mergeResult
, boolean update
) {
190 return cacheLoader
.load(mergeResult
, true, update
);
193 public CdmModelFieldPropertyFromClass
getFromCdmlibModelCache(String className
) {
194 return cacheLoader
.getFromCdmlibModelCache(className
);
198 public void addNewEntity(CdmBase newEntity
) {
199 if(newEntity
!= null && newEntity
.getId() == 0 && newEntity
.getUuid() != null) {
200 newEntitiesMap
.put(newEntity
.getUuid(), newEntity
);
205 public void put(CdmBase cdmEntity
) {
207 CdmBase cachedCdmEntity
= cdmServiceCacher
.load(cdmEntity
);
208 if(cachedCdmEntity
!= null) {
209 logger
.info("Cdm Entity with id : " + cdmEntity
.getId() + " already exists in permanent cache. Ignoring put.");
212 CdmEntityCacheKey id
= new CdmEntityCacheKey(cdmEntity
);
214 cachedCdmEntity
= getFromCache(id
);
215 if(cachedCdmEntity
== null) {
216 CdmBase cdmEntityToCache
= cdmEntity
;
217 CdmBase newEntity
= newEntitiesMap
.get(cdmEntity
.getUuid());
218 if(newEntity
!= null) {
219 newEntity
.setId(cdmEntity
.getId());
220 cdmEntityToCache
= newEntity
;
222 getCache().put(new Element(id
, cdmEntityToCache
));
223 cdmEntityToCache
.initListener();
224 newEntitiesMap
.remove(cdmEntity
.getUuid());
225 logger
.info(" - object of type " + cdmEntityToCache
.getClass().getName() + " with id " + cdmEntityToCache
.getId() + " put in cache");
228 logger
.info(" - object of type " + cdmEntity
.getClass().getName() + " with id " + cdmEntity
.getId() + " already exists");
232 private Element
getCacheElement(CdmEntityCacheKey key
) {
233 return getCache().get(key
);
237 public CdmBase
getFromCache(CdmEntityCacheKey id
) {
238 Element e
= getCacheElement(id
);
243 return (CdmBase
) e
.getObjectValue();
247 public CdmBase
getFromCache(Class
<?
extends CdmBase
> clazz
, int id
) {
248 CdmEntityCacheKey cacheId
= generateKey(clazz
,id
);
249 return getFromCache(cacheId
);
253 public CdmBase
getFromCache(CdmBase cdmBase
) {
255 CdmEntityCacheKey cacheId
= generateKey((CdmBase
)ProxyUtils
.deproxy(cdmBase
));
256 // first try this cache
257 CdmBase cachedCdmEntity
= getFromCache(cacheId
);
259 if(cachedCdmEntity
== null) {
260 // ... then try the permanent cache
261 cachedCdmEntity
= cdmServiceCacher
.getFromCache(cdmBase
.getUuid());
264 return cachedCdmEntity
;
267 public CdmBase
getFromCache(CdmBase cdmBase
, Class
<?
extends CdmBase
> clazz
) {
269 cdmBase
= CdmBase
.deproxy(cdmBase
, clazz
);
270 return getFromCache(cdmBase
);
273 public List
<CdmBase
> getAllEntities() {
274 List
<CdmBase
> entities
= new ArrayList
<CdmBase
>();
275 Map
<String
, CdmBase
> elementsMap
= getCache().getAllWithLoader(getCache().getKeys(), null);
276 for (Map
.Entry
<String
, CdmBase
> entry
: elementsMap
.entrySet()) {
277 entities
.add(entry
.getValue());
282 public boolean exists(CdmEntityCacheKey key
) {
283 return (getCacheElement(key
) != null);
286 public boolean existsAndIsNotNull(CdmEntityCacheKey id
) {
287 return getFromCache(id
) != null;
290 public void clear() {
294 public void dispose() {
295 CacheManager
.create().removeCache(cache
.getName());
297 newEntitiesMap
.clear();
302 public static CdmEntityCacheKey
generateKey(Class
<?
extends CdmBase
> clazz
, int id
) {
303 return new CdmEntityCacheKey(clazz
, id
);
307 public static CdmEntityCacheKey
generateKey(CdmBase cdmBase
) {
308 Class
<?
extends CdmBase
> entityClass
= cdmBase
.getClass();
309 int id
= cdmBase
.getId();
310 return new CdmEntityCacheKey(entityClass
, id
);
314 public CdmBase
load(CdmBase cdmEntity
) {
315 return load(cdmEntity
, true);
319 public boolean isCachable(CdmBase cdmEntity
) {
324 public boolean exists(CdmBase cdmBase
) {
325 return exists(generateKey(cdmBase
));