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
.HashSet
;
15 import java
.util
.List
;
19 import net
.sf
.ehcache
.Cache
;
20 import net
.sf
.ehcache
.CacheManager
;
21 import net
.sf
.ehcache
.Element
;
22 import net
.sf
.ehcache
.Status
;
23 import net
.sf
.ehcache
.config
.CacheConfiguration
;
24 import net
.sf
.ehcache
.config
.SizeOfPolicyConfiguration
;
25 import net
.sf
.ehcache
.statistics
.LiveCacheStatistics
;
27 import org
.apache
.log4j
.Logger
;
29 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationState
;
30 import eu
.etaxonomy
.cdm
.api
.cache
.CdmServiceCacher
;
31 import eu
.etaxonomy
.cdm
.api
.service
.UpdateResult
;
32 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CdmEntityIdentifier
;
33 import eu
.etaxonomy
.cdm
.model
.ICdmCacher
;
34 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
35 import eu
.etaxonomy
.taxeditor
.session
.ICdmEntitySessionManager
;
39 * This cache guarantees that
40 * - all objects put will be ancestors of CdmBase
41 * - all CdmBase objects in the cache will be already de-proxied
42 * - after any CdmBase object is put in the cache,
43 * all non-null / non-proxy CdmBase objects in the sub-graph
44 * will also be present in the cache.
51 public class CdmTransientEntityCacher
implements ICdmCacher
{
53 private static final Logger logger
= Logger
.getLogger(CdmTransientEntityCacher
.class);
56 private final ICdmEntitySessionManager cdmEntitySessionManager
;
58 private static CdmServiceCacher cdmServiceCacher
;
60 private final String cacheId
;
62 private final Cache cache
;
64 private final CacheLoader cacheLoader
;
68 public CdmTransientEntityCacher(String cacheId
, ICdmEntitySessionManager cdmEntitySessionManager
) {
69 this.cacheId
= cacheId
;
71 cache
= new Cache(getEntityCacheConfiguration(cacheId
));
73 CacheManager
.create().addCache(cache
);
75 this.cdmEntitySessionManager
= cdmEntitySessionManager
;
77 cacheLoader
= new CacheLoader(this);
80 public CdmTransientEntityCacher(Object sessionOwner
, ICdmEntitySessionManager cdmEntitySessionManager
) {
81 this(generateCacheId(sessionOwner
), cdmEntitySessionManager
);
84 public static String
generateCacheId(Object sessionOwner
) {
85 return sessionOwner
.getClass().getName() + String
.valueOf(sessionOwner
.hashCode());
89 * Returns the default cache configuration.
93 private CacheConfiguration
getEntityCacheConfiguration(String cacheId
) {
94 SizeOfPolicyConfiguration sizeOfConfig
= new SizeOfPolicyConfiguration();
95 sizeOfConfig
.setMaxDepth(10000);
96 sizeOfConfig
.setMaxDepthExceededBehavior("abort");
98 return new CacheConfiguration(cacheId
, 0)
101 .sizeOfPolicy(sizeOfConfig
)
102 .overflowToOffHeap(false);
106 public static void setDefaultCacher(CdmServiceCacher css
) {
107 cdmServiceCacher
= css
;
110 public LiveCacheStatistics
getCacheStatistics() {
111 if(cache
.getStatus() == Status
.STATUS_ALIVE
) {
112 return cache
.getLiveCacheStatistics();
119 * Returns the cache corresponding to the cache id
124 private Cache
getCache() {
125 return CacheManager
.create().getCache(cacheId
);
128 public <T
extends Object
> T
load(T obj
, boolean update
) {
129 return cacheLoader
.load(obj
, true, update
);
132 public <T
extends Object
> Map
<T
,T
> load(Map
<T
,T
> map
, boolean update
){
133 return cacheLoader
.load(map
, true, update
);
136 public <T
extends Object
> Collection
<T
> load(Collection
<T
> collection
, boolean update
){
137 return cacheLoader
.load(collection
, true, update
);
140 public CdmBase
load(CdmBase cdmEntity
, boolean update
) {
141 return cacheLoader
.load(cdmEntity
, true, update
);
145 private CdmBase
load(CdmEntityIdentifier cei
, boolean update
) {
146 return load(CdmBase
.deproxy(CdmApplicationState
.getCommonService().find(cei
.getCdmClass(), cei
.getId()),cei
.getCdmClass()), update
);
150 public UpdateResult
load(UpdateResult result
, boolean update
) {
151 // probably a good time to broadcast to other sessions
153 Set
<CdmBase
> updatedObjects
= result
.getUpdatedObjects();
154 Set
<CdmBase
> reloadedObjects
= new HashSet
<CdmBase
>();
155 Set
<CdmEntityIdentifier
> updatedCdmIds
= result
.getUpdatedCdmIds();
156 boolean updatedCdmIdsIsEmpty
= updatedCdmIds
.isEmpty();
158 // if the cdm identifier set contains identifiers of objects already
159 // present in the updated objects set reomve them
160 for(CdmBase updatedObject
: updatedObjects
) {
161 if(exists(new CdmEntityCacheKey(updatedObject
.getClass(), updatedObject
.getId()))) {
162 CdmEntityIdentifier cdmEntityIdentifier
= new CdmEntityIdentifier(updatedObject
.getId(), updatedObject
.getClass());
163 if(!updatedCdmIdsIsEmpty
&& updatedCdmIds
.contains(cdmEntityIdentifier
)) {
164 updatedCdmIds
.remove(cdmEntityIdentifier
);
166 reloadedObjects
.add(cacheLoader
.load(updatedObject
, true, update
));
170 // remote load cdm identifiers of objects which already exist
173 for(CdmEntityIdentifier cei
: updatedCdmIds
) {
174 if(exists(new CdmEntityCacheKey(cei
.getCdmClass(), cei
.getId()))) {
175 reloadedObjects
.add(load(cei
, update
));
179 updatedObjects
.clear();
180 result
.addUpdatedObjects(reloadedObjects
);
184 public CdmModelFieldPropertyFromClass
getFromCdmlibModelCache(String className
) {
185 return cacheLoader
.getFromCdmlibModelCache(className
);
192 public void put(CdmBase cdmEntity
) {
194 CdmBase cachedCdmEntity
= cdmServiceCacher
.load(cdmEntity
);
195 if(cachedCdmEntity
!= null) {
196 logger
.info("Cdm Entity with id : " + cdmEntity
.getId() + " already exists in permanent cache. Ignoring put.");
199 CdmEntityCacheKey id
= new CdmEntityCacheKey(cdmEntity
);
201 cachedCdmEntity
= getFromCache(id
);
202 if(cachedCdmEntity
== null) {
203 getCache().put(new Element(id
, cdmEntity
));
204 logger
.info(" - object of type " + cdmEntity
.getClass().getName() + " with id " + cdmEntity
.getId() + " put in cache");
207 logger
.info(" - object of type " + cdmEntity
.getClass().getName() + " with id " + cdmEntity
.getId() + " already exists");
211 private Element
getCacheElement(CdmEntityCacheKey key
) {
212 return getCache().get(key
);
216 public CdmBase
getFromCache(CdmEntityCacheKey id
) {
217 Element e
= getCacheElement(id
);
222 return (CdmBase
) e
.getObjectValue();
226 public CdmBase
getFromCache(Class
<?
extends CdmBase
> clazz
, int id
) {
227 CdmEntityCacheKey cacheId
= generateKey(clazz
,id
);
228 return getFromCache(cacheId
);
232 public CdmBase
getFromCache(CdmBase cdmBase
) {
234 CdmEntityCacheKey cacheId
= generateKey((CdmBase
)ProxyUtils
.deproxy(cdmBase
));
235 // first try this cache
236 CdmBase cachedCdmEntity
= getFromCache(cacheId
);
238 if(cachedCdmEntity
== null) {
239 // ... then try the permanent cache
240 cachedCdmEntity
= cdmServiceCacher
.getFromCache(cdmBase
.getUuid());
243 return cachedCdmEntity
;
246 public CdmBase
getFromCache(CdmBase cdmBase
, Class
<?
extends CdmBase
> clazz
) {
248 cdmBase
= CdmBase
.deproxy(cdmBase
, clazz
);
249 return getFromCache(cdmBase
);
252 public List
<CdmBase
> getAllEntities() {
253 List
<CdmBase
> entities
= new ArrayList
<CdmBase
>();
254 Map
<String
, CdmBase
> elementsMap
= getCache().getAllWithLoader(getCache().getKeys(), null);
255 for (Map
.Entry
<String
, CdmBase
> entry
: elementsMap
.entrySet()) {
256 entities
.add(entry
.getValue());
261 public boolean exists(CdmEntityCacheKey key
) {
262 return (getCacheElement(key
) != null);
265 public boolean existsAndIsNotNull(CdmEntityCacheKey id
) {
266 return getFromCache(id
) != null;
269 public void clear() {
273 public void dispose() {
274 CacheManager
.create().removeCache(cache
.getName());
280 public static CdmEntityCacheKey
generateKey(Class
<?
extends CdmBase
> clazz
, int id
) {
281 return new CdmEntityCacheKey(clazz
, id
);
285 public static CdmEntityCacheKey
generateKey(CdmBase cdmBase
) {
286 Class
<?
extends CdmBase
> entityClass
= cdmBase
.getClass();
287 int id
= cdmBase
.getId();
288 return new CdmEntityCacheKey(entityClass
, id
);
292 * @see eu.etaxonomy.cdm.model.ICdmCacher#load(eu.etaxonomy.cdm.model.common.CdmBase)
295 public CdmBase
load(CdmBase cdmEntity
) {
296 return load(cdmEntity
, true);
300 * @see eu.etaxonomy.cdm.model.ICdmCacher#isCachable(eu.etaxonomy.cdm.model.common.CdmBase)
303 public boolean isCachable(CdmBase cdmEntity
) {
308 * @see eu.etaxonomy.cdm.model.ICdmCacher#exists(eu.etaxonomy.cdm.model.common.CdmBase)
311 public boolean exists(CdmBase cdmBase
) {
312 return exists(generateKey(cdmBase
));