1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.client.runtime.cache;
20
21 import java.io.IOException;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25 import java.lang.ref.SoftReference;
26 import java.util.HashMap;
27 import java.util.LinkedHashMap;
28 import java.util.Map;
29 import java.util.concurrent.locks.ReentrantReadWriteLock;
30
31 import org.apache.chemistry.opencmis.client.api.CmisObject;
32 import org.apache.chemistry.opencmis.client.api.Session;
33 import org.apache.chemistry.opencmis.commons.PropertyIds;
34 import org.apache.chemistry.opencmis.commons.SessionParameter;
35 import org.apache.chemistry.opencmis.commons.SessionParameterDefaults;
36
37
38
39
40
41 public class CacheImpl implements Cache {
42
43 private static final long serialVersionUID = 1L;
44
45 private static final float HASHTABLE_LOAD_FACTOR = 0.75f;
46
47 private int cacheSize;
48 private int cacheTtl;
49 private int pathToIdSize;
50 private int pathToIdTtl;
51
52 private LinkedHashMap<String, CacheItem<Map<String, CmisObject>>> objectMap;
53 private LinkedHashMap<String, CacheItem<String>> pathToIdMap;
54
55 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
56
57
58
59
60 public CacheImpl() {
61 }
62
63 @Override
64 public void initialize(Session session, Map<String, String> parameters) {
65 assert parameters != null;
66
67 lock.writeLock().lock();
68 try {
69
70 try {
71 cacheSize = Integer.valueOf(parameters.get(SessionParameter.CACHE_SIZE_OBJECTS));
72 if (cacheSize < 0) {
73 cacheSize = 0;
74 }
75 } catch (Exception e) {
76 cacheSize = SessionParameterDefaults.CACHE_SIZE_OBJECTS;
77 }
78
79
80 try {
81 cacheTtl = Integer.valueOf(parameters.get(SessionParameter.CACHE_TTL_OBJECTS));
82 if (cacheTtl < 0) {
83 cacheTtl = SessionParameterDefaults.CACHE_TTL_OBJECTS;
84 }
85 } catch (Exception e) {
86 cacheTtl = SessionParameterDefaults.CACHE_TTL_OBJECTS;
87 }
88
89
90 try {
91 pathToIdSize = Integer.valueOf(parameters.get(SessionParameter.CACHE_SIZE_PATHTOID));
92 if (pathToIdSize < 0) {
93 pathToIdSize = 0;
94 }
95 } catch (Exception e) {
96 pathToIdSize = SessionParameterDefaults.CACHE_SIZE_PATHTOID;
97 }
98
99
100 try {
101 pathToIdTtl = Integer.valueOf(parameters.get(SessionParameter.CACHE_TTL_PATHTOID));
102 if (pathToIdTtl < 0) {
103 pathToIdTtl = SessionParameterDefaults.CACHE_TTL_PATHTOID;
104 }
105 } catch (Exception e) {
106 pathToIdTtl = SessionParameterDefaults.CACHE_TTL_PATHTOID;
107 }
108
109 initializeInternals();
110 } finally {
111 lock.writeLock().unlock();
112 }
113 }
114
115
116
117
118 private void initializeInternals() {
119 lock.writeLock().lock();
120 try {
121
122 int cacheHashTableCapacity = (int) Math.ceil(cacheSize / HASHTABLE_LOAD_FACTOR) + 1;
123
124 final int cs = cacheSize;
125
126 objectMap = new LinkedHashMap<String, CacheItem<Map<String, CmisObject>>>(cacheHashTableCapacity,
127 HASHTABLE_LOAD_FACTOR) {
128
129 private static final long serialVersionUID = 1L;
130
131 @Override
132 protected boolean removeEldestEntry(Map.Entry<String, CacheItem<Map<String, CmisObject>>> eldest) {
133 return size() > cs;
134 }
135 };
136
137
138 int pathtoidHashTableCapacity = (int) Math.ceil(pathToIdSize / HASHTABLE_LOAD_FACTOR) + 1;
139
140 final int ptis = pathToIdSize;
141
142 pathToIdMap = new LinkedHashMap<String, CacheItem<String>>(pathtoidHashTableCapacity, HASHTABLE_LOAD_FACTOR) {
143
144 private static final long serialVersionUID = 1L;
145
146 @Override
147 protected boolean removeEldestEntry(Map.Entry<String, CacheItem<String>> eldest) {
148 return size() > ptis;
149 }
150 };
151 } finally {
152 lock.writeLock().unlock();
153 }
154 }
155
156 @Override
157 public void clear() {
158 initializeInternals();
159 }
160
161 @Override
162 public boolean containsId(String objectId, String cacheKey) {
163 lock.writeLock().lock();
164 try {
165 if (!objectMap.containsKey(objectId)) {
166 return false;
167 }
168
169 CacheItem<Map<String, CmisObject>> item = objectMap.get(objectId);
170 if (item.isExpired()) {
171 objectMap.remove(objectId);
172 return false;
173 }
174
175 return true;
176 } finally {
177 lock.writeLock().unlock();
178 }
179 }
180
181 @Override
182 public boolean containsPath(String path, String cacheKey) {
183 lock.writeLock().lock();
184 try {
185 if (!pathToIdMap.containsKey(path)) {
186 return false;
187 }
188
189 CacheItem<String> item = pathToIdMap.get(path);
190 if (item.isExpired() || !containsId(item.getItem(), cacheKey)) {
191 pathToIdMap.remove(path);
192 return false;
193 }
194
195 return true;
196 } finally {
197 lock.writeLock().unlock();
198 }
199 }
200
201 @Override
202 public CmisObject getById(String objectId, String cacheKey) {
203 lock.writeLock().lock();
204 try {
205 if (!containsId(objectId, cacheKey)) {
206 return null;
207 }
208
209 Map<String, CmisObject> item = objectMap.get(objectId).getItem();
210 return item == null ? null : item.get(cacheKey);
211 } finally {
212 lock.writeLock().unlock();
213 }
214 }
215
216 @Override
217 public CmisObject getByPath(String path, String cacheKey) {
218 lock.writeLock().lock();
219 try {
220 if (!containsPath(path, cacheKey)) {
221 return null;
222 }
223
224 CacheItem<String> item = pathToIdMap.get(path);
225 return getById(item.getItem(), cacheKey);
226 } finally {
227 lock.writeLock().unlock();
228 }
229 }
230
231 @Override
232 public String getObjectIdByPath(String path) {
233 lock.writeLock().lock();
234 try {
235 CacheItem<String> item = pathToIdMap.get(path);
236 if (item == null) {
237 return null;
238 }
239 if (item.isExpired()) {
240 pathToIdMap.remove(path);
241 return null;
242 }
243
244 return item.getItem();
245 } finally {
246 lock.writeLock().unlock();
247 }
248 }
249
250 @Override
251 public void put(CmisObject object, String cacheKey) {
252
253 if ((object == null) || (cacheKey == null)) {
254 return;
255 }
256
257
258 if (object.getId() == null) {
259 return;
260 }
261
262 lock.writeLock().lock();
263 try {
264
265 CacheItem<Map<String, CmisObject>> cacheKeyMap = objectMap.get(object.getId());
266 if (cacheKeyMap == null) {
267 cacheKeyMap = new CacheItem<Map<String, CmisObject>>(new HashMap<String, CmisObject>(), cacheTtl);
268 objectMap.put(object.getId(), cacheKeyMap);
269 }
270
271
272 Map<String, CmisObject> m = cacheKeyMap.getItem();
273 if (m != null) {
274 m.put(cacheKey, object);
275 }
276
277
278 String path = object.getPropertyValue(PropertyIds.PATH);
279 if (path != null) {
280 pathToIdMap.put(path, new CacheItem<String>(object.getId(), pathToIdTtl));
281 }
282 } finally {
283 lock.writeLock().unlock();
284 }
285 }
286
287 @Override
288 public void putPath(String path, CmisObject object, String cacheKey) {
289 if (path == null) {
290 return;
291 }
292
293 lock.writeLock().lock();
294 try {
295 put(object, cacheKey);
296
297 if ((object != null) && (object.getId() != null) && (cacheKey != null)) {
298 pathToIdMap.put(path, new CacheItem<String>(object.getId(), pathToIdTtl));
299 }
300 } finally {
301 lock.writeLock().unlock();
302 }
303 }
304
305 @Override
306 public void remove(String objectId) {
307 if (objectId == null) {
308 return;
309 }
310
311 lock.writeLock().lock();
312 try {
313 objectMap.remove(objectId);
314 } finally {
315 lock.writeLock().unlock();
316 }
317 }
318
319 @Override
320 public void removePath(String path) {
321 if (path == null) {
322 return;
323 }
324
325 lock.writeLock().lock();
326 try {
327 pathToIdMap.remove(path);
328 } finally {
329 lock.writeLock().unlock();
330 }
331 }
332
333 @Override
334 public int getCacheSize() {
335 return this.cacheSize;
336 }
337
338
339
340 private static class CacheItem<T> implements Serializable {
341
342 private static final long serialVersionUID = 1L;
343
344 private SoftReference<T> item;
345 private long timestamp;
346 private int ttl;
347
348 public CacheItem(T item, int ttl) {
349 this.item = new SoftReference<T>(item);
350 timestamp = System.currentTimeMillis();
351 this.ttl = ttl;
352 }
353
354 public synchronized boolean isExpired() {
355 if ((item == null) || (item.get() == null)) {
356 return true;
357 }
358
359 return timestamp + ttl < System.currentTimeMillis();
360 }
361
362 public synchronized T getItem() {
363 if (isExpired()) {
364 item = null;
365 return null;
366 }
367
368 return item.get();
369 }
370
371 private void writeObject(ObjectOutputStream out) throws IOException {
372 out.writeObject(isExpired() ? null : item.get());
373 out.writeLong(timestamp);
374 out.writeInt(ttl);
375 }
376
377 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
378 @SuppressWarnings("unchecked")
379 T object = (T) in.readObject();
380 timestamp = in.readLong();
381 ttl = in.readInt();
382
383 if ((object != null) && (timestamp + ttl >= System.currentTimeMillis())) {
384 this.item = new SoftReference<T>(object);
385 }
386 }
387 }
388 }