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
.cdm
.cache
;
11 import java
.lang
.management
.ManagementFactory
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.HashMap
;
15 import java
.util
.List
;
17 import java
.util
.UUID
;
19 import javax
.management
.MBeanServer
;
21 import org
.apache
.log4j
.Logger
;
23 import eu
.etaxonomy
.cdm
.api
.cache
.CdmCacher
;
24 import eu
.etaxonomy
.cdm
.model
.ICdmCacher
;
25 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
26 import eu
.etaxonomy
.cdm
.persistence
.dto
.MergeResult
;
27 import net
.sf
.ehcache
.Cache
;
28 import net
.sf
.ehcache
.CacheManager
;
29 import net
.sf
.ehcache
.Element
;
30 import net
.sf
.ehcache
.Status
;
31 import net
.sf
.ehcache
.config
.CacheConfiguration
;
32 import net
.sf
.ehcache
.config
.SizeOfPolicyConfiguration
;
33 import net
.sf
.ehcache
.management
.ManagementService
;
34 import net
.sf
.ehcache
.statistics
.LiveCacheStatistics
;
37 * This cache handles transient (id>0) and volatile (id=0) CdmBase objects.
38 * Volatile objects need to be added via {@link #putVolatitleEntity(CdmBase)}
39 * and their id is updated as soon as a transient object with the same
40 * uuid is added to the cacher.
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.
52 public class CdmTransientEntityCacher
implements ICdmCacher
{
54 private static final Logger logger
= Logger
.getLogger(CdmTransientEntityCacher
.class);
56 //the key for this cacher within the CacheManager
57 private final String cacheId
;
60 private final Cache cache
;
62 //permanent cache which is usually used to cache terms permanently
63 private static CdmCacher permanentCache
;
65 private final CacheLoader cacheLoader
;
67 //map for volatile entities (id=0)
68 private final Map
<UUID
, CdmBase
> volatileEntitiesMap
= new HashMap
<>();
70 private static volatile boolean managementBeansConfigured
= false;
72 // ********************* CONSTRUCTOR **********************************/
74 public CdmTransientEntityCacher(String cacheId
) {
75 this.cacheId
= cacheId
;
77 cache
= new Cache(getEntityCacheConfiguration(cacheId
));
79 getCacheManager().removeCache(cache
.getName());
80 getCacheManager().addCache(cache
);
82 cacheLoader
= new CacheLoader(this);
85 public CdmTransientEntityCacher(Object sessionOwner
) {
86 this(generateCacheId(sessionOwner
));
89 //****************************** STATIC METHODS *********************************/
92 * Generates an id for this session.
96 private static String
generateCacheId(Object sessionOwner
) {
97 return sessionOwner
.getClass().getName() + String
.valueOf(sessionOwner
.hashCode());
100 public static <T
extends CdmBase
> CdmEntityCacheKey
<T
> generateKey(Class
<T
> clazz
, int id
) {
101 return new CdmEntityCacheKey
<T
>(clazz
, id
);
104 public static <T
extends CdmBase
> CdmEntityCacheKey
<T
> generateKey(T cdmBase
) {
105 Class
<T
> entityClass
= (Class
<T
>)cdmBase
.getClass();
106 return new CdmEntityCacheKey
<T
>(entityClass
, cdmBase
.getId());
109 public static void setPermanentCacher(CdmCacher permanentCacher
) {
110 permanentCache
= permanentCacher
;
113 //****************************** METHODS *********************************/
116 * Returns the default cache configuration.
118 private CacheConfiguration
getEntityCacheConfiguration(String cacheId
) {
119 SizeOfPolicyConfiguration sizeOfConfig
= new SizeOfPolicyConfiguration();
120 sizeOfConfig
.setMaxDepth(100);
121 sizeOfConfig
.setMaxDepthExceededBehavior("abort");
123 return new CacheConfiguration(cacheId
, 0)
126 .sizeOfPolicy(sizeOfConfig
)
127 .overflowToOffHeap(false);
130 public LiveCacheStatistics
getCacheStatistics() {
131 if(cache
.getStatus() == Status
.STATUS_ALIVE
) {
132 return cache
.getLiveCacheStatistics();
138 * Returns the cache corresponding to the cache id
140 private Cache
getCache() {
141 return getCacheManager().getCache(cacheId
);
145 * @return the singleton cacheManager
147 protected CacheManager
getCacheManager() {
149 CacheManager cacheManager
= CacheManager
.create();
151 if(!managementBeansConfigured
){
152 MBeanServer mBeanServer
= ManagementFactory
.getPlatformMBeanServer();
153 boolean registerCacheManager
= false;
154 boolean registerCaches
= true;
155 boolean registerCacheConfigurations
= false;
156 boolean registerCacheStatistics
= true;
157 ManagementService
.registerMBeans(cacheManager
, mBeanServer
, registerCacheManager
, registerCaches
, registerCacheConfigurations
, registerCacheStatistics
);
158 managementBeansConfigured
= true;
164 public <T
extends Object
> T
load(T obj
, boolean update
) {
165 return cacheLoader
.load(obj
, true, update
);
168 public <T
extends Object
> Map
<T
,T
> load(Map
<T
,T
> map
, boolean update
){
169 return cacheLoader
.load(map
, true, update
);
172 public <T
extends Object
> Collection
<T
> load(Collection
<T
> collection
, boolean update
){
173 return cacheLoader
.load(collection
, true, update
);
177 * Loads the {@link eu.etaxonomy.cdm.model.common.CdmBase cdmEntity}) graph recursively into the
180 * For in depth details on the whole mechanism see
181 * {@link CacheLoader#load(CdmBase, boolean, boolean)},
182 * {@link CacheLoader#loadRecursive(CdmBase, List, boolean)} and
183 * {@link CacheLoader#getCdmBaseTypeFieldValue(CdmBase, CdmBase, String, List, boolean)}
186 * the entity to be put into the cache
188 * all fields of the cached entity will be overwritten by setting
189 * them to the value of the cdm entity being loaded
192 public <T
extends CdmBase
> T
load(T cdmEntity
, boolean update
) {
193 return cacheLoader
.load(cdmEntity
, true, update
);
196 public MergeResult
<CdmBase
> load(MergeResult
<CdmBase
> mergeResult
, boolean update
) {
197 return cacheLoader
.load(mergeResult
, true, update
);
200 public CdmModelFieldPropertyFromClass
getFromCdmlibModelCache(String className
) {
201 return cacheLoader
.getFromCdmlibModelCache(className
);
204 private void putVolatitleEntity(CdmBase volatileEntity
) {
205 if(volatileEntity
!= null && volatileEntity
.getId() == 0 && volatileEntity
.getUuid() != null) {
206 CdmBase cachedEntity
= volatileEntitiesMap
.get(volatileEntity
.getUuid());
207 if (cachedEntity
== null){
208 volatileEntitiesMap
.put(volatileEntity
.getUuid(), volatileEntity
);
214 * Puts the passed <code>cdmEntity</code> into the according caches
215 * (cache, newEntitiesMap, permanentCache(TODO still needs to be checked, not implemented yet))
216 * as long it does not yet exist there.
218 * The adjacent <b>ENTITY GRAPH WILL NOT BE LOADED RECURSIVELY</b>
221 public void putToCache(CdmBase cdmEntity
) {
222 if (cdmEntity
== null){
224 }else if (!cdmEntity
.isPersited()){
225 putVolatitleEntity(cdmEntity
);
227 CdmBase cachedCdmEntity
= permanentCache
.load(cdmEntity
);
228 if(cachedCdmEntity
!= null) {
229 logger
.info("Cdm Entity with id : " + cdmEntity
.getId() + " already exists in permanent cache. Ignoring put.");
232 CdmEntityCacheKey
<?
> key
= new CdmEntityCacheKey
<>(cdmEntity
);
234 cachedCdmEntity
= getFromCache(key
);
235 if(cachedCdmEntity
== null) {
236 CdmBase cdmEntityToCache
= cdmEntity
;
237 CdmBase cachedVolatileEntity
= volatileEntitiesMap
.get(cdmEntity
.getUuid());
238 //if former volatile object became transient now
239 if(cachedVolatileEntity
!= null) {
240 cachedVolatileEntity
.setId(cdmEntity
.getId());
241 cdmEntityToCache
= cachedVolatileEntity
;
243 putToTransientCache(key
, cdmEntityToCache
);
244 cdmEntityToCache
.initListener();
245 volatileEntitiesMap
.remove(cdmEntity
.getUuid());
246 if (logger
.isDebugEnabled()){logger
.debug(" - object of type " + cdmEntityToCache
.getClass().getName() + " with id " + cdmEntityToCache
.getId() + " put in cache");}
249 logger
.debug(" - object of type " + cdmEntity
.getClass().getName() + " with id " + cdmEntity
.getId() + " already exists");
255 * Puts the entity to the cache for transient entities. If the entity is not transient
256 * but volatile (id = 0) an {@link IllegalArgumentException} is thrown
258 protected void putToTransientCache(CdmEntityCacheKey
<?
> key
, CdmBase cdmEntityToCache
) throws IllegalArgumentException
{
259 if (key
.getPersistenceId() == 0){
260 throw new IllegalArgumentException("Volatile objects are not allowed in the transient object cache. Use newEntitiesMap instead.");
262 getCache().put(new Element(key
, cdmEntityToCache
));
265 private Element
getCacheElement(CdmEntityCacheKey
<?
> key
) {
266 return getCache().get(key
);
269 public <T
extends CdmBase
> T
getFromCache(CdmEntityCacheKey
<T
> id
) {
270 Element e
= getCacheElement(id
);
275 @SuppressWarnings("unchecked")
276 T result
= (T
) e
.getObjectValue();
281 public <T
extends CdmBase
> T
getFromCache(Class
<T
> clazz
, int id
) {
282 CdmEntityCacheKey
<T
> cacheId
= generateKey(clazz
, id
);
283 return getFromCache(cacheId
);
287 public <T
extends CdmBase
> T
getFromCache(T cdmBase
) {
288 if (!cdmBase
.isPersited()){
289 return (T
)volatileEntitiesMap
.get(cdmBase
.getUuid());
291 CdmEntityCacheKey
<T
> cacheId
= generateKey(ProxyUtils
.deproxy(cdmBase
));
292 // first try this cache
293 T cachedCdmEntity
= getFromCache(cacheId
);
295 if(cachedCdmEntity
== null) {
296 // ... then try the permanent cache
297 //TODO also use generics and clazz parameter for getFromCache(uuid)
298 cachedCdmEntity
= (T
)permanentCache
.getFromCache(cdmBase
.getUuid());
300 return cachedCdmEntity
;
305 public List
<CdmBase
> getAllEntities() {
306 List
<CdmBase
> entities
= new ArrayList
<>();
307 Map
<String
, CdmBase
> elementsMap
= getCache().getAllWithLoader(getCache().getKeys(), null);
308 for (Map
.Entry
<String
, CdmBase
> entry
: elementsMap
.entrySet()) {
309 entities
.add(entry
.getValue());
314 public boolean exists(CdmEntityCacheKey
<?
> key
) {
315 return (getCacheElement(key
) != null);
318 public boolean existsAndIsNotNull(CdmEntityCacheKey
<?
> id
) {
319 return getFromCache(id
) != null;
322 public void clear() {
324 volatileEntitiesMap
.clear();
328 public void dispose() {
329 getCacheManager().removeCache(cache
.getName());
331 volatileEntitiesMap
.clear();
335 public <T
extends CdmBase
> T
load(T cdmEntity
) {
336 return load(cdmEntity
, true);
340 public boolean isCachable(CdmBase cdmEntity
) {
345 public boolean exists(CdmBase cdmEntity
) {
346 return exists(generateKey(cdmEntity
));