7047b190d5d681558aca023a467b3621cdb5d00a
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / eu / etaxonomy / taxeditor / remoting / cache / CdmTransientEntityCacher.java
1 // $Id$
2 /**
3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
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.
9 */
10 package eu.etaxonomy.taxeditor.remoting.cache;
11
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;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.UUID;
20
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;
28
29 import org.apache.log4j.Logger;
30
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;
39
40 /**
41 *
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.
48 *
49 * @author cmathew
50 * @date 14 Oct 2014
51 *
52 */
53
54 public class CdmTransientEntityCacher implements ICdmCacher {
55
56 private static final Logger logger = Logger.getLogger(CdmTransientEntityCacher.class);
57
58
59 private final ICdmEntitySessionManager cdmEntitySessionManager;
60
61 private static CdmServiceCacher cdmServiceCacher;
62
63 private final String cacheId;
64
65 private final Cache cache;
66
67 private final CacheLoader cacheLoader;
68
69 private final Map<UUID, CdmBase> newEntitiesMap = new HashMap<UUID, CdmBase>();
70
71 public CdmTransientEntityCacher(String cacheId, ICdmEntitySessionManager cdmEntitySessionManager) {
72 this.cacheId = cacheId;
73
74 cache = new Cache(getEntityCacheConfiguration(cacheId));
75
76 CacheManager.create().addCache(cache);
77
78 this.cdmEntitySessionManager = cdmEntitySessionManager;
79
80 cacheLoader = new CacheLoader(this);
81
82 }
83
84 public CdmTransientEntityCacher(Object sessionOwner, ICdmEntitySessionManager cdmEntitySessionManager) {
85 this(generateCacheId(sessionOwner), cdmEntitySessionManager);
86 }
87
88 public static String generateCacheId(Object sessionOwner) {
89 return sessionOwner.getClass().getName() + String.valueOf(sessionOwner.hashCode());
90 }
91
92 /**
93 * Returns the default cache configuration.
94 *
95 * @return
96 */
97 private CacheConfiguration getEntityCacheConfiguration(String cacheId) {
98 SizeOfPolicyConfiguration sizeOfConfig = new SizeOfPolicyConfiguration();
99 sizeOfConfig.setMaxDepth(100);
100 sizeOfConfig.setMaxDepthExceededBehavior("abort");
101
102 return new CacheConfiguration(cacheId, 0)
103 .eternal(true)
104 .statistics(true)
105 .sizeOfPolicy(sizeOfConfig)
106 .overflowToOffHeap(false);
107
108 }
109
110 public static void setDefaultCacher(CdmServiceCacher css) {
111 cdmServiceCacher = css;
112 }
113
114 public LiveCacheStatistics getCacheStatistics() {
115 if(cache.getStatus() == Status.STATUS_ALIVE) {
116 return cache.getLiveCacheStatistics();
117 }
118 return null;
119
120 }
121
122 /**
123 * Returns the cache corresponding to the cache id
124 *
125 * @param cacheId
126 * @return
127 */
128 private Cache getCache() {
129 return CacheManager.create().getCache(cacheId);
130 }
131
132 public <T extends Object> T load(T obj, boolean update) {
133 return cacheLoader.load(obj, true, update);
134 }
135
136 public <T extends Object> Map<T,T> load(Map<T,T> map, boolean update){
137 return cacheLoader.load(map, true, update);
138 }
139
140 public <T extends Object> Collection<T> load(Collection<T> collection, boolean update){
141 return cacheLoader.load(collection, true, update);
142 }
143
144 public CdmBase load(CdmBase cdmEntity, boolean update) {
145 return cacheLoader.load(cdmEntity, true, update);
146 }
147
148
149 private CdmBase load(CdmEntityIdentifier cei, boolean update) {
150 return CdmApplicationState.getCommonService().findWithUpdate(cei.getCdmClass(), cei.getId());
151 }
152
153
154 public UpdateResult load(UpdateResult result, boolean update) {
155 // probably a good time to broadcast to other sessions
156
157 Set<CdmBase> updatedObjects = result.getUpdatedObjects();
158 Set<CdmBase> reloadedObjects = new HashSet<CdmBase>();
159 Set<CdmEntityIdentifier> updatedCdmIds = result.getUpdatedCdmIds();
160 boolean updatedCdmIdsIsEmpty = updatedCdmIds.isEmpty();
161
162 // if the cdm identifier set contains identifiers of objects already
163 // present in the updated objects set reomve them
164 for(CdmBase updatedObject : updatedObjects) {
165 if(updatedObject != null && exists(new CdmEntityCacheKey(updatedObject.getClass(), updatedObject.getId()))) {
166 CdmEntityIdentifier cdmEntityIdentifier = new CdmEntityIdentifier(updatedObject.getId(), updatedObject.getClass());
167 if(!updatedCdmIdsIsEmpty && updatedCdmIds.contains(cdmEntityIdentifier)) {
168 updatedCdmIds.remove(cdmEntityIdentifier);
169 }
170 reloadedObjects.add(cacheLoader.load(updatedObject, true, update));
171 }
172 }
173
174 // remote load cdm identifiers of objects which already exist
175 // in the cache
176
177 for(CdmEntityIdentifier cei : updatedCdmIds) {
178 if(exists(new CdmEntityCacheKey(cei.getCdmClass(), cei.getId()))) {
179 reloadedObjects.add(load(cei, update));
180 }
181
182 }
183 updatedObjects.clear();
184 result.addUpdatedObjects(reloadedObjects);
185 return result;
186 }
187
188 public MergeResult<CdmBase> load(MergeResult<CdmBase> mergeResult, boolean update) {
189 return cacheLoader.load(mergeResult, true, update);
190 }
191
192 public CdmModelFieldPropertyFromClass getFromCdmlibModelCache(String className) {
193 return cacheLoader.getFromCdmlibModelCache(className);
194 }
195
196
197 public void addNewEntity(CdmBase newEntity) {
198 if(newEntity != null && newEntity.getId() == 0 && newEntity.getUuid() != null) {
199 newEntitiesMap.put(newEntity.getUuid(), newEntity);
200 }
201 }
202
203 @Override
204 public void put(CdmBase cdmEntity) {
205
206 CdmBase cachedCdmEntity = cdmServiceCacher.load(cdmEntity);
207 if(cachedCdmEntity != null) {
208 logger.info("Cdm Entity with id : " + cdmEntity.getId() + " already exists in permanent cache. Ignoring put.");
209 return;
210 }
211 CdmEntityCacheKey id = new CdmEntityCacheKey(cdmEntity);
212
213 cachedCdmEntity = getFromCache(id);
214 if(cachedCdmEntity == null) {
215 CdmBase cdmEntityToCache = cdmEntity;
216 CdmBase newEntity = newEntitiesMap.get(cdmEntity.getUuid());
217 if(newEntity != null) {
218 newEntity.setId(cdmEntity.getId());
219 cdmEntityToCache = newEntity;
220 }
221 getCache().put(new Element(id, cdmEntityToCache));
222 cdmEntityToCache.initListener();
223 newEntitiesMap.remove(cdmEntity.getUuid());
224 logger.info(" - object of type " + cdmEntityToCache.getClass().getName() + " with id " + cdmEntityToCache.getId() + " put in cache");
225 return;
226 }
227 logger.info(" - object of type " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId() + " already exists");
228 }
229
230
231 private Element getCacheElement(CdmEntityCacheKey key) {
232 return getCache().get(key);
233 }
234
235
236 public CdmBase getFromCache(CdmEntityCacheKey id) {
237 Element e = getCacheElement(id);
238
239 if (e == null) {
240 return null;
241 } else {
242 return (CdmBase) e.getObjectValue();
243 }
244 }
245
246 public CdmBase getFromCache(Class<? extends CdmBase> clazz, int id) {
247 CdmEntityCacheKey cacheId = generateKey(clazz,id);
248 return getFromCache(cacheId);
249 }
250
251 @Override
252 public CdmBase getFromCache(CdmBase cdmBase) {
253
254 CdmEntityCacheKey cacheId = generateKey((CdmBase)ProxyUtils.deproxy(cdmBase));
255 // first try this cache
256 CdmBase cachedCdmEntity = getFromCache(cacheId);
257
258 if(cachedCdmEntity == null) {
259 // ... then try the permanent cache
260 cachedCdmEntity = cdmServiceCacher.getFromCache(cdmBase.getUuid());
261 }
262
263 return cachedCdmEntity;
264 }
265
266 public CdmBase getFromCache(CdmBase cdmBase, Class<? extends CdmBase> clazz) {
267
268 cdmBase = CdmBase.deproxy(cdmBase, clazz);
269 return getFromCache(cdmBase);
270 }
271
272 public List<CdmBase> getAllEntities() {
273 List<CdmBase> entities = new ArrayList<CdmBase>();
274 Map<String, CdmBase> elementsMap = getCache().getAllWithLoader(getCache().getKeys(), null);
275 for (Map.Entry<String, CdmBase> entry : elementsMap.entrySet()) {
276 entities.add(entry.getValue());
277 }
278 return entities;
279 }
280
281 public boolean exists(CdmEntityCacheKey key) {
282 return (getCacheElement(key) != null);
283 }
284
285 public boolean existsAndIsNotNull(CdmEntityCacheKey id) {
286 return getFromCache(id) != null;
287 }
288
289 public void clear() {
290 cache.removeAll();
291 }
292
293 public void dispose() {
294 CacheManager.create().removeCache(cache.getName());
295 cache.dispose();
296 newEntitiesMap.clear();
297
298 }
299
300
301 public static CdmEntityCacheKey generateKey(Class<? extends CdmBase> clazz, int id) {
302 return new CdmEntityCacheKey(clazz, id);
303 }
304
305
306 public static CdmEntityCacheKey generateKey(CdmBase cdmBase) {
307 Class<? extends CdmBase> entityClass = cdmBase.getClass();
308 int id = cdmBase.getId();
309 return new CdmEntityCacheKey(entityClass, id);
310 }
311
312 /* (non-Javadoc)
313 * @see eu.etaxonomy.cdm.model.ICdmCacher#load(eu.etaxonomy.cdm.model.common.CdmBase)
314 */
315 @Override
316 public CdmBase load(CdmBase cdmEntity) {
317 return load(cdmEntity, true);
318 }
319
320 /* (non-Javadoc)
321 * @see eu.etaxonomy.cdm.model.ICdmCacher#isCachable(eu.etaxonomy.cdm.model.common.CdmBase)
322 */
323 @Override
324 public boolean isCachable(CdmBase cdmEntity) {
325 return true;
326 }
327
328 /* (non-Javadoc)
329 * @see eu.etaxonomy.cdm.model.ICdmCacher#exists(eu.etaxonomy.cdm.model.common.CdmBase)
330 */
331 @Override
332 public boolean exists(CdmBase cdmBase) {
333 return exists(generateKey(cdmBase));
334 }
335
336
337
338 }