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