21c04da4253df485339cd494cbcc874963347cb9
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / eu / etaxonomy / taxeditor / remoting / cache / CdmTransientEntityCacher.java
1 /**
2 * Copyright (C) 2014 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9 package eu.etaxonomy.taxeditor.remoting.cache;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.UUID;
19
20 import org.apache.log4j.Logger;
21
22 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
23 import eu.etaxonomy.cdm.api.cache.CdmServiceCacher;
24 import eu.etaxonomy.cdm.api.service.UpdateResult;
25 import eu.etaxonomy.cdm.api.service.dto.CdmEntityIdentifier;
26 import eu.etaxonomy.cdm.model.ICdmCacher;
27 import eu.etaxonomy.cdm.model.common.CdmBase;
28 import eu.etaxonomy.cdm.persistence.dto.MergeResult;
29 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
30 import net.sf.ehcache.Cache;
31 import net.sf.ehcache.CacheManager;
32 import net.sf.ehcache.Element;
33 import net.sf.ehcache.Status;
34 import net.sf.ehcache.config.CacheConfiguration;
35 import net.sf.ehcache.config.SizeOfPolicyConfiguration;
36 import net.sf.ehcache.statistics.LiveCacheStatistics;
37
38 /**
39 *
40 * This cache guarantees that
41 * - all objects put will be ancestors of CdmBase
42 * - all CdmBase objects in the cache will be already de-proxied
43 * - after any CdmBase object is put in the cache,
44 * all non-null / non-proxy CdmBase objects in the sub-graph
45 * will also be present in the cache.
46 *
47 * @author cmathew
48 * @date 14 Oct 2014
49 *
50 */
51
52 public class CdmTransientEntityCacher implements ICdmCacher {
53
54 private static final Logger logger = Logger.getLogger(CdmTransientEntityCacher.class);
55
56
57 private final ICdmEntitySessionManager cdmEntitySessionManager;
58
59 private static CdmServiceCacher cdmServiceCacher;
60
61 private final String cacheId;
62
63 private final Cache cache;
64
65 private final CacheLoader cacheLoader;
66
67 private final Map<UUID, CdmBase> newEntitiesMap = new HashMap<UUID, CdmBase>();
68
69 public CdmTransientEntityCacher(String cacheId, ICdmEntitySessionManager cdmEntitySessionManager) {
70 this.cacheId = cacheId;
71
72 cache = new Cache(getEntityCacheConfiguration(cacheId));
73
74 CacheManager.create().removeCache(cache.getName());
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(100);
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 MergeResult<CdmBase> load(MergeResult<CdmBase> mergeResult, boolean update) {
188 return cacheLoader.load(mergeResult, true, update);
189 }
190
191 public CdmModelFieldPropertyFromClass getFromCdmlibModelCache(String className) {
192 return cacheLoader.getFromCdmlibModelCache(className);
193 }
194
195
196 public void addNewEntity(CdmBase newEntity) {
197 if(newEntity != null && newEntity.getId() == 0 && newEntity.getUuid() != null) {
198 newEntitiesMap.put(newEntity.getUuid(), newEntity);
199 }
200 }
201
202 @Override
203 public void put(CdmBase cdmEntity) {
204
205 CdmBase cachedCdmEntity = cdmServiceCacher.load(cdmEntity);
206 if(cachedCdmEntity != null) {
207 logger.info("Cdm Entity with id : " + cdmEntity.getId() + " already exists in permanent cache. Ignoring put.");
208 return;
209 }
210 CdmEntityCacheKey id = new CdmEntityCacheKey(cdmEntity);
211
212 cachedCdmEntity = getFromCache(id);
213 if(cachedCdmEntity == null) {
214 CdmBase cdmEntityToCache = cdmEntity;
215 CdmBase newEntity = newEntitiesMap.get(cdmEntity.getUuid());
216 if(newEntity != null) {
217 newEntity.setId(cdmEntity.getId());
218 cdmEntityToCache = newEntity;
219 }
220 getCache().put(new Element(id, cdmEntityToCache));
221 cdmEntityToCache.initListener();
222 newEntitiesMap.remove(cdmEntity.getUuid());
223 logger.info(" - object of type " + cdmEntityToCache.getClass().getName() + " with id " + cdmEntityToCache.getId() + " put in cache");
224 return;
225 }
226 logger.info(" - object of type " + cdmEntity.getClass().getName() + " with id " + cdmEntity.getId() + " already exists");
227 }
228
229
230 private Element getCacheElement(CdmEntityCacheKey key) {
231 return getCache().get(key);
232 }
233
234
235 public CdmBase getFromCache(CdmEntityCacheKey id) {
236 Element e = getCacheElement(id);
237
238 if (e == null) {
239 return null;
240 } else {
241 return (CdmBase) e.getObjectValue();
242 }
243 }
244
245 public CdmBase getFromCache(Class<? extends CdmBase> clazz, int id) {
246 CdmEntityCacheKey cacheId = generateKey(clazz,id);
247 return getFromCache(cacheId);
248 }
249
250 @Override
251 public CdmBase getFromCache(CdmBase cdmBase) {
252
253 CdmEntityCacheKey cacheId = generateKey((CdmBase)ProxyUtils.deproxy(cdmBase));
254 // first try this cache
255 CdmBase cachedCdmEntity = getFromCache(cacheId);
256
257 if(cachedCdmEntity == null) {
258 // ... then try the permanent cache
259 cachedCdmEntity = cdmServiceCacher.getFromCache(cdmBase.getUuid());
260 }
261
262 return cachedCdmEntity;
263 }
264
265 public CdmBase getFromCache(CdmBase cdmBase, Class<? extends CdmBase> clazz) {
266
267 cdmBase = CdmBase.deproxy(cdmBase, clazz);
268 return getFromCache(cdmBase);
269 }
270
271 public List<CdmBase> getAllEntities() {
272 List<CdmBase> entities = new ArrayList<CdmBase>();
273 Map<String, CdmBase> elementsMap = getCache().getAllWithLoader(getCache().getKeys(), null);
274 for (Map.Entry<String, CdmBase> entry : elementsMap.entrySet()) {
275 entities.add(entry.getValue());
276 }
277 return entities;
278 }
279
280 public boolean exists(CdmEntityCacheKey key) {
281 return (getCacheElement(key) != null);
282 }
283
284 public boolean existsAndIsNotNull(CdmEntityCacheKey id) {
285 return getFromCache(id) != null;
286 }
287
288 public void clear() {
289 cache.removeAll();
290 }
291
292 public void dispose() {
293 CacheManager.create().removeCache(cache.getName());
294 cache.dispose();
295 newEntitiesMap.clear();
296
297 }
298
299
300 public static CdmEntityCacheKey generateKey(Class<? extends CdmBase> clazz, int id) {
301 return new CdmEntityCacheKey(clazz, id);
302 }
303
304
305 public static CdmEntityCacheKey generateKey(CdmBase cdmBase) {
306 Class<? extends CdmBase> entityClass = cdmBase.getClass();
307 int id = cdmBase.getId();
308 return new CdmEntityCacheKey(entityClass, id);
309 }
310
311 @Override
312 public CdmBase load(CdmBase cdmEntity) {
313 return load(cdmEntity, true);
314 }
315
316 @Override
317 public boolean isCachable(CdmBase cdmEntity) {
318 return true;
319 }
320
321 @Override
322 public boolean exists(CdmBase cdmBase) {
323 return exists(generateKey(cdmBase));
324 }
325
326
327
328 }