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