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